Files
codegraph/src/extraction/languages/c-cpp.ts
T
Colby McHenry 6f34be38aa feat: Add C/C++ typedef enum and struct extraction with inner type resolution
Addresses C/C++ typedef syntax where anonymous enum/struct definitions are wrapped in typedef declarations (e.g. `typedef enum { A, B } MyEnum;`). Adds resolveTypeAliasKind to identify inner enum_specifier and struct_specifier nodes within typedefs, enabling proper extraction of enum members and struct fields from the inner anonymous definitions rather than treating them as simple type aliases.
2026-04-06 22:58:35 -05:00

108 lines
4.3 KiB
TypeScript

import type { Node as SyntaxNode } from 'web-tree-sitter';
import { getChildByField, getNodeText } from '../tree-sitter-helpers';
import type { LanguageExtractor } from '../tree-sitter-types';
export const cExtractor: LanguageExtractor = {
functionTypes: ['function_definition'],
classTypes: [],
methodTypes: [],
interfaceTypes: [],
structTypes: ['struct_specifier'],
enumTypes: ['enum_specifier'],
enumMemberTypes: ['enumerator'],
typeAliasTypes: ['type_definition'], // typedef
importTypes: ['preproc_include'],
callTypes: ['call_expression'],
variableTypes: ['declaration'],
nameField: 'declarator',
bodyField: 'body',
paramsField: 'parameters',
resolveTypeAliasKind: (node, _source) => {
// C typedef: `typedef enum { ... } name;` or `typedef struct { ... } name;`
// The inner enum_specifier/struct_specifier is anonymous, but we want the typedef name
// to become the enum/struct node name.
for (let i = 0; i < node.namedChildCount; i++) {
const child = node.namedChild(i);
if (!child) continue;
if (child.type === 'enum_specifier' && getChildByField(child, 'body')) return 'enum';
if (child.type === 'struct_specifier' && getChildByField(child, 'body')) return 'struct';
}
return undefined;
},
extractImport: (node, source) => {
const importText = source.substring(node.startIndex, node.endIndex).trim();
// C includes: #include <stdio.h>, #include "myheader.h"
const systemLib = node.namedChildren.find((c: SyntaxNode) => c.type === 'system_lib_string');
if (systemLib) {
return { moduleName: getNodeText(systemLib, source).replace(/^<|>$/g, ''), signature: importText };
}
const stringLiteral = node.namedChildren.find((c: SyntaxNode) => c.type === 'string_literal');
if (stringLiteral) {
const stringContent = stringLiteral.namedChildren.find((c: SyntaxNode) => c.type === 'string_content');
if (stringContent) {
return { moduleName: getNodeText(stringContent, source), signature: importText };
}
}
return null;
},
};
export const cppExtractor: LanguageExtractor = {
functionTypes: ['function_definition'],
classTypes: ['class_specifier'],
methodTypes: ['function_definition'],
interfaceTypes: [],
structTypes: ['struct_specifier'],
enumTypes: ['enum_specifier'],
enumMemberTypes: ['enumerator'],
typeAliasTypes: ['type_definition', 'alias_declaration'], // typedef and using
importTypes: ['preproc_include'],
callTypes: ['call_expression'],
variableTypes: ['declaration'],
nameField: 'declarator',
bodyField: 'body',
paramsField: 'parameters',
getVisibility: (node) => {
// Check for access specifier in parent
const parent = node.parent;
if (parent) {
for (let i = 0; i < parent.childCount; i++) {
const child = parent.child(i);
if (child?.type === 'access_specifier') {
const text = child.text;
if (text.includes('public')) return 'public';
if (text.includes('private')) return 'private';
if (text.includes('protected')) return 'protected';
}
}
}
return undefined;
},
resolveTypeAliasKind: (node, _source) => {
// C++ typedef: `typedef enum { ... } name;` or `typedef struct { ... } name;`
for (let i = 0; i < node.namedChildCount; i++) {
const child = node.namedChild(i);
if (!child) continue;
if (child.type === 'enum_specifier' && getChildByField(child, 'body')) return 'enum';
if (child.type === 'struct_specifier' && getChildByField(child, 'body')) return 'struct';
}
return undefined;
},
extractImport: (node, source) => {
const importText = source.substring(node.startIndex, node.endIndex).trim();
// C++ includes: #include <iostream>, #include "myheader.h"
const systemLib = node.namedChildren.find((c: SyntaxNode) => c.type === 'system_lib_string');
if (systemLib) {
return { moduleName: getNodeText(systemLib, source).replace(/^<|>$/g, ''), signature: importText };
}
const stringLiteral = node.namedChildren.find((c: SyntaxNode) => c.type === 'string_literal');
if (stringLiteral) {
const stringContent = stringLiteral.namedChildren.find((c: SyntaxNode) => c.type === 'string_content');
if (stringContent) {
return { moduleName: getNodeText(stringContent, source), signature: importText };
}
}
return null;
},
};