import and call nodes fix
This commit is contained in:
+52
-37
@@ -1628,11 +1628,15 @@ def parse_rust_file_cached(filepath_str: str, file_hash: str) -> tuple:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Rust AST helper failed for {filepath}: {e}; falling back to regex")
|
logger.warning(f"Rust AST helper failed for {filepath}: {e}; falling back to regex")
|
||||||
|
|
||||||
# Fallback: Regex-based Rust parsing
|
|
||||||
logger.info(f" Falling back to regex parsing for {filepath}")
|
# --- Import extraction --------------------------------------------
|
||||||
regex_chunks = _parse_rust_with_regex(filepath, code, lines)
|
import_re = re.compile(r'^\s*use\s+([^;]+);', re.MULTILINE)
|
||||||
chunks.extend(regex_chunks)
|
|
||||||
logger.info(f" Regex found {len(regex_chunks)} chunks")
|
# Fallback: Regex-based Rust parsing
|
||||||
|
logger.info(f" Falling back to regex parsing for {filepath}")
|
||||||
|
regex_chunks = _parse_rust_with_regex(filepath, code, lines, import_re)
|
||||||
|
chunks.extend(regex_chunks)
|
||||||
|
logger.info(f" Regex found {len(regex_chunks)} chunks")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"parse_rust_file failed {filepath}: {e}")
|
logger.warning(f"parse_rust_file failed {filepath}: {e}")
|
||||||
@@ -1641,13 +1645,13 @@ def parse_rust_file_cached(filepath_str: str, file_hash: str) -> tuple:
|
|||||||
logger.info(f"📦 Final result: {len(final_result)} chunks with metadata")
|
logger.info(f"📦 Final result: {len(final_result)} chunks with metadata")
|
||||||
return final_result
|
return final_result
|
||||||
|
|
||||||
def _parse_rust_with_regex(filepath: Path, code: str, lines: list) -> list:
|
def _parse_rust_with_regex(filepath: Path, code: str, lines: list, import_re) -> list:
|
||||||
"""Fallback regex-based Rust parser."""
|
"""Fallback regex-based Rust parser."""
|
||||||
chunks = []
|
chunks = []
|
||||||
|
|
||||||
# Pattern for Rust functions (including async, unsafe, methods)
|
# Pattern for Rust functions (including async, unsafe, methods)
|
||||||
func_pattern = re.compile(
|
func_pattern = re.compile(
|
||||||
r'^(?:pub\s+)?(?:async\s+)?(?:unsafe\s+)?fn\s+(\w+)\s*[<(]',
|
r'^(?:pub\\s+)?(?:async\\s+)?(?:unsafe\\s+)?fn\\s+(\\w+)\\s*[\u003c(]',
|
||||||
re.MULTILINE
|
re.MULTILINE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1683,15 +1687,20 @@ def _parse_rust_with_regex(filepath: Path, code: str, lines: list) -> list:
|
|||||||
|
|
||||||
# Parse functions
|
# Parse functions
|
||||||
for match in func_pattern.finditer(code):
|
for match in func_pattern.finditer(code):
|
||||||
func_name = match.group(1)
|
|
||||||
start_pos = match.start()
|
start_pos = match.start()
|
||||||
start_line = code[:start_pos].count('\n')
|
start_line = code[:start_pos].count('\\n')
|
||||||
end_line = _find_rust_brace_block_end(lines, start_line)
|
end_line = _find_rust_brace_block_end(lines, start_line)
|
||||||
func_code = "".join(lines[start_line:end_line + 1])
|
func_code = "".join(lines[start_line:end_line + 1])
|
||||||
|
|
||||||
# Enhanced function analysis
|
# Extract function name
|
||||||
is_async = 'async' in match.group(0)
|
func_name = match.group(1)
|
||||||
is_unsafe = 'unsafe' in match.group(0)
|
|
||||||
|
# Extract visibility (pub or private)
|
||||||
|
visibility = "pub" if "pub" in func_code else "private"
|
||||||
|
|
||||||
|
# Detect async/unsafe
|
||||||
|
is_async = "async" in func_code
|
||||||
|
is_unsafe = "unsafe" in func_code
|
||||||
is_pub = 'pub' in match.group(0)
|
is_pub = 'pub' in match.group(0)
|
||||||
|
|
||||||
chunk_text = f"File: {filepath}\nType: function\nName: {func_name}\n"
|
chunk_text = f"File: {filepath}\nType: function\nName: {func_name}\n"
|
||||||
@@ -2263,8 +2272,17 @@ def build_indexes():
|
|||||||
graph.add_edge(file_node_id, node_id, "contains")
|
graph.add_edge(file_node_id, node_id, "contains")
|
||||||
|
|
||||||
# ---------- IMPORTS ----------
|
# ---------- IMPORTS ----------
|
||||||
for imp in m.get("imports", []):
|
# The imports metadata might be a list or a comma-separated string
|
||||||
# Find an existing module node with this name.
|
# We need to handle both cases
|
||||||
|
imports_raw = m.get("imports", [])
|
||||||
|
if isinstance(imports_raw, str):
|
||||||
|
# Split the string back into a list
|
||||||
|
imports = [imp.strip() for imp in imports_raw.split(",") if imp.strip()]
|
||||||
|
else:
|
||||||
|
imports = imports_raw
|
||||||
|
|
||||||
|
for imp in imports:
|
||||||
|
# Try to find an existing node for this import
|
||||||
candidate_id = None
|
candidate_id = None
|
||||||
for node_id, data in created_nodes.items():
|
for node_id, data in created_nodes.items():
|
||||||
if data.get("type") in {"module", "import"} and data.get("name") == imp:
|
if data.get("type") in {"module", "import"} and data.get("name") == imp:
|
||||||
@@ -2272,14 +2290,10 @@ def build_indexes():
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not candidate_id:
|
if not candidate_id:
|
||||||
# If nothing exists, fall back to a generic import node.
|
# No existing node found - create a placeholder
|
||||||
candidate_id = f"import::{imp}"
|
candidate_id = f"import::{imp}"
|
||||||
# ---- Import node --------------------------------------------
|
|
||||||
# For imports we want a node that mirrors the target module if
|
|
||||||
# it exists; otherwise we still create a stub with language &
|
|
||||||
# file attributes for consistency.
|
|
||||||
if candidate_id not in graph.graph.nodes:
|
if candidate_id not in graph.graph.nodes:
|
||||||
# First try to find a module node with that name
|
# Double-check for a module node with this name
|
||||||
found = False
|
found = False
|
||||||
for n_id, n_data in created_nodes.items():
|
for n_id, n_data in created_nodes.items():
|
||||||
if n_data.get("name") == imp and n_data.get("type") == "module":
|
if n_data.get("name") == imp and n_data.get("type") == "module":
|
||||||
@@ -2287,8 +2301,7 @@ def build_indexes():
|
|||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
if not found:
|
if not found:
|
||||||
# Generic import stub
|
# Create a generic import node
|
||||||
candidate_id = f"import::{imp}"
|
|
||||||
graph.add_node(
|
graph.add_node(
|
||||||
candidate_id,
|
candidate_id,
|
||||||
type="Import",
|
type="Import",
|
||||||
@@ -2296,13 +2309,20 @@ def build_indexes():
|
|||||||
lang="unknown",
|
lang="unknown",
|
||||||
file="",
|
file="",
|
||||||
)
|
)
|
||||||
graph.add_edge(file_node_id, candidate_id, "imports")
|
graph.add_edge(file_node_id, candidate_id, "imports")
|
||||||
|
# Create the connection
|
||||||
graph.add_edge(file_node_id, candidate_id, "imports")
|
graph.add_edge(file_node_id, candidate_id, "imports")
|
||||||
|
|
||||||
# ---------- FUNCTION CALLS ----------
|
# ---------- FUNCTION CALLS ----------
|
||||||
for call in m.get("calls", []):
|
calls_raw = m.get("calls", [])
|
||||||
# Try to find a function/method node with this name.
|
if isinstance(calls_raw, str):
|
||||||
|
# Split the string back into a list
|
||||||
|
calls = [c.strip() for c in calls_raw.split(",") if c.strip()]
|
||||||
|
else:
|
||||||
|
calls = calls_raw
|
||||||
|
|
||||||
|
for call in calls:
|
||||||
|
# Try to find the function/method being called
|
||||||
target_id = None
|
target_id = None
|
||||||
for node_id, data in created_nodes.items():
|
for node_id, data in created_nodes.items():
|
||||||
if data.get("type") in {"function", "method", "constructor"} and data.get("name") == call:
|
if data.get("type") in {"function", "method", "constructor"} and data.get("name") == call:
|
||||||
@@ -2310,14 +2330,10 @@ def build_indexes():
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not target_id:
|
if not target_id:
|
||||||
# Fallback to a generic call node.
|
# No existing function found - create a placeholder
|
||||||
target_id = f"call::{call}"
|
target_id = f"call::{call}"
|
||||||
# ---- Call node ---------------------------------------------
|
|
||||||
# If we already know the target node (by name), reuse it. Otherwise
|
|
||||||
# create a generic node that still carries language & file context
|
|
||||||
# so it can be displayed or filtered later.
|
|
||||||
if target_id not in graph.graph.nodes:
|
if target_id not in graph.graph.nodes:
|
||||||
# Attempt a best‑effort lookup for the actual function node
|
# Double-check for a matching function
|
||||||
found = False
|
found = False
|
||||||
for n_id, n_data in created_nodes.items():
|
for n_id, n_data in created_nodes.items():
|
||||||
if (
|
if (
|
||||||
@@ -2328,8 +2344,7 @@ def build_indexes():
|
|||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
if not found:
|
if not found:
|
||||||
# Fallback to a generic FunctionCall node
|
# Create a generic function call node
|
||||||
target_id = f"call::{call}"
|
|
||||||
graph.add_node(
|
graph.add_node(
|
||||||
target_id,
|
target_id,
|
||||||
type="FunctionCall",
|
type="FunctionCall",
|
||||||
@@ -2337,8 +2352,8 @@ def build_indexes():
|
|||||||
lang="unknown",
|
lang="unknown",
|
||||||
file="",
|
file="",
|
||||||
)
|
)
|
||||||
graph.add_edge(node_id, target_id, "calls")
|
graph.add_edge(node_id, target_id, "calls")
|
||||||
|
# Create the connection
|
||||||
graph.add_edge(node_id, target_id, "calls")
|
graph.add_edge(node_id, target_id, "calls")
|
||||||
|
|
||||||
|
|
||||||
@@ -2517,7 +2532,7 @@ def _connect_impl_relationships(graph, node_id, node_data):
|
|||||||
|
|
||||||
def build_bevy_relationships(graph, created_nodes):
|
def build_bevy_relationships(graph, created_nodes):
|
||||||
"""Build Bevy-specific relationships"""
|
"""Build Bevy-specific relationships"""
|
||||||
connections = 0
|
# Extract return type = 0
|
||||||
|
|
||||||
# Find all plugin structs and impls
|
# Find all plugin structs and impls
|
||||||
plugin_structs = {}
|
plugin_structs = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user