From ee86d664bfd039df0f09090664f2a4bd7655e1ce Mon Sep 17 00:00:00 2001 From: Avi Avni Date: Thu, 8 May 2025 10:28:36 +0300 Subject: [PATCH 1/3] add call text --- api/analyzers/source_analyzer.py | 4 ++-- api/graph.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/api/analyzers/source_analyzer.py b/api/analyzers/source_analyzer.py index 12502ab..f53164b 100644 --- a/api/analyzers/source_analyzer.py +++ b/api/analyzers/source_analyzer.py @@ -144,7 +144,7 @@ def second_pass(self, graph: Graph, files: list[Path], path: Path) -> None: for _, entity in file.entities.items(): entity.resolved_symbol(lambda key, symbol: analyzers[file_path.suffix].resolve_symbol(self.files, lsps[file_path.suffix], file_path, path, key, symbol)) for key, symbols in entity.resolved_symbols.items(): - for symbol in symbols: + for i, symbol in enumerate(symbols): if key == "base_class": graph.connect_entities("EXTENDS", entity.id, symbol.id) elif key == "implement_interface": @@ -152,7 +152,7 @@ def second_pass(self, graph: Graph, files: list[Path], path: Path) -> None: elif key == "extend_interface": graph.connect_entities("EXTENDS", entity.id, symbol.id) elif key == "call": - graph.connect_entities("CALLS", entity.id, symbol.id) + graph.connect_entities("CALLS", entity.id, symbol.id, {"line": entity.symbols[key][i].start_point.row, "text": entity.symbols[key][i].text}) elif key == "return_type": graph.connect_entities("RETURNS", entity.id, symbol.id) elif key == "parameters": diff --git a/api/graph.py b/api/graph.py index 7c91405..c54092d 100644 --- a/api/graph.py +++ b/api/graph.py @@ -479,7 +479,7 @@ def set_file_coverage(self, path: str, name: str, ext: str, coverage: float) -> res = self._query(q, params) - def connect_entities(self, relation: str, src_id: int, dest_id: int) -> None: + def connect_entities(self, relation: str, src_id: int, dest_id: int, properties: dict) -> None: """ Establish a relationship between src and dest @@ -491,9 +491,10 @@ def connect_entities(self, relation: str, src_id: int, dest_id: int) -> None: q = f"""MATCH (src), (dest) WHERE ID(src) = $src_id AND ID(dest) = $dest_id MERGE (src)-[e:{relation}]->(dest) + SET e += $properties RETURN e""" - params = {'src_id': src_id, 'dest_id': dest_id} + params = {'src_id': src_id, 'dest_id': dest_id, properties: properties} self._query(q, params) def function_calls_function(self, caller_id: int, callee_id: int, pos: int) -> None: From 95b55651c73cd5ce4e9e100fa62a5a48c80543a3 Mon Sep 17 00:00:00 2001 From: Avi Avni Date: Thu, 8 May 2025 14:05:24 +0300 Subject: [PATCH 2/3] fix --- api/analyzers/source_analyzer.py | 17 ++++++++++------- api/entities/entity.py | 22 +++++++++++----------- api/graph.py | 4 ++-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/api/analyzers/source_analyzer.py b/api/analyzers/source_analyzer.py index f53164b..b2ce4b4 100644 --- a/api/analyzers/source_analyzer.py +++ b/api/analyzers/source_analyzer.py @@ -143,20 +143,23 @@ def second_pass(self, graph: Graph, files: list[Path], path: Path) -> None: logging.info(f'Processing file ({i + 1}/{files_len}): {file_path}') for _, entity in file.entities.items(): entity.resolved_symbol(lambda key, symbol: analyzers[file_path.suffix].resolve_symbol(self.files, lsps[file_path.suffix], file_path, path, key, symbol)) - for key, symbols in entity.resolved_symbols.items(): + for key, symbols in entity.symbols.items(): for i, symbol in enumerate(symbols): + if len(symbol.resolved_symbol) == 0: + continue + resolved_symbol = next(iter(symbol.resolved_symbol)) if key == "base_class": - graph.connect_entities("EXTENDS", entity.id, symbol.id) + graph.connect_entities("EXTENDS", entity.id, resolved_symbol.id) elif key == "implement_interface": - graph.connect_entities("IMPLEMENTS", entity.id, symbol.id) + graph.connect_entities("IMPLEMENTS", entity.id, resolved_symbol.id) elif key == "extend_interface": - graph.connect_entities("EXTENDS", entity.id, symbol.id) + graph.connect_entities("EXTENDS", entity.id, resolved_symbol.id) elif key == "call": - graph.connect_entities("CALLS", entity.id, symbol.id, {"line": entity.symbols[key][i].start_point.row, "text": entity.symbols[key][i].text}) + graph.connect_entities("CALLS", entity.id, resolved_symbol.id, {"line": symbol.symbol.start_point.row, "text": symbol.symbol.text.decode("utf-8")}) elif key == "return_type": - graph.connect_entities("RETURNS", entity.id, symbol.id) + graph.connect_entities("RETURNS", entity.id, resolved_symbol.id) elif key == "parameters": - graph.connect_entities("PARAMETERS", entity.id, symbol.id) + graph.connect_entities("PARAMETERS", entity.id, resolved_symbol.id) def analyze_files(self, files: list[Path], path: Path, graph: Graph) -> None: self.first_pass(path, files, [], graph) diff --git a/api/entities/entity.py b/api/entities/entity.py index e271693..77f1cc9 100644 --- a/api/entities/entity.py +++ b/api/entities/entity.py @@ -1,23 +1,24 @@ from typing import Callable, Self from tree_sitter import Node +class Symbol: + def __init__(self, symbol: Node): + self.symbol = symbol + self.resolved_symbol = set() + + def add_resolve_symbol(self, resolved_symbol): + self.resolved_symbol.add(resolved_symbol) class Entity: def __init__(self, node: Node): self.node = node - self.symbols: dict[str, list[Node]] = {} - self.resolved_symbols: dict[str, set[Self]] = {} + self.symbols: dict[str, list[Symbol]] = {} self.children: dict[Node, Self] = {} def add_symbol(self, key: str, symbol: Node): if key not in self.symbols: self.symbols[key] = [] - self.symbols[key].append(symbol) - - def add_resolved_symbol(self, key: str, symbol: Self): - if key not in self.resolved_symbols: - self.resolved_symbols[key] = set() - self.resolved_symbols[key].add(symbol) + self.symbols[key].append(Symbol(symbol)) def add_child(self, child: Self): child.parent = self @@ -25,7 +26,6 @@ def add_child(self, child: Self): def resolved_symbol(self, f: Callable[[str, Node], list[Self]]): for key, symbols in self.symbols.items(): - self.resolved_symbols[key] = set() for symbol in symbols: - for resolved_symbol in f(key, symbol): - self.resolved_symbols[key].add(resolved_symbol) \ No newline at end of file + for resolved_symbol in f(key, symbol.symbol): + symbol.add_resolve_symbol(resolved_symbol) \ No newline at end of file diff --git a/api/graph.py b/api/graph.py index c54092d..a9aa57f 100644 --- a/api/graph.py +++ b/api/graph.py @@ -479,7 +479,7 @@ def set_file_coverage(self, path: str, name: str, ext: str, coverage: float) -> res = self._query(q, params) - def connect_entities(self, relation: str, src_id: int, dest_id: int, properties: dict) -> None: + def connect_entities(self, relation: str, src_id: int, dest_id: int, properties: dict = {}) -> None: """ Establish a relationship between src and dest @@ -494,7 +494,7 @@ def connect_entities(self, relation: str, src_id: int, dest_id: int, properties: SET e += $properties RETURN e""" - params = {'src_id': src_id, 'dest_id': dest_id, properties: properties} + params = {'src_id': src_id, 'dest_id': dest_id, "properties": properties} self._query(q, params) def function_calls_function(self, caller_id: int, callee_id: int, pos: int) -> None: From 4c03c81efdf36934a14eace8a048eb27553ef430 Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Thu, 8 May 2025 16:33:36 +0300 Subject: [PATCH 3/3] rm-unused-enu --- api/analyzers/source_analyzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/analyzers/source_analyzer.py b/api/analyzers/source_analyzer.py index b2ce4b4..4e1385b 100644 --- a/api/analyzers/source_analyzer.py +++ b/api/analyzers/source_analyzer.py @@ -144,7 +144,7 @@ def second_pass(self, graph: Graph, files: list[Path], path: Path) -> None: for _, entity in file.entities.items(): entity.resolved_symbol(lambda key, symbol: analyzers[file_path.suffix].resolve_symbol(self.files, lsps[file_path.suffix], file_path, path, key, symbol)) for key, symbols in entity.symbols.items(): - for i, symbol in enumerate(symbols): + for symbol in symbols: if len(symbol.resolved_symbol) == 0: continue resolved_symbol = next(iter(symbol.resolved_symbol))