|
|
@@ -11,7 +11,7 @@ import * as os from 'os';
|
|
|
import { CodeGraph } from '../src';
|
|
|
import { extractFromSource, scanDirectory, buildDefaultIgnore, discoverEmbeddedRepoRoots, buildScopeIgnore } from '../src/extraction';
|
|
|
import { detectLanguage, isLanguageSupported, getSupportedLanguages, initGrammars, loadAllGrammars, isSourceFile } from '../src/extraction/grammars';
|
|
|
-import { stripCppTemplateArgs, blankCppExportMacros } from '../src/extraction/languages/c-cpp';
|
|
|
+import { stripCppTemplateArgs, blankCppExportMacros, blankCppInlineMacros } from '../src/extraction/languages/c-cpp';
|
|
|
import { normalizePath } from '../src/utils';
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
@@ -2928,6 +2928,60 @@ class APXCharacter { // the one real definition
|
|
|
});
|
|
|
});
|
|
|
|
|
|
+ describe('C++ macro-prefixed function names (#1093 follow-up)', () => {
|
|
|
+ // An unknown inline-specifier macro before the return type
|
|
|
+ // (`FORCEINLINE FString GetName(…)`) threw tree-sitter into error recovery:
|
|
|
+ // the macro became the return type and — for a non-primitive return — the
|
|
|
+ // return type was glued onto the name (`"FString GetName"`), so the function
|
|
|
+ // was unfindable by name and its callers didn't link. `blankCppInlineMacros`
|
|
|
+ // blanks the known UE inline macros before parsing (offset-preserving), the
|
|
|
+ // same recover-don't-drop approach as the macro-annotated-class fix. Pervasive
|
|
|
+ // in Unreal Engine (`FORCEINLINE`).
|
|
|
+ const infoOf = (code: string) =>
|
|
|
+ extractFromSource('m.cpp', code).nodes
|
|
|
+ .filter((n) => n.kind === 'method' || n.kind === 'function')
|
|
|
+ .map((n) => ({ name: n.name, ret: n.returnType }));
|
|
|
+
|
|
|
+ it('recovers the real name AND return type of a FORCEINLINE function', () => {
|
|
|
+ expect(infoOf('static FORCEINLINE FString GetName(int V) { return H(V); }')).toEqual([
|
|
|
+ { name: 'GetName', ret: 'FString' },
|
|
|
+ ]);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles the templated UE helper shape (GetEnumerationToString)', () => {
|
|
|
+ const names = infoOf(
|
|
|
+ 'template <typename E> static FORCEINLINE FString GetEnumerationToString(const E V) { return H(V); }'
|
|
|
+ ).map((x) => x.name);
|
|
|
+ expect(names).toContain('GetEnumerationToString');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles FORCENOINLINE / FORCEINLINE_DEBUGGABLE, methods, void, and reference returns', () => {
|
|
|
+ expect(infoOf('FORCENOINLINE FString A(int V){return H(V);}').map((x) => x.name)).toContain('A');
|
|
|
+ expect(infoOf('FORCEINLINE_DEBUGGABLE FString B(int V){return H(V);}').map((x) => x.name)).toContain('B');
|
|
|
+ expect(infoOf('struct S { FORCEINLINE FString GetName(int V) { return H(V); } };').map((x) => x.name)).toContain('GetName');
|
|
|
+ expect(infoOf('static FORCEINLINE void DoThing(int V) { H(V); }').map((x) => x.name)).toContain('DoThing');
|
|
|
+ expect(infoOf('static FORCEINLINE const FString& GetRef(int V) { return H(V); }').map((x) => x.name)).toContain('GetRef');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('leaves ordinary functions and real all-caps return types untouched (controls)', () => {
|
|
|
+ expect(infoOf('FString GetName(int V) { return H(V); }')).toEqual([{ name: 'GetName', ret: 'FString' }]);
|
|
|
+ // A real all-caps type that is NOT a listed inline macro stays the return type.
|
|
|
+ expect(infoOf('HRESULT DoIt(int V) { return H(V); }')).toEqual([{ name: 'DoIt', ret: 'HRESULT' }]);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('blankCppInlineMacros preserves offsets and only touches specifier-position macros', () => {
|
|
|
+ // Blanked with equal-length spaces (byte offsets preserved).
|
|
|
+ expect(blankCppInlineMacros('FORCEINLINE FString F()')).toBe(' FString F()');
|
|
|
+ expect(blankCppInlineMacros('FORCEINLINE FString F()')).toHaveLength('FORCEINLINE FString F()'.length);
|
|
|
+ // Not in specifier position → untouched: string literals, expressions,
|
|
|
+ // longer word (`FORCEINLINE_COUNT`), and the fast path.
|
|
|
+ expect(blankCppInlineMacros('const char* s = "FORCEINLINE";')).toBe('const char* s = "FORCEINLINE";');
|
|
|
+ expect(blankCppInlineMacros('x = FORCEINLINE + 1;')).toBe('x = FORCEINLINE + 1;');
|
|
|
+ expect(blankCppInlineMacros('int FORCEINLINE_COUNT = 3;')).toBe('int FORCEINLINE_COUNT = 3;');
|
|
|
+ expect(blankCppInlineMacros('no macros here')).toBe('no macros here');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
describe('C++ templated base-class inheritance (#1043)', () => {
|
|
|
// Inheriting from a template (`class D : public Base<int>`) recorded the base
|
|
|
// ref as the full instantiation `Base<int>`, which never name-matched the
|