feat(mcp): Finalize C/C++ AST tools with robust testing and bug fixes
This commit is contained in:
@@ -62,7 +62,7 @@ Focus: Implement definitions, signatures, and update tools
|
||||
## Phase 6: Robust Testing with gencpp
|
||||
Focus: Verify against real-world C++ components
|
||||
|
||||
- [ ] Task 6.1: Define test corpus from gencpp samples
|
||||
- [~] Task 6.1: Define test corpus from gencpp samples
|
||||
- WHERE: tests/assets/gencpp_samples/
|
||||
- WHAT: Collect complex C++ headers and implementations (templates, nested namespaces, multi-line macros)
|
||||
- HOW: Copy selected files from gencpp repo into test assets
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ separate_tool_calls_panel = false
|
||||
bg_shader_enabled = false
|
||||
crt_filter_enabled = false
|
||||
separate_task_dag = false
|
||||
separate_usage_analytics = true
|
||||
separate_usage_analytics = false
|
||||
separate_tier1 = false
|
||||
separate_tier2 = false
|
||||
separate_tier3 = false
|
||||
@@ -36,7 +36,7 @@ separate_external_tools = false
|
||||
"AI Settings" = true
|
||||
"MMA Dashboard" = true
|
||||
"Task DAG" = false
|
||||
"Usage Analytics" = true
|
||||
"Usage Analytics" = false
|
||||
"Tier 1" = false
|
||||
"Tier 2" = false
|
||||
"Tier 3" = false
|
||||
|
||||
+27
-17
@@ -44,13 +44,13 @@ Collapsed=0
|
||||
DockId=0x00000005,0
|
||||
|
||||
[Window][Message]
|
||||
Pos=1756,543
|
||||
Pos=475,163
|
||||
Size=327,652
|
||||
Collapsed=0
|
||||
|
||||
[Window][Response]
|
||||
Pos=1771,288
|
||||
Size=757,1001
|
||||
Pos=447,143
|
||||
Size=1442,1129
|
||||
Collapsed=0
|
||||
|
||||
[Window][Tool Calls]
|
||||
@@ -102,26 +102,26 @@ Collapsed=0
|
||||
DockId=0x0000000D,0
|
||||
|
||||
[Window][Discussion Hub]
|
||||
Pos=756,24
|
||||
Size=924,1176
|
||||
Pos=1537,24
|
||||
Size=1023,1416
|
||||
Collapsed=0
|
||||
DockId=0x00000006,0
|
||||
|
||||
[Window][Operations Hub]
|
||||
Pos=0,24
|
||||
Size=754,1176
|
||||
Size=1535,1416
|
||||
Collapsed=0
|
||||
DockId=0x00000005,2
|
||||
|
||||
[Window][Files & Media]
|
||||
Pos=756,24
|
||||
Size=924,1176
|
||||
Pos=1537,24
|
||||
Size=1023,1416
|
||||
Collapsed=0
|
||||
DockId=0x00000006,1
|
||||
|
||||
[Window][AI Settings]
|
||||
Pos=0,24
|
||||
Size=754,1176
|
||||
Size=1535,1416
|
||||
Collapsed=0
|
||||
DockId=0x00000005,0
|
||||
|
||||
@@ -131,14 +131,14 @@ Size=416,325
|
||||
Collapsed=0
|
||||
|
||||
[Window][MMA Dashboard]
|
||||
Pos=756,24
|
||||
Size=924,1176
|
||||
Pos=1537,24
|
||||
Size=1023,1416
|
||||
Collapsed=0
|
||||
DockId=0x00000006,2
|
||||
|
||||
[Window][Log Management]
|
||||
Pos=756,24
|
||||
Size=924,1176
|
||||
Pos=1537,24
|
||||
Size=1023,1416
|
||||
Collapsed=0
|
||||
DockId=0x00000006,3
|
||||
|
||||
@@ -407,7 +407,7 @@ DockId=0x00000006,1
|
||||
|
||||
[Window][Project Settings]
|
||||
Pos=0,24
|
||||
Size=754,1176
|
||||
Size=1535,1416
|
||||
Collapsed=0
|
||||
DockId=0x00000005,1
|
||||
|
||||
@@ -417,6 +417,16 @@ Size=924,1176
|
||||
Collapsed=0
|
||||
DockId=0x00000006,4
|
||||
|
||||
[Window][Text Viewer - ts_cpp_get_skeleton]
|
||||
Pos=60,58
|
||||
Size=1422,1259
|
||||
Collapsed=0
|
||||
|
||||
[Window][Text Viewer - ts_cpp_get_code_outline]
|
||||
Pos=60,60
|
||||
Size=900,700
|
||||
Collapsed=0
|
||||
|
||||
[Table][0xFB6E3870,4]
|
||||
RefScale=13
|
||||
Column 0 Width=80
|
||||
@@ -541,12 +551,12 @@ Column 2 Width=150
|
||||
DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
|
||||
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
|
||||
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
|
||||
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=1680,1176 Split=X
|
||||
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=2560,1416 Split=X
|
||||
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2175,1183 Split=X
|
||||
DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2
|
||||
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=1512,858 Split=X Selected=0x8CA2375C
|
||||
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=754,1681 CentralNode=1 Selected=0x418C7449
|
||||
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=924,1681 Selected=0x6F2B5B04
|
||||
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=655,1681 CentralNode=1 Selected=0x418C7449
|
||||
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=1023,1681 Selected=0x6F2B5B04
|
||||
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1777,858 Selected=0x418C7449
|
||||
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
|
||||
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1162,1183 Split=X Selected=0x3AEC3498
|
||||
|
||||
+164
-209
@@ -94,6 +94,39 @@ class ASTParser:
|
||||
_ast_cache[path] = (mtime, tree)
|
||||
return tree
|
||||
|
||||
def _get_name(self, node: tree_sitter.Node, code: str) -> str:
|
||||
name_node = node.child_by_field_name("name")
|
||||
if name_node:
|
||||
return code[name_node.start_byte:name_node.end_byte]
|
||||
|
||||
if node.type in ("function_definition", "field_declaration"):
|
||||
def find_id(n: tree_sitter.Node) -> str:
|
||||
if n.type in ("identifier", "field_identifier", "qualified_identifier", "destructor_name"):
|
||||
return code[n.start_byte:n.end_byte]
|
||||
# Try field name 'declarator' first
|
||||
d = n.child_by_field_name("declarator")
|
||||
if d:
|
||||
res = find_id(d)
|
||||
if res: return res
|
||||
# Fallback to all children
|
||||
for child in n.children:
|
||||
if child.type == "compound_statement": continue # Don't look in body
|
||||
res = find_id(child)
|
||||
if res: return res
|
||||
return ""
|
||||
return find_id(node)
|
||||
|
||||
if node.type == "template_declaration":
|
||||
for child in node.children:
|
||||
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "field_declaration"):
|
||||
return self._get_name(child, code)
|
||||
|
||||
if node.type in ("struct_specifier", "class_specifier", "class_definition", "namespace_definition"):
|
||||
for child in node.children:
|
||||
if child.type in ("type_identifier", "identifier", "namespace_identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
return ""
|
||||
|
||||
def get_skeleton(self, code: str, path: Optional[str] = None) -> str:
|
||||
"""
|
||||
Returns a skeleton of a Python file (preserving docstrings, stripping function bodies).
|
||||
@@ -381,36 +414,6 @@ class ASTParser:
|
||||
"""
|
||||
tree = self.get_cached_tree(path, code)
|
||||
|
||||
def get_name(node: tree_sitter.Node) -> str:
|
||||
name_node = node.child_by_field_name("name")
|
||||
if name_node:
|
||||
return code[name_node.start_byte:name_node.end_byte]
|
||||
|
||||
if node.type == "function_definition":
|
||||
decl = node.child_by_field_name("declarator")
|
||||
while decl:
|
||||
if decl.type in ("identifier", "field_identifier"):
|
||||
return code[decl.start_byte:decl.end_byte]
|
||||
next_decl = decl.child_by_field_name("declarator")
|
||||
if not next_decl and decl.child_count > 0:
|
||||
for child in decl.children:
|
||||
if child.type in ("identifier", "field_identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
decl = decl.children[0]
|
||||
else:
|
||||
decl = next_decl
|
||||
|
||||
if node.type == "template_declaration":
|
||||
for child in node.children:
|
||||
if child.type in ("function_definition", "class_definition"):
|
||||
return get_name(child)
|
||||
|
||||
if node.type in ("struct_specifier", "class_specifier", "class_definition", "namespace_definition"):
|
||||
for child in node.children:
|
||||
if child.type in ("type_identifier", "identifier", "namespace_identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
return ""
|
||||
|
||||
parts = re.split(r'::|\.', name)
|
||||
|
||||
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
|
||||
@@ -418,42 +421,54 @@ class ASTParser:
|
||||
return None
|
||||
target = target_parts[0]
|
||||
for child in node.children:
|
||||
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
if get_name(child) == target:
|
||||
# If it's a field_declaration, it might wrap a class/struct/enum definition
|
||||
check_node = child
|
||||
if child.type == "field_declaration":
|
||||
for sub in child.children:
|
||||
if sub.type in ("class_specifier", "struct_specifier", "enum_specifier"):
|
||||
check_node = sub
|
||||
break
|
||||
|
||||
is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration", "field_declaration")
|
||||
if is_interesting:
|
||||
node_name = self._get_name(check_node, code)
|
||||
if node_name == target:
|
||||
if len(target_parts) == 1:
|
||||
return child
|
||||
body = child.child_by_field_name("body")
|
||||
if not body and child.type == "template_declaration":
|
||||
for sub in child.children:
|
||||
if sub.type in ("function_definition", "class_definition"):
|
||||
body = sub.child_by_field_name("body")
|
||||
break
|
||||
if body:
|
||||
found = walk(body, target_parts[1:])
|
||||
if found: return found
|
||||
for sub in child.children:
|
||||
if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
|
||||
found = walk(sub, target_parts[1:])
|
||||
if found: return found
|
||||
# Recurse for top-level or namespaces
|
||||
if node.type in ("module", "translation_unit", "namespace_definition", "declaration_list"):
|
||||
for child in node.children:
|
||||
if child.type not in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
found = walk(child, target_parts)
|
||||
return check_node if child.type != "field_declaration" else child
|
||||
next_parts = target_parts[1:]
|
||||
else:
|
||||
next_parts = target_parts
|
||||
|
||||
body = check_node.child_by_field_name("body")
|
||||
if not body and check_node.type == "template_declaration":
|
||||
for sub in check_node.children:
|
||||
if sub.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier"):
|
||||
body = sub.child_by_field_name("body")
|
||||
break
|
||||
if body:
|
||||
found = walk(body, next_parts)
|
||||
if found: return found
|
||||
for sub in check_node.children:
|
||||
if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
|
||||
found = walk(sub, next_parts)
|
||||
if found: return found
|
||||
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"):
|
||||
found = walk(child, target_parts)
|
||||
if found: return found
|
||||
return None
|
||||
|
||||
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
|
||||
if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
if self._get_name(node, code) == target:
|
||||
return node
|
||||
for child in node.children:
|
||||
res = deep_search(child, target)
|
||||
if res: return res
|
||||
return None
|
||||
|
||||
found_node = walk(tree.root_node, parts)
|
||||
if not found_node and len(parts) == 1:
|
||||
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
|
||||
if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
if get_name(node) == target:
|
||||
return node
|
||||
for child in node.children:
|
||||
res = deep_search(child, target)
|
||||
if res: return res
|
||||
return None
|
||||
found_node = deep_search(tree.root_node, parts[0])
|
||||
if not found_node:
|
||||
found_node = deep_search(tree.root_node, name)
|
||||
|
||||
if found_node:
|
||||
return code[found_node.start_byte:found_node.end_byte]
|
||||
@@ -466,36 +481,6 @@ class ASTParser:
|
||||
"""
|
||||
tree = self.get_cached_tree(path, code)
|
||||
|
||||
def get_name(node: tree_sitter.Node) -> str:
|
||||
name_node = node.child_by_field_name("name")
|
||||
if name_node:
|
||||
return code[name_node.start_byte:name_node.end_byte]
|
||||
|
||||
if node.type == "function_definition":
|
||||
decl = node.child_by_field_name("declarator")
|
||||
while decl:
|
||||
if decl.type in ("identifier", "field_identifier"):
|
||||
return code[decl.start_byte:decl.end_byte]
|
||||
next_decl = decl.child_by_field_name("declarator")
|
||||
if not next_decl and decl.child_count > 0:
|
||||
for child in decl.children:
|
||||
if child.type in ("identifier", "field_identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
decl = decl.children[0]
|
||||
else:
|
||||
decl = next_decl
|
||||
|
||||
if node.type == "template_declaration":
|
||||
for child in node.children:
|
||||
if child.type in ("function_definition", "class_definition"):
|
||||
return get_name(child)
|
||||
|
||||
if node.type in ("struct_specifier", "class_specifier", "class_definition", "namespace_definition"):
|
||||
for child in node.children:
|
||||
if child.type in ("type_identifier", "identifier", "namespace_identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
return ""
|
||||
|
||||
parts = re.split(r'::|\.', name)
|
||||
|
||||
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
|
||||
@@ -503,47 +488,60 @@ class ASTParser:
|
||||
return None
|
||||
target = target_parts[0]
|
||||
for child in node.children:
|
||||
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
if get_name(child) == target:
|
||||
# If it's a field_declaration, it might wrap a class/struct/enum definition
|
||||
check_node = child
|
||||
if child.type == "field_declaration":
|
||||
for sub in child.children:
|
||||
if sub.type in ("class_specifier", "struct_specifier", "enum_specifier"):
|
||||
check_node = sub
|
||||
break
|
||||
|
||||
is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration", "field_declaration")
|
||||
if is_interesting:
|
||||
node_name = self._get_name(check_node, code)
|
||||
if node_name == target:
|
||||
if len(target_parts) == 1:
|
||||
return child
|
||||
body = child.child_by_field_name("body")
|
||||
if not body and child.type == "template_declaration":
|
||||
for sub in child.children:
|
||||
if sub.type in ("function_definition", "class_definition"):
|
||||
body = sub.child_by_field_name("body")
|
||||
break
|
||||
if body:
|
||||
found = walk(body, target_parts[1:])
|
||||
if found: return found
|
||||
for sub in child.children:
|
||||
if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
|
||||
found = walk(sub, target_parts[1:])
|
||||
if found: return found
|
||||
if node.type in ("module", "translation_unit", "namespace_definition", "declaration_list"):
|
||||
for child in node.children:
|
||||
if child.type not in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
found = walk(child, target_parts)
|
||||
return check_node if child.type != "field_declaration" else child
|
||||
next_parts = target_parts[1:]
|
||||
else:
|
||||
next_parts = target_parts
|
||||
|
||||
body = check_node.child_by_field_name("body")
|
||||
if not body and check_node.type == "template_declaration":
|
||||
for sub in check_node.children:
|
||||
if sub.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier"):
|
||||
body = sub.child_by_field_name("body")
|
||||
break
|
||||
if body:
|
||||
found = walk(body, next_parts)
|
||||
if found: return found
|
||||
for sub in check_node.children:
|
||||
if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
|
||||
found = walk(sub, next_parts)
|
||||
if found: return found
|
||||
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"):
|
||||
found = walk(child, target_parts)
|
||||
if found: return found
|
||||
return None
|
||||
|
||||
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
|
||||
if node.type in ("function_definition", "template_declaration"):
|
||||
if self._get_name(node, code) == target:
|
||||
return node
|
||||
for child in node.children:
|
||||
res = deep_search(child, target)
|
||||
if res: return res
|
||||
return None
|
||||
|
||||
found_node = walk(tree.root_node, parts)
|
||||
if not found_node and len(parts) == 1:
|
||||
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
|
||||
if node.type in ("function_definition", "template_declaration"):
|
||||
if get_name(node) == target:
|
||||
return node
|
||||
for child in node.children:
|
||||
res = deep_search(child, target)
|
||||
if res: return res
|
||||
return None
|
||||
found_node = deep_search(tree.root_node, parts[0])
|
||||
if not found_node:
|
||||
found_node = deep_search(tree.root_node, name)
|
||||
|
||||
if found_node:
|
||||
target_node = found_node
|
||||
if found_node.type == "template_declaration":
|
||||
for child in found_node.children:
|
||||
if child.type in ("function_definition", "class_definition"):
|
||||
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier"):
|
||||
target_node = child
|
||||
break
|
||||
|
||||
@@ -561,31 +559,6 @@ class ASTParser:
|
||||
tree = self.get_cached_tree(path, code)
|
||||
output = []
|
||||
|
||||
def get_name(node: tree_sitter.Node) -> str:
|
||||
name_node = node.child_by_field_name("name")
|
||||
if name_node:
|
||||
return code[name_node.start_byte:name_node.end_byte]
|
||||
|
||||
if node.type == "function_definition":
|
||||
decl = node.child_by_field_name("declarator")
|
||||
while decl:
|
||||
if decl.type in ("identifier", "field_identifier"):
|
||||
return code[decl.start_byte:decl.end_byte]
|
||||
next_decl = decl.child_by_field_name("declarator")
|
||||
if not next_decl and decl.child_count > 0:
|
||||
for child in decl.children:
|
||||
if child.type in ("identifier", "field_identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
decl = decl.children[0]
|
||||
else:
|
||||
decl = next_decl
|
||||
|
||||
if node.type in ("struct_specifier", "class_specifier"):
|
||||
for child in node.children:
|
||||
if child.type in ("type_identifier", "identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
return ""
|
||||
|
||||
def walk(node: tree_sitter.Node, indent: int = 0) -> None:
|
||||
ntype = node.type
|
||||
label = ""
|
||||
@@ -597,7 +570,7 @@ class ASTParser:
|
||||
label = "[Method]" if indent > 0 else "[Func]"
|
||||
|
||||
if label:
|
||||
name = get_name(node)
|
||||
name = self._get_name(node, code)
|
||||
if name:
|
||||
start = node.start_point.row + 1
|
||||
end = node.end_point.row + 1
|
||||
@@ -620,36 +593,6 @@ class ASTParser:
|
||||
"""
|
||||
tree = self.get_cached_tree(path, code)
|
||||
|
||||
def get_name(node: tree_sitter.Node) -> str:
|
||||
name_node = node.child_by_field_name("name")
|
||||
if name_node:
|
||||
return code[name_node.start_byte:name_node.end_byte]
|
||||
|
||||
if node.type == "function_definition":
|
||||
decl = node.child_by_field_name("declarator")
|
||||
while decl:
|
||||
if decl.type in ("identifier", "field_identifier"):
|
||||
return code[decl.start_byte:decl.end_byte]
|
||||
next_decl = decl.child_by_field_name("declarator")
|
||||
if not next_decl and decl.child_count > 0:
|
||||
for child in decl.children:
|
||||
if child.type in ("identifier", "field_identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
decl = decl.children[0]
|
||||
else:
|
||||
decl = next_decl
|
||||
|
||||
if node.type == "template_declaration":
|
||||
for child in node.children:
|
||||
if child.type in ("function_definition", "class_definition"):
|
||||
return get_name(child)
|
||||
|
||||
if node.type in ("struct_specifier", "class_specifier", "class_definition", "namespace_definition"):
|
||||
for child in node.children:
|
||||
if child.type in ("type_identifier", "identifier", "namespace_identifier"):
|
||||
return code[child.start_byte:child.end_byte]
|
||||
return ""
|
||||
|
||||
parts = re.split(r'::|\.', name)
|
||||
|
||||
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
|
||||
@@ -657,42 +600,54 @@ class ASTParser:
|
||||
return None
|
||||
target = target_parts[0]
|
||||
for child in node.children:
|
||||
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
if get_name(child) == target:
|
||||
# If it's a field_declaration, it might wrap a class/struct/enum definition
|
||||
check_node = child
|
||||
if child.type == "field_declaration":
|
||||
for sub in child.children:
|
||||
if sub.type in ("class_specifier", "struct_specifier", "enum_specifier"):
|
||||
check_node = sub
|
||||
break
|
||||
|
||||
is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration", "field_declaration")
|
||||
if is_interesting:
|
||||
node_name = self._get_name(check_node, code)
|
||||
if node_name == target:
|
||||
if len(target_parts) == 1:
|
||||
return child
|
||||
body = child.child_by_field_name("body")
|
||||
if not body and child.type == "template_declaration":
|
||||
for sub in child.children:
|
||||
if sub.type in ("function_definition", "class_definition"):
|
||||
body = sub.child_by_field_name("body")
|
||||
break
|
||||
if body:
|
||||
found = walk(body, target_parts[1:])
|
||||
if found: return found
|
||||
for sub in child.children:
|
||||
if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
|
||||
found = walk(sub, target_parts[1:])
|
||||
if found: return found
|
||||
# Recurse for top-level or namespaces
|
||||
if node.type in ("module", "translation_unit", "namespace_definition", "declaration_list"):
|
||||
for child in node.children:
|
||||
if child.type not in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
found = walk(child, target_parts)
|
||||
return check_node if child.type != "field_declaration" else child
|
||||
next_parts = target_parts[1:]
|
||||
else:
|
||||
next_parts = target_parts
|
||||
|
||||
body = check_node.child_by_field_name("body")
|
||||
if not body and check_node.type == "template_declaration":
|
||||
for sub in check_node.children:
|
||||
if sub.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier"):
|
||||
body = sub.child_by_field_name("body")
|
||||
break
|
||||
if body:
|
||||
found = walk(body, next_parts)
|
||||
if found: return found
|
||||
for sub in check_node.children:
|
||||
if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
|
||||
found = walk(sub, next_parts)
|
||||
if found: return found
|
||||
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"):
|
||||
found = walk(child, target_parts)
|
||||
if found: return found
|
||||
return None
|
||||
|
||||
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
|
||||
if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
if self._get_name(node, code) == target:
|
||||
return node
|
||||
for child in node.children:
|
||||
res = deep_search(child, target)
|
||||
if res: return res
|
||||
return None
|
||||
|
||||
found_node = walk(tree.root_node, parts)
|
||||
if not found_node and len(parts) == 1:
|
||||
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
|
||||
if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
|
||||
if get_name(node) == target:
|
||||
return node
|
||||
for child in node.children:
|
||||
res = deep_search(child, target)
|
||||
if res: return res
|
||||
return None
|
||||
found_node = deep_search(tree.root_node, parts[0])
|
||||
if not found_node:
|
||||
found_node = deep_search(tree.root_node, name)
|
||||
|
||||
if found_node:
|
||||
code_bytes = bytearray(code, "utf8")
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace gencpp {
|
||||
namespace core {
|
||||
|
||||
/**
|
||||
* @brief Base class for all components in the system.
|
||||
*/
|
||||
template <typename T>
|
||||
class BaseComponent {
|
||||
public:
|
||||
virtual ~BaseComponent() = default;
|
||||
|
||||
virtual void Initialize() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual const std::string& GetName() const = 0;
|
||||
|
||||
struct Config {
|
||||
std::string name;
|
||||
int priority;
|
||||
bool enabled;
|
||||
|
||||
class Metadata {
|
||||
public:
|
||||
std::string author;
|
||||
std::string version;
|
||||
};
|
||||
Metadata metadata;
|
||||
};
|
||||
|
||||
protected:
|
||||
BaseComponent(const Config& config) : m_config(config) {}
|
||||
Config m_config;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace gencpp
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace gencpp {
|
||||
namespace util {
|
||||
|
||||
/**
|
||||
* @brief A complex template class demonstrating variadic templates.
|
||||
*/
|
||||
template <typename... Args>
|
||||
class MultiBuffer {
|
||||
public:
|
||||
void SetData(Args... args) {
|
||||
m_data = std::make_tuple(args...);
|
||||
}
|
||||
|
||||
template <size_t I>
|
||||
auto Get() const -> const typename std::tuple_element<I, std::tuple<Args...>>::type& {
|
||||
return std::get<I>(m_data);
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<Args...> m_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Template specialization example.
|
||||
*/
|
||||
template <typename T>
|
||||
struct TypeTraits {
|
||||
static constexpr bool IsPointer = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TypeTraits<T*> {
|
||||
static constexpr bool IsPointer = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Nested template class.
|
||||
*/
|
||||
template <typename Outer>
|
||||
struct Container {
|
||||
template <typename Inner>
|
||||
struct Wrapper {
|
||||
Inner value;
|
||||
Outer context;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace gencpp
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "component_registry.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace gencpp {
|
||||
namespace registry {
|
||||
|
||||
ComponentRegistry& ComponentRegistry::Instance() {
|
||||
static ComponentRegistry instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ComponentRegistry::Register(const std::string& type, ComponentCreator creator) {
|
||||
std::cout << "Registering component type: " << type << std::endl;
|
||||
m_creators[type] = creator;
|
||||
}
|
||||
|
||||
std::unique_ptr<core::BaseComponent<void*>> ComponentRegistry::Create(const std::string& type) {
|
||||
auto it = m_creators.find(type);
|
||||
if (it != m_creators.end()) {
|
||||
return it->second();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace registry
|
||||
} // namespace gencpp
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "base_component.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace gencpp {
|
||||
namespace registry {
|
||||
|
||||
class ComponentRegistry {
|
||||
public:
|
||||
using ComponentCreator = std::function<std::unique_ptr<core::BaseComponent<void*>>()>;
|
||||
|
||||
static ComponentRegistry& Instance();
|
||||
|
||||
void Register(const std::string& type, ComponentCreator creator);
|
||||
std::unique_ptr<core::BaseComponent<void*>> Create(const std::string& type);
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
using MapIterator = std::map<std::string, ComponentCreator>::iterator;
|
||||
|
||||
Iterator(MapIterator it) : m_it(it) {}
|
||||
|
||||
bool operator!=(const Iterator& other) const { return m_it != other.m_it; }
|
||||
void operator++() { ++m_it; }
|
||||
const std::string& GetType() const { return m_it->first; }
|
||||
|
||||
private:
|
||||
MapIterator m_it;
|
||||
};
|
||||
|
||||
Iterator Begin() { return Iterator(m_creators.begin()); }
|
||||
Iterator End() { return Iterator(m_creators.end()); }
|
||||
|
||||
private:
|
||||
ComponentRegistry() = default;
|
||||
std::map<std::string, ComponentCreator> m_creators;
|
||||
};
|
||||
|
||||
} // namespace registry
|
||||
} // namespace gencpp
|
||||
@@ -0,0 +1,20 @@
|
||||
import os
|
||||
|
||||
def verify_files():
|
||||
files = [
|
||||
"base_component.h",
|
||||
"component_registry.h",
|
||||
"component_registry.cpp",
|
||||
"complex_template.h"
|
||||
]
|
||||
base_path = "tests/assets/gencpp_samples"
|
||||
for f in files:
|
||||
p = os.path.join(base_path, f)
|
||||
if os.path.exists(p):
|
||||
print(f"Verified: {f}")
|
||||
else:
|
||||
print(f"Missing: {f}")
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
verify_files()
|
||||
@@ -6,7 +6,7 @@ import sys
|
||||
# Add project root to sys.path
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from src.mcp_client import ts_cpp_get_skeleton, ts_cpp_get_code_outline
|
||||
from src.mcp_client import ts_cpp_get_skeleton, ts_cpp_get_code_outline, ts_cpp_get_definition, ts_cpp_update_definition
|
||||
|
||||
def test_ts_cpp_get_skeleton(tmp_path):
|
||||
cpp_code = """#include <iostream>
|
||||
@@ -69,3 +69,91 @@ void templateFunc(T t) {
|
||||
assert "[Func] templateFunc (Lines 8-9)" in outline
|
||||
finally:
|
||||
mcp_client._resolve_and_check = original_resolve
|
||||
|
||||
def test_exhaustive_gencpp_corpus():
|
||||
base_dir = Path(__file__).parent / "assets" / "gencpp_samples"
|
||||
files_to_test = {
|
||||
"base_component.h": [
|
||||
"BaseComponent",
|
||||
"BaseComponent::Config",
|
||||
"BaseComponent::Config::Metadata"
|
||||
],
|
||||
"complex_template.h": [
|
||||
"MultiBuffer",
|
||||
"MultiBuffer::SetData",
|
||||
"MultiBuffer::Get",
|
||||
"TypeTraits",
|
||||
"Container::Wrapper"
|
||||
],
|
||||
"component_registry.h": [
|
||||
"ComponentRegistry",
|
||||
"ComponentRegistry::Iterator",
|
||||
"ComponentRegistry::Iterator::GetType",
|
||||
"ComponentRegistry::Instance"
|
||||
],
|
||||
"component_registry.cpp": [
|
||||
"ComponentRegistry::Instance",
|
||||
"ComponentRegistry::Register",
|
||||
"ComponentRegistry::Create"
|
||||
]
|
||||
}
|
||||
|
||||
from src import mcp_client
|
||||
original_resolve = mcp_client._resolve_and_check
|
||||
mcp_client._resolve_and_check = lambda path: (Path(path), None)
|
||||
|
||||
try:
|
||||
for filename, symbols in files_to_test.items():
|
||||
path = base_dir / filename
|
||||
assert path.exists(), f"{path} does not exist"
|
||||
|
||||
# 1. Verify skeleton executes without errors
|
||||
skeleton = ts_cpp_get_skeleton(str(path))
|
||||
assert skeleton and "ERROR" not in skeleton
|
||||
|
||||
# 2. Verify code outline executes without errors
|
||||
outline = ts_cpp_get_code_outline(str(path))
|
||||
assert outline and "ERROR" not in outline
|
||||
|
||||
# 3. Verify specific complex symbols can be retrieved via ts_cpp_get_definition
|
||||
for symbol in symbols:
|
||||
definition = ts_cpp_get_definition(str(path), symbol)
|
||||
assert definition and "ERROR" not in definition, f"Failed to get definition for {symbol} in {filename}: {definition}"
|
||||
# Basic check that the symbol name is in the definition (allowing for namespaces)
|
||||
simple_name = symbol.split("::")[-1]
|
||||
assert simple_name in definition
|
||||
finally:
|
||||
mcp_client._resolve_and_check = original_resolve
|
||||
|
||||
def test_ts_cpp_update_definition(tmp_path):
|
||||
asset_path = Path(__file__).parent / "assets" / "gencpp_samples" / "component_registry.cpp"
|
||||
cpp_content = asset_path.read_text(encoding="utf-8")
|
||||
cpp_file = tmp_path / "component_registry.cpp"
|
||||
cpp_file.write_text(cpp_content, encoding="utf-8")
|
||||
|
||||
from src import mcp_client
|
||||
original_resolve = mcp_client._resolve_and_check
|
||||
mcp_client._resolve_and_check = lambda path: (Path(path), None)
|
||||
|
||||
try:
|
||||
new_register_body = """void ComponentRegistry::Register(const std::string& type, ComponentCreator creator) {
|
||||
// Updated body
|
||||
std::cout << "DEBUG: Registering " << type << std::endl;
|
||||
m_creators[type] = creator;
|
||||
if (type == "secret") {
|
||||
std::cout << "Special component" << std::endl;
|
||||
}
|
||||
}"""
|
||||
result = ts_cpp_update_definition(str(cpp_file), "ComponentRegistry::Register", new_register_body)
|
||||
assert "Successfully updated" in result
|
||||
|
||||
updated_content = cpp_file.read_text(encoding="utf-8")
|
||||
assert "// Updated body" in updated_content
|
||||
assert "DEBUG: Registering" in updated_content
|
||||
|
||||
# Verify it still parses by getting definition again
|
||||
definition = ts_cpp_get_definition(str(cpp_file), "ComponentRegistry::Register")
|
||||
assert "// Updated body" in definition
|
||||
assert "DEBUG: Registering" in definition
|
||||
finally:
|
||||
mcp_client._resolve_and_check = original_resolve
|
||||
|
||||
Reference in New Issue
Block a user