feat(mcp): Finalize C/C++ AST tools with robust testing and bug fixes

This commit is contained in:
2026-05-05 20:08:51 -04:00
parent 584e8e526e
commit 992e206769
10 changed files with 468 additions and 230 deletions
@@ -62,7 +62,7 @@ Focus: Implement definitions, signatures, and update tools
## Phase 6: Robust Testing with gencpp ## Phase 6: Robust Testing with gencpp
Focus: Verify against real-world C++ components 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/ - WHERE: tests/assets/gencpp_samples/
- WHAT: Collect complex C++ headers and implementations (templates, nested namespaces, multi-line macros) - WHAT: Collect complex C++ headers and implementations (templates, nested namespaces, multi-line macros)
- HOW: Copy selected files from gencpp repo into test assets - HOW: Copy selected files from gencpp repo into test assets
+2 -2
View File
@@ -23,7 +23,7 @@ separate_tool_calls_panel = false
bg_shader_enabled = false bg_shader_enabled = false
crt_filter_enabled = false crt_filter_enabled = false
separate_task_dag = false separate_task_dag = false
separate_usage_analytics = true separate_usage_analytics = false
separate_tier1 = false separate_tier1 = false
separate_tier2 = false separate_tier2 = false
separate_tier3 = false separate_tier3 = false
@@ -36,7 +36,7 @@ separate_external_tools = false
"AI Settings" = true "AI Settings" = true
"MMA Dashboard" = true "MMA Dashboard" = true
"Task DAG" = false "Task DAG" = false
"Usage Analytics" = true "Usage Analytics" = false
"Tier 1" = false "Tier 1" = false
"Tier 2" = false "Tier 2" = false
"Tier 3" = false "Tier 3" = false
+27 -17
View File
@@ -44,13 +44,13 @@ Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
[Window][Message] [Window][Message]
Pos=1756,543 Pos=475,163
Size=327,652 Size=327,652
Collapsed=0 Collapsed=0
[Window][Response] [Window][Response]
Pos=1771,288 Pos=447,143
Size=757,1001 Size=1442,1129
Collapsed=0 Collapsed=0
[Window][Tool Calls] [Window][Tool Calls]
@@ -102,26 +102,26 @@ Collapsed=0
DockId=0x0000000D,0 DockId=0x0000000D,0
[Window][Discussion Hub] [Window][Discussion Hub]
Pos=756,24 Pos=1537,24
Size=924,1176 Size=1023,1416
Collapsed=0 Collapsed=0
DockId=0x00000006,0 DockId=0x00000006,0
[Window][Operations Hub] [Window][Operations Hub]
Pos=0,24 Pos=0,24
Size=754,1176 Size=1535,1416
Collapsed=0 Collapsed=0
DockId=0x00000005,2 DockId=0x00000005,2
[Window][Files & Media] [Window][Files & Media]
Pos=756,24 Pos=1537,24
Size=924,1176 Size=1023,1416
Collapsed=0 Collapsed=0
DockId=0x00000006,1 DockId=0x00000006,1
[Window][AI Settings] [Window][AI Settings]
Pos=0,24 Pos=0,24
Size=754,1176 Size=1535,1416
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
@@ -131,14 +131,14 @@ Size=416,325
Collapsed=0 Collapsed=0
[Window][MMA Dashboard] [Window][MMA Dashboard]
Pos=756,24 Pos=1537,24
Size=924,1176 Size=1023,1416
Collapsed=0 Collapsed=0
DockId=0x00000006,2 DockId=0x00000006,2
[Window][Log Management] [Window][Log Management]
Pos=756,24 Pos=1537,24
Size=924,1176 Size=1023,1416
Collapsed=0 Collapsed=0
DockId=0x00000006,3 DockId=0x00000006,3
@@ -407,7 +407,7 @@ DockId=0x00000006,1
[Window][Project Settings] [Window][Project Settings]
Pos=0,24 Pos=0,24
Size=754,1176 Size=1535,1416
Collapsed=0 Collapsed=0
DockId=0x00000005,1 DockId=0x00000005,1
@@ -417,6 +417,16 @@ Size=924,1176
Collapsed=0 Collapsed=0
DockId=0x00000006,4 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] [Table][0xFB6E3870,4]
RefScale=13 RefScale=13
Column 0 Width=80 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=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 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=0x00000003 Parent=0xAFC85805 SizeRef=2175,1183 Split=X
DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 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=0x00000007 Parent=0x0000000B SizeRef=1512,858 Split=X Selected=0x8CA2375C
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=754,1681 CentralNode=1 Selected=0x418C7449 DockNode ID=0x00000005 Parent=0x00000007 SizeRef=655,1681 CentralNode=1 Selected=0x418C7449
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=924,1681 Selected=0x6F2B5B04 DockNode ID=0x00000006 Parent=0x00000007 SizeRef=1023,1681 Selected=0x6F2B5B04
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1777,858 Selected=0x418C7449 DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1777,858 Selected=0x418C7449
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1162,1183 Split=X Selected=0x3AEC3498 DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1162,1183 Split=X Selected=0x3AEC3498
+125 -170
View File
@@ -94,6 +94,39 @@ class ASTParser:
_ast_cache[path] = (mtime, tree) _ast_cache[path] = (mtime, tree)
return 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: def get_skeleton(self, code: str, path: Optional[str] = None) -> str:
""" """
Returns a skeleton of a Python file (preserving docstrings, stripping function bodies). 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) 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) parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
@@ -418,42 +421,54 @@ class ASTParser:
return None return None
target = target_parts[0] target = target_parts[0]
for child in node.children: for child in node.children:
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): # If it's a field_declaration, it might wrap a class/struct/enum definition
if get_name(child) == target: check_node = child
if len(target_parts) == 1: if child.type == "field_declaration":
return child
body = child.child_by_field_name("body")
if not body and child.type == "template_declaration":
for sub in child.children: for sub in child.children:
if sub.type in ("function_definition", "class_definition"): 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 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") body = sub.child_by_field_name("body")
break break
if body: if body:
found = walk(body, target_parts[1:]) found = walk(body, next_parts)
if found: return found if found: return found
for sub in child.children: for sub in check_node.children:
if sub.type in ("field_declaration_list", "class_body", "declaration_list"): if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
found = walk(sub, target_parts[1:]) found = walk(sub, next_parts)
if found: return found if found: return found
# Recurse for top-level or namespaces elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"):
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) found = walk(child, target_parts)
if found: return found if found: return found
return None 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]: 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 node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
if get_name(node) == target: if self._get_name(node, code) == target:
return node return node
for child in node.children: for child in node.children:
res = deep_search(child, target) res = deep_search(child, target)
if res: return res if res: return res
return None return None
found_node = deep_search(tree.root_node, parts[0])
found_node = walk(tree.root_node, parts)
if not found_node:
found_node = deep_search(tree.root_node, name)
if found_node: if found_node:
return code[found_node.start_byte:found_node.end_byte] return code[found_node.start_byte:found_node.end_byte]
@@ -466,36 +481,6 @@ class ASTParser:
""" """
tree = self.get_cached_tree(path, code) 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) parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
@@ -503,47 +488,60 @@ class ASTParser:
return None return None
target = target_parts[0] target = target_parts[0]
for child in node.children: for child in node.children:
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): # If it's a field_declaration, it might wrap a class/struct/enum definition
if get_name(child) == target: check_node = child
if len(target_parts) == 1: if child.type == "field_declaration":
return child
body = child.child_by_field_name("body")
if not body and child.type == "template_declaration":
for sub in child.children: for sub in child.children:
if sub.type in ("function_definition", "class_definition"): 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 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") body = sub.child_by_field_name("body")
break break
if body: if body:
found = walk(body, target_parts[1:]) found = walk(body, next_parts)
if found: return found if found: return found
for sub in child.children: for sub in check_node.children:
if sub.type in ("field_declaration_list", "class_body", "declaration_list"): if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
found = walk(sub, target_parts[1:]) found = walk(sub, next_parts)
if found: return found if found: return found
if node.type in ("module", "translation_unit", "namespace_definition", "declaration_list"): elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"):
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) found = walk(child, target_parts)
if found: return found if found: return found
return None 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]: def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
if node.type in ("function_definition", "template_declaration"): if node.type in ("function_definition", "template_declaration"):
if get_name(node) == target: if self._get_name(node, code) == target:
return node return node
for child in node.children: for child in node.children:
res = deep_search(child, target) res = deep_search(child, target)
if res: return res if res: return res
return None return None
found_node = deep_search(tree.root_node, parts[0])
found_node = walk(tree.root_node, parts)
if not found_node:
found_node = deep_search(tree.root_node, name)
if found_node: if found_node:
target_node = found_node target_node = found_node
if found_node.type == "template_declaration": if found_node.type == "template_declaration":
for child in found_node.children: 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 target_node = child
break break
@@ -561,31 +559,6 @@ class ASTParser:
tree = self.get_cached_tree(path, code) tree = self.get_cached_tree(path, code)
output = [] 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: def walk(node: tree_sitter.Node, indent: int = 0) -> None:
ntype = node.type ntype = node.type
label = "" label = ""
@@ -597,7 +570,7 @@ class ASTParser:
label = "[Method]" if indent > 0 else "[Func]" label = "[Method]" if indent > 0 else "[Func]"
if label: if label:
name = get_name(node) name = self._get_name(node, code)
if name: if name:
start = node.start_point.row + 1 start = node.start_point.row + 1
end = node.end_point.row + 1 end = node.end_point.row + 1
@@ -620,36 +593,6 @@ class ASTParser:
""" """
tree = self.get_cached_tree(path, code) 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) parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
@@ -657,42 +600,54 @@ class ASTParser:
return None return None
target = target_parts[0] target = target_parts[0]
for child in node.children: for child in node.children:
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): # If it's a field_declaration, it might wrap a class/struct/enum definition
if get_name(child) == target: check_node = child
if len(target_parts) == 1: if child.type == "field_declaration":
return child
body = child.child_by_field_name("body")
if not body and child.type == "template_declaration":
for sub in child.children: for sub in child.children:
if sub.type in ("function_definition", "class_definition"): 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 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") body = sub.child_by_field_name("body")
break break
if body: if body:
found = walk(body, target_parts[1:]) found = walk(body, next_parts)
if found: return found if found: return found
for sub in child.children: for sub in check_node.children:
if sub.type in ("field_declaration_list", "class_body", "declaration_list"): if sub.type in ("field_declaration_list", "class_body", "declaration_list"):
found = walk(sub, target_parts[1:]) found = walk(sub, next_parts)
if found: return found if found: return found
# Recurse for top-level or namespaces elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"):
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) found = walk(child, target_parts)
if found: return found if found: return found
return None 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]: 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 node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"):
if get_name(node) == target: if self._get_name(node, code) == target:
return node return node
for child in node.children: for child in node.children:
res = deep_search(child, target) res = deep_search(child, target)
if res: return res if res: return res
return None return None
found_node = deep_search(tree.root_node, parts[0])
found_node = walk(tree.root_node, parts)
if not found_node:
found_node = deep_search(tree.root_node, name)
if found_node: if found_node:
code_bytes = bytearray(code, "utf8") 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()
+89 -1
View File
@@ -6,7 +6,7 @@ import sys
# Add project root to sys.path # Add project root to sys.path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 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): def test_ts_cpp_get_skeleton(tmp_path):
cpp_code = """#include <iostream> cpp_code = """#include <iostream>
@@ -69,3 +69,91 @@ void templateFunc(T t) {
assert "[Func] templateFunc (Lines 8-9)" in outline assert "[Func] templateFunc (Lines 8-9)" in outline
finally: finally:
mcp_client._resolve_and_check = original_resolve 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