Skip to content

Commit

Permalink
Add support for include_directive in C
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
gkorland committed Jan 2, 2025
1 parent c8ec9a4 commit 52b015e
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 0 deletions.
39 changes: 39 additions & 0 deletions api/analyzers/c/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,36 @@ def process_struct_specifier(self, parent: File, node: Node, path: Path,
# Connect parent to entity
graph.connect_entities('DEFINES', parent.id, entity.id)

def process_include_directive(self, parent: File, node: Node, path: Path, graph: Graph) -> None:
"""
Processes an include directive node to create an edge between files.
Args:
parent (File): The parent File object.
node (Node): The AST node representing the include directive.
path (Path): The file path where the include directive is found.
graph (Graph): The Graph object to which the file entities and edges will be added.
Returns:
None
"""

assert(node.type == 'include_directive')

# Extract the included file path
included_file_node = node.child_by_field_name('path')
if included_file_node is None:
return

included_file_path = included_file_node.text.decode('utf-8').strip('"<>')

# Create file entity for the included file
included_file = File(os.path.dirname(path), included_file_path, os.path.splitext(included_file_path)[1])
graph.add_file(included_file)

# Connect the parent file to the included file
graph.connect_entities('INCLUDES', parent.id, included_file.id)

def first_pass(self, path: Path, f: io.TextIOWrapper, graph:Graph) -> None:
"""
Perform the first pass processing of a C source file or header file.
Expand Down Expand Up @@ -388,6 +418,15 @@ def first_pass(self, path: Path, f: io.TextIOWrapper, graph:Graph) -> None:
for node in structs:
self.process_struct_specifier(file, node, path, graph)

# Process include directives
query = C_LANGUAGE.query("(preprocessor_directive (include_directive) @include)")
captures = query.captures(tree.root_node)

if 'include' in captures:
includes = captures['include']
for node in includes:
self.process_include_directive(file, node, path, graph)

def second_pass(self, path: Path, f: io.TextIOWrapper, graph: Graph) -> None:
"""
Perform the second pass processing of a C source file or header file to establish function call relationships.
Expand Down
7 changes: 7 additions & 0 deletions tests/test_c_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,10 @@ def test_analyzer(self):
self.assertIn('add', callers)
self.assertIn('main', callers)

# Test for include_directive edge creation
included_file = g.get_file('', 'myheader.h', '.h')
self.assertIsNotNone(included_file)

includes = g.get_neighbors([f.id], rel='INCLUDES')
included_files = [node['properties']['name'] for node in includes['nodes']]
self.assertIn('myheader.h', included_files)

0 comments on commit 52b015e

Please sign in to comment.