Skip to content

Commit 52b015e

Browse files
committed
Add support for include_directive in C
Fixes #46 Add support for processing `include_directive` in C files. * **api/analyzers/c/analyzer.py** - Add `process_include_directive` method to handle `include_directive` nodes and create edges between files. - Modify `first_pass` method to process `include_directive` nodes and create edges between files. * **tests/test_c_analyzer.py** - Add test case to verify the creation of edges between files for `include_directive`. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/FalkorDB/code-graph-backend/issues/46?shareId=XXXX-XXXX-XXXX-XXXX).
1 parent c8ec9a4 commit 52b015e

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

api/analyzers/c/analyzer.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,36 @@ def process_struct_specifier(self, parent: File, node: Node, path: Path,
315315
# Connect parent to entity
316316
graph.connect_entities('DEFINES', parent.id, entity.id)
317317

318+
def process_include_directive(self, parent: File, node: Node, path: Path, graph: Graph) -> None:
319+
"""
320+
Processes an include directive node to create an edge between files.
321+
322+
Args:
323+
parent (File): The parent File object.
324+
node (Node): The AST node representing the include directive.
325+
path (Path): The file path where the include directive is found.
326+
graph (Graph): The Graph object to which the file entities and edges will be added.
327+
328+
Returns:
329+
None
330+
"""
331+
332+
assert(node.type == 'include_directive')
333+
334+
# Extract the included file path
335+
included_file_node = node.child_by_field_name('path')
336+
if included_file_node is None:
337+
return
338+
339+
included_file_path = included_file_node.text.decode('utf-8').strip('"<>')
340+
341+
# Create file entity for the included file
342+
included_file = File(os.path.dirname(path), included_file_path, os.path.splitext(included_file_path)[1])
343+
graph.add_file(included_file)
344+
345+
# Connect the parent file to the included file
346+
graph.connect_entities('INCLUDES', parent.id, included_file.id)
347+
318348
def first_pass(self, path: Path, f: io.TextIOWrapper, graph:Graph) -> None:
319349
"""
320350
Perform the first pass processing of a C source file or header file.
@@ -388,6 +418,15 @@ def first_pass(self, path: Path, f: io.TextIOWrapper, graph:Graph) -> None:
388418
for node in structs:
389419
self.process_struct_specifier(file, node, path, graph)
390420

421+
# Process include directives
422+
query = C_LANGUAGE.query("(preprocessor_directive (include_directive) @include)")
423+
captures = query.captures(tree.root_node)
424+
425+
if 'include' in captures:
426+
includes = captures['include']
427+
for node in includes:
428+
self.process_include_directive(file, node, path, graph)
429+
391430
def second_pass(self, path: Path, f: io.TextIOWrapper, graph: Graph) -> None:
392431
"""
393432
Perform the second pass processing of a C source file or header file to establish function call relationships.

tests/test_c_analyzer.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,10 @@ def test_analyzer(self):
6060
self.assertIn('add', callers)
6161
self.assertIn('main', callers)
6262

63+
# Test for include_directive edge creation
64+
included_file = g.get_file('', 'myheader.h', '.h')
65+
self.assertIsNotNone(included_file)
66+
67+
includes = g.get_neighbors([f.id], rel='INCLUDES')
68+
included_files = [node['properties']['name'] for node in includes['nodes']]
69+
self.assertIn('myheader.h', included_files)

0 commit comments

Comments
 (0)