mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-07 02:58:02 +00:00
fix: C/C++/C# language support - flows from 0 to 300 on real repos
- fix(c/cpp): isExported was hardcoded false; now checks static linkage - fix(c/cpp): findEnclosingFunctionId - function name is nested in declarator -> function_declarator -> identifier/qualified_identifier, not a direct 'name' field. All CALLS were sourced from File nodes. - fix(cpp): qualified_identifier methods (ImGui::Foo) were registered as 'Method' nodes but findEnclosingFunctionId returned 'Function' label, causing ID mismatch. Fix sets label = 'Method' for qualified_identifier. - fix(c#): CSHARP_QUERIES used 'simple_base_type' which is not a valid node type in tree-sitter-c-sharp. Query silently failed to compile, producing 0 nodes/flows for all C# repos. Fixed to use correct AST structure: base_list directly contains identifier/generic_name. - fix(builtins): Remove 'open', 'read', 'write', 'close' from BUILT_INS set — these are real POSIX syscalls in C, not Python builtins to ignore. - feat(entry-points): Expand C/C++ entry point scoring patterns (~30 new patterns: _init, _run, handle_, _handler, cmd_, server_, session_, etc.) - feat(cpp): Add tree-sitter query for inline class methods defined inside class body (field_declaration with function_declarator) Results on test repos: - tmux (C): 0 → 300 flows, 0 → 22,686 edges - curl (C): 300 flows, 53,229 edges (verified meaningful) - imgui (C++): 218 flows, 5,476 edges, Method CALLS working - ShareX (C#): 0 → 300 flows, 31,338 edges (was completely broken)
This commit is contained in:
parent
fa9ba8925c
commit
e5d3480fa3
5 changed files with 123 additions and 18 deletions
|
|
@ -75,10 +75,26 @@ const findEnclosingFunction = (
|
|||
current.type === 'async_function_declaration' ||
|
||||
current.type === 'generator_function_declaration' ||
|
||||
current.type === 'function_item') { // Rust function
|
||||
// Named function: function foo() {}
|
||||
// Try direct name field (JS/TS/Python/Rust)
|
||||
const nameNode = current.childForFieldName?.('name') ||
|
||||
current.children?.find((c: any) => c.type === 'identifier' || c.type === 'property_identifier');
|
||||
funcName = nameNode?.text;
|
||||
if (nameNode) {
|
||||
funcName = nameNode.text;
|
||||
} else {
|
||||
// C/C++: name nested inside declarator -> function_declarator -> identifier
|
||||
const declarator = current.childForFieldName?.('declarator');
|
||||
if (declarator) {
|
||||
const innerDecl = declarator.childForFieldName?.('declarator');
|
||||
if (innerDecl?.type === 'identifier') {
|
||||
funcName = innerDecl.text;
|
||||
} else if (innerDecl?.type === 'qualified_identifier') {
|
||||
const nameIdent = innerDecl.childForFieldName?.('name') ||
|
||||
innerDecl.children?.find((c: any) => c.type === 'identifier');
|
||||
funcName = nameIdent?.text;
|
||||
label = 'Method'; // qualified_identifier => registered as Method
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (current.type === 'impl_item') {
|
||||
// Rust method inside impl block: wrapper around function_item or const_item
|
||||
// We need to look inside for the function_item
|
||||
|
|
@ -325,7 +341,8 @@ const BUILT_IN_NAMES = new Set([
|
|||
'hasOwnProperty', 'toString', 'valueOf',
|
||||
// Python built-ins
|
||||
'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
|
||||
'open', 'read', 'write', 'close', 'append', 'extend', 'update',
|
||||
'append', 'extend', 'update',
|
||||
// NOTE: 'open', 'read', 'write', 'close' removed — these are real C POSIX syscalls
|
||||
'super', 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
|
||||
'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
|
||||
// Kotlin stdlib (IMPORTANT: keep in sync with parse-worker.ts BUILT_IN_NAMES)
|
||||
|
|
|
|||
|
|
@ -86,21 +86,60 @@ const ENTRY_POINT_PATTERNS: Record<string, RegExp[]> = {
|
|||
/^spawn/, // Async spawn
|
||||
],
|
||||
|
||||
// C - explicit main() boost (critical for C programs)
|
||||
// C - explicit main() boost plus common C entry point conventions
|
||||
'c': [
|
||||
/^main$/, // THE entry point
|
||||
/^init_/, // Initialization functions
|
||||
/^start_/, // Start functions
|
||||
/^run_/, // Run functions
|
||||
/^init_/, // init_server, init_client
|
||||
/_init$/, // module_init, server_init
|
||||
/^start_/, // start_server
|
||||
/_start$/, // thread_start
|
||||
/^run_/, // run_loop
|
||||
/_run$/, // event_run
|
||||
/^stop_/, // stop_server
|
||||
/_stop$/, // service_stop
|
||||
/^open_/, // open_connection
|
||||
/_open$/, // file_open
|
||||
/^close_/, // close_connection
|
||||
/_close$/, // socket_close
|
||||
/^create_/, // create_session
|
||||
/_create$/, // object_create
|
||||
/^destroy_/, // destroy_session
|
||||
/_destroy$/, // object_destroy
|
||||
/^handle_/, // handle_request
|
||||
/_handler$/, // signal_handler
|
||||
/_callback$/, // event_callback
|
||||
/^cmd_/, // tmux: cmd_new_window, cmd_attach_session
|
||||
/^server_/, // server_start, server_loop
|
||||
/^client_/, // client_connect
|
||||
/^session_/, // session_create
|
||||
/^window_/, // window_resize (tmux)
|
||||
/^key_/, // key_press
|
||||
/^input_/, // input_parse
|
||||
/^output_/, // output_write
|
||||
/^notify_/, // notify_client
|
||||
/^control_/, // control_start
|
||||
],
|
||||
|
||||
// C++ - same as C plus class patterns
|
||||
// C++ - same as C plus OOP/template patterns
|
||||
'cpp': [
|
||||
/^main$/, // THE entry point
|
||||
/^init_/,
|
||||
/_init$/,
|
||||
/^Create[A-Z]/, // Factory patterns
|
||||
/^create_/,
|
||||
/^Run$/, // Run methods
|
||||
/^run$/,
|
||||
/^Start$/, // Start methods
|
||||
/^start$/,
|
||||
/^handle_/,
|
||||
/_handler$/,
|
||||
/_callback$/,
|
||||
/^OnEvent/, // Event callbacks
|
||||
/^on_/,
|
||||
/::Run$/, // Class::Run
|
||||
/::Start$/, // Class::Start
|
||||
/::Init$/, // Class::Init
|
||||
/::Execute$/, // Class::Execute
|
||||
],
|
||||
|
||||
// Swift / iOS
|
||||
|
|
|
|||
|
|
@ -159,11 +159,25 @@ export const isNodeExported = (node: any, name: string, language: string): boole
|
|||
// No visibility modifier = public (Kotlin default)
|
||||
return true;
|
||||
|
||||
// C/C++: No native export concept at language level
|
||||
// Entry points will be detected via name patterns (main, etc.)
|
||||
// C/C++: Functions without 'static' storage class have external linkage
|
||||
// by default, making them globally accessible (equivalent to exported).
|
||||
// Only functions explicitly marked 'static' are file-scoped (not exported).
|
||||
case 'c':
|
||||
case 'cpp':
|
||||
return false;
|
||||
case 'cpp': {
|
||||
// Walk up to the function_definition/declaration and check for 'static'
|
||||
let cur = node;
|
||||
while (cur) {
|
||||
if (cur.type === 'function_definition' || cur.type === 'declaration') {
|
||||
// Check text before the opening brace (or semicolon) for 'static'
|
||||
const declText: string = (cur.text || '').split('{')[0].split(';')[0];
|
||||
// 'static' as a storage class (not 'static_assert' etc.)
|
||||
if (/\bstatic\b/.test(declText)) return false;
|
||||
return true; // No 'static' = external linkage = exported
|
||||
}
|
||||
cur = cur.parent;
|
||||
}
|
||||
return true; // Top-level C/C++ functions default to external linkage
|
||||
}
|
||||
|
||||
// Swift: Check for 'public' or 'open' access modifiers
|
||||
case 'swift':
|
||||
|
|
|
|||
|
|
@ -232,6 +232,9 @@ export const CPP_QUERIES = `
|
|||
(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function
|
||||
(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.method
|
||||
|
||||
; Inline class methods (defined inside the class body)
|
||||
(field_declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.method
|
||||
|
||||
; Templates
|
||||
(template_declaration (class_specifier name: (type_identifier) @name)) @definition.template
|
||||
(template_declaration (function_definition declarator: (function_declarator declarator: (identifier) @name))) @definition.template
|
||||
|
|
@ -282,9 +285,9 @@ export const CSHARP_QUERIES = `
|
|||
|
||||
; Heritage
|
||||
(class_declaration name: (identifier) @heritage.class
|
||||
(base_list (simple_base_type (identifier) @heritage.extends))) @heritage
|
||||
(base_list (identifier) @heritage.extends)) @heritage
|
||||
(class_declaration name: (identifier) @heritage.class
|
||||
(base_list (simple_base_type (generic_name (identifier) @heritage.extends)))) @heritage
|
||||
(base_list (generic_name (identifier) @heritage.extends))) @heritage
|
||||
`;
|
||||
|
||||
// Rust queries - works with tree-sitter-rust
|
||||
|
|
|
|||
|
|
@ -225,9 +225,22 @@ const isNodeExported = (node: any, name: string, language: string): boolean => {
|
|||
// No visibility modifier = public (Kotlin default)
|
||||
return true;
|
||||
|
||||
// C/C++: Functions without 'static' storage class have external linkage
|
||||
// by default, making them globally accessible (equivalent to exported).
|
||||
// Only functions explicitly marked 'static' are file-scoped (not exported).
|
||||
case 'c':
|
||||
case 'cpp':
|
||||
return false;
|
||||
case 'cpp': {
|
||||
let cur = node;
|
||||
while (cur) {
|
||||
if (cur.type === 'function_definition' || cur.type === 'declaration') {
|
||||
const declText: string = (cur.text || '').split('{')[0].split(';')[0];
|
||||
if (/\bstatic\b/.test(declText)) return false;
|
||||
return true;
|
||||
}
|
||||
cur = cur.parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case 'php':
|
||||
// Top-level classes/interfaces/traits are always accessible
|
||||
|
|
@ -297,9 +310,27 @@ const findEnclosingFunctionId = (node: any, filePath: string): string | null =>
|
|||
|
||||
if (['function_declaration', 'function_definition', 'async_function_declaration',
|
||||
'generator_function_declaration', 'function_item'].includes(current.type)) {
|
||||
// Try direct name field (JS/TS/Python/Rust)
|
||||
const nameNode = current.childForFieldName?.('name') ||
|
||||
current.children?.find((c: any) => c.type === 'identifier' || c.type === 'property_identifier');
|
||||
funcName = nameNode?.text;
|
||||
if (nameNode) {
|
||||
funcName = nameNode.text;
|
||||
} else {
|
||||
// C/C++: name is nested in declarator -> function_declarator -> identifier/qualified_identifier
|
||||
const declarator = current.childForFieldName?.('declarator');
|
||||
if (declarator) {
|
||||
const innerDecl = declarator.childForFieldName?.('declarator');
|
||||
if (innerDecl?.type === 'identifier') {
|
||||
funcName = innerDecl.text;
|
||||
} else if (innerDecl?.type === 'qualified_identifier') {
|
||||
// C++ qualified name: Foo::bar — captured as 'Method' node
|
||||
const nameIdent = innerDecl.childForFieldName?.('name') ||
|
||||
innerDecl.children?.find((c: any) => c.type === 'identifier');
|
||||
funcName = nameIdent?.text;
|
||||
label = 'Method'; // qualified_identifier => registered as Method
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (current.type === 'impl_item') {
|
||||
const funcItem = current.children?.find((c: any) => c.type === 'function_item');
|
||||
if (funcItem) {
|
||||
|
|
@ -359,7 +390,8 @@ const BUILT_INS = new Set([
|
|||
'hasOwnProperty', 'toString', 'valueOf',
|
||||
// Python
|
||||
'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
|
||||
'open', 'read', 'write', 'close', 'append', 'extend', 'update',
|
||||
'append', 'extend', 'update',
|
||||
// NOTE: 'open', 'read', 'write', 'close' removed — these are real C POSIX syscalls
|
||||
'super', 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
|
||||
'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
|
||||
// Kotlin stdlib (IMPORTANT: keep in sync with call-processor.ts BUILT_IN_NAMES)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue