Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
a5fe6e1
fix(go): a call on an indexed element reaches its element type's meth…
colbymchenry Oct 21, 2026
1f3a274
fix(go): a method a type declares in a file the index couldn't read i…
colbymchenry Oct 21, 2026
847f1df
fix(go): an interface or struct embedding a standard-library type cou…
colbymchenry Oct 21, 2026
8b5d1d5
chore(scripts): regenerate the Go standard-library method table from …
colbymchenry Oct 21, 2026
dbb6aa9
fix(vbnet): link reads through a namespace or a bare member name, and…
colbymchenry Oct 21, 2026
bf6f1fa
fix(dart): a typedef references the types it aliases (#2763)
colbymchenry Oct 21, 2026
efff745
fix(go): a field typed by a dot-imported package resolves in that pac…
colbymchenry Oct 21, 2026
d6e8c91
fix(installer): upgrade keeps everything around CodeGraph's section i…
colbymchenry Oct 22, 2026
0abae11
fix(installer): a symlinked instructions or config file is written th…
colbymchenry Oct 22, 2026
1ef8866
test(explore): the cross-call dedup test passes with LF and CRLF chec…
colbymchenry Oct 22, 2026
6bd217a
fix(cpp): code that calls operator++ by its name no longer stops an i…
colbymchenry Oct 22, 2026
0de9497
fix(installer): the Codex table is found however its config.toml writ…
colbymchenry Oct 22, 2026
5a6917b
fix(c): a function whose return type is a macro call is indexed (#2773)
colbymchenry Oct 22, 2026
7dfef84
fix(installer): Hermes keeps your MCP entry and gets no platform_tool…
colbymchenry Oct 22, 2026
c326b6b
fix(go): a function value keeps its link past a local of the same nam…
colbymchenry Oct 22, 2026
57ce5b9
fix(upgrade): a global npm install is upgraded with -g from any folde…
colbymchenry Oct 22, 2026
929c65c
fix(pascal): Delphi routines with a parameter attribute like `const […
colbymchenry Oct 22, 2026
88ebefa
fix(pascal): calls inside a Delphi `for var x in` loop are indexed (#…
colbymchenry Oct 22, 2026
0c3e12d
fix(go): a call further along a chain links the method of the type th…
colbymchenry Oct 22, 2026
1890b10
fix(explore): a named function too long for its share renders instead…
colbymchenry Oct 22, 2026
800e582
fix(go): conversions, external test packages and build-tag twins link…
colbymchenry Oct 22, 2026
c465230
fix(context): the busiest file is found even when 20 test or generate…
colbymchenry Oct 22, 2026
7180fd0
fix(js,ts): a dynamic import from Node or a package no longer links t…
colbymchenry Oct 22, 2026
0953084
fix(pascal): the receiver calls of a chained Delphi call are indexed …
colbymchenry Oct 22, 2026
214ef50
fix(explore): names the query spells that the index never saw are sai…
colbymchenry Oct 22, 2026
e69546e
fix(js,ts): code outside tests resolves as if Jest manual mocks were …
colbymchenry Oct 22, 2026
8651990
fix(js,ts): an import spelled with a trailing slash or a ./ segment l…
colbymchenry Oct 22, 2026
b2a8487
fix(daemon): a long sync of another project no longer gets the shared…
colbymchenry Oct 22, 2026
8073637
fix(db): reopening an interrupted index no longer gets the server kil…
colbymchenry Oct 22, 2026
1040d09
fix(mcp): a daemon that never answers no longer stalls a new session …
colbymchenry Oct 22, 2026
63ca568
fix(db): a database SQLite can't open is reported as that, not as a N…
colbymchenry Oct 22, 2026
407efb1
fix(pascal): Delphi calls written without parentheses link to the rou…
colbymchenry Oct 22, 2026
c1744e7
fix(prompt-hook): desktop reminders and other sessions' messages no l…
colbymchenry Oct 22, 2026
e8ddc4e
fix(js,ts): imports of a repo's own packages follow their package.jso…
colbymchenry Oct 22, 2026
5d81d29
fix(js,ts): a call or new never lands on an interface or a type alias…
colbymchenry Oct 22, 2026
d82a4ad
fix(js,ts): a call or new in a parameter's default value is the funct…
colbymchenry Oct 22, 2026
7b50ca8
fix(pascal): Delphi calls to compiler intrinsics and plain data no lo…
colbymchenry Oct 22, 2026
89cd08f
fix(explore): a long query, like a pasted log, answers in seconds on …
colbymchenry Oct 22, 2026
c4dbc12
fix(bundle): bundle Node 24.21.0 so Windows commands no longer abort …
colbymchenry Oct 22, 2026
9ed79c6
fix(add-lang): check-grammar fails a grammar that crashes or hangs it…
colbymchenry Oct 22, 2026
80c3680
fix(extraction): drop the extractVariables hook, which the core never…
colbymchenry Oct 22, 2026
f3fdba2
fix(pascal): calls inside nested Delphi routines are indexed, each un…
colbymchenry Oct 22, 2026
08478ee
fix(js,ts): a receiver declared outside the calling function goes by …
colbymchenry Oct 22, 2026
efb091e
fix(liquid): editor-saved Shopify JSON and {% sections %} groups link…
colbymchenry Oct 22, 2026
68fcf2f
fix(js,ts): static and instance methods override only their own kind …
colbymchenry Oct 22, 2026
53f72fe
fix(liquid): variables declared with {% capture %} are indexed, as as…
colbymchenry Oct 22, 2026
ab92c90
fix(js,ts): a super.method() call links to the method of the class's …
colbymchenry Oct 22, 2026
df1cd1e
fix(pascal): Delphi units with re-published properties and similar sy…
colbymchenry Oct 22, 2026
b8c3c03
fix(bundle): bundle Node 24.22.1, the October security release, and v…
colbymchenry Oct 22, 2026
3f58e34
fix(cli): index no longer blocks on a daemon lock naming a process th…
colbymchenry Oct 22, 2026
e32021f
fix(java,kotlin,csharp,scala): an overload links only the overrides w…
colbymchenry Oct 23, 2026
d5fc1fb
fix(python): calls like reg.get() on a typed local or parameter reach…
colbymchenry Oct 23, 2026
628f69e
fix(kotlin): repository().pending() links the method of the type repo…
colbymchenry Oct 23, 2026
23a80a8
fix(python): a from-import of a module with no dot in its name links …
colbymchenry Oct 23, 2026
013b91c
fix(js,ts): a named class expression is a class with its own members …
colbymchenry Oct 23, 2026
9ee6718
fix(vue): a <script lang="tsx"> block is read with the TSX grammar (#…
colbymchenry Oct 23, 2026
58eb57a
fix(nuxt): navigateTo('/x') links to the page it opens, as router.pus…
colbymchenry Oct 23, 2026
5b47bc4
fix(js,ts): .tsx and .jsx classes link their overrides as .ts and .js…
colbymchenry Oct 23, 2026
dcb985f
fix(ruby): a call on a namespaced class links its caller to the class…
colbymchenry Oct 23, 2026
8fdc8a1
fix(rust): calls on field chains, struct literals and outside-type pa…
colbymchenry Oct 23, 2026
f7820b5
fix(dart): calls to a private name in another part of the same librar…
colbymchenry Oct 23, 2026
16a8e77
fix(ruby): a call reaches the class method or the instance method its…
colbymchenry Oct 23, 2026
51c4124
fix(cfml): a dotted extends finds its component when a test suite mir…
colbymchenry Oct 23, 2026
cf3e6bc
fix(ts): a type-only re-export like export type * from './types' now …
colbymchenry Oct 23, 2026
d90f6ab
fix(cfml): <cfinclude template> and cfinclude() link the code to the …
colbymchenry Oct 23, 2026
f406fe0
fix(cfml): createObject("component", path) links its component, and .…
colbymchenry Oct 23, 2026
ef05970
test(daemon): the OS process lookup tests pass on a loaded Windows ma…
colbymchenry Oct 23, 2026
6500a37
fix(cfml): a <cfquery> or <cfscript> block that fails to parse no lon…
colbymchenry Oct 23, 2026
d833827
perf(resolution): skip receiver typing for JS/TS calls of a method no…
colbymchenry Oct 23, 2026
23eb392
fix(ts): a re-export's import node keeps export type * as the file wr…
colbymchenry Oct 23, 2026
865ddf4
fix(library): indexFiles keeps other writers out until its files are …
colbymchenry Oct 23, 2026
9c4794d
fix(rust): a call keeps its whole receiver chain, and links through m…
colbymchenry Oct 23, 2026
679c4e0
test(windows): the windowsHide guard reads quoted and computed keys, …
Oct 23, 2026
2def0a6
test(kernel): skip the kernel-only suites when CODEGRAPH_KERNEL=0
Oct 23, 2026
33dc2ae
fix(viewer): the routes list's limit counts routes, not one route's c…
Oct 23, 2026
5c5bfd4
fix(php): a namespace alias matches whatever case it is written in
Oct 23, 2026
449eefa
fix(js,ts): a call on an element of a class field reaches the element…
inth3shadows Oct 23, 2026
16f2d35
fix(go): a project package named like a standard-library package is n…
Oct 23, 2026
0d46dba
fix(js,ts): a call on a new C() receiver resolves through C
Oct 23, 2026
428b3c4
fix(js,ts): a nested function takes calls to a name the file imports …
Oct 23, 2026
8f8f04b
fix(java,ts): a parameterized supertype links its type, like implemen…
Oct 23, 2026
90c5dc6
fix(extraction): a Go method carries the exportedness of its name
Oct 23, 2026
44adef8
fix(erlang): a comment in an argument list no longer counts as an arg…
Dshuishui Oct 23, 2026
a278c2e
fix(scope): a folder git ignores matches only itself, not every folde…
colbymchenry Oct 23, 2026
43a4f5b
fix(graph): the type hierarchy returns a type's subtypes, not only it…
Oct 23, 2026
d02a88d
fix(java,csharp): a call on a new object reaches the class the file i…
colbymchenry Oct 23, 2026
3750921
test(resolution): calls on nested receivers keep their evidence and g…
bompus Oct 23, 2026
a565f21
fix(viewer): saved trails stay inside the project when a folder or tr…
Oct 23, 2026
3a93f57
fix(java): a record is indexed as a class, with its components' field…
Oct 23, 2026
de2b80e
fix(mcp): a project opened for projectPath is released on its own idl…
bompus Oct 23, 2026
0b23845
fix(mcp): explore's maxFiles text gives the real default, not 12
bompus Oct 24, 2026
cde4cd7
fix(resolution): a lone same-file definition scores as a strong match
Oct 24, 2026
4c572e0
fix(play): a Class.method reference reaches a member of the class it …
Oct 23, 2026
69af19a
fix(build): building from source with bun finishes instead of startin…
bompus Oct 24, 2026
f67f4c0
Merge upstream main through 69af19ac into merge/upstream-69af19ac
bompus Oct 26, 2026
dad95c1
fix(resolve): port upstream's new Ruby, Pascal, CFML and JS/Rust reso…
bompus Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
# Native extraction-kernel prebuilds (docs/design/rust-kernel-migration-plan.md).
# The kernel is the ONLY parser (no wasm fallback since the kernel-only
# extraction plan's Phase 5), so this matrix is REQUIRED: a failed kernel
# build blocks the release, and build-bundle.sh refuses to stage a bundle
# build blocks the release, and the bundle recipe refuses to stage a bundle
# without a prebuild. (Formerly: per-file wasm fallback for erroring files and unsupported
# platforms — but every published bundle must carry its .node. Note the
# vendored-grammar-C languages (kotlin/lua/scala/dart) compile parser.c via
Expand Down
4 changes: 4 additions & 0 deletions BUNDLING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ scripts/build-bundle.sh linux-x64 # -> release/codegraph-linux-x64.ta
scripts/build-bundle.sh win32-x64 # -> release/codegraph-win32-x64.zip
```

The Node runtime is the official nodejs.org release named by the script's
default `NODE_VERSION` (or its second argument). The download is refused
unless it matches the SHA-256 listed in that release's `SHASUMS256.txt`.

Because dropping better-sqlite3 left **zero native addons**, building a bundle is
pure file-packaging — **any** target builds on **any** OS (the whole matrix builds
on one Linux runner). Cross-compilation isn't a concern; only *run-testing* a
Expand Down
240 changes: 240 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Follow [@getcodegraph](https://x.com/getcodegraph) on X for updates.

## About this fork

This is **bompus/codegraph**, a fork of [colbymchenry/codegraph](https://github.com/colbymchenry/codegraph). Its default branch, `fork/consolidated`, contains upstream `main` through [`553d5d10`](https://github.com/colbymchenry/codegraph/commit/553d5d101dd8c8ed65e83480e31192e836d8431a) (after v1.6.2) plus the fork's own work, and it takes upstream changes as they land. Changes that suit upstream are also offered there as pull requests.
This is **bompus/codegraph**, a fork of [colbymchenry/codegraph](https://github.com/colbymchenry/codegraph). Its default branch, `fork/consolidated`, contains upstream `main` through [`69af19ac`](https://github.com/colbymchenry/codegraph/commit/69af19ace270f2e940444d514de6dde971df12df) (after v1.6.2) plus the fork's own work, and it takes upstream changes as they land. Changes that suit upstream are also offered there as pull requests.

The fork publishes no releases. The install scripts, npm package, badges and `codegraph upgrade` further down this page install **upstream's** releases. To run the fork, build it from source (below).

Expand Down
94 changes: 94 additions & 0 deletions __tests__/bundle-launcher.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterEach, describe, expect, it } from 'vitest';
import { spawnSync } from 'child_process';
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
Expand Down Expand Up @@ -124,3 +125,96 @@ describe('bundled launchers (#1278)', () => {
}
});
});

describe('bundled Node runtime (#2617)', () => {
it('ships a Node 24 release with the fix for the Windows abort at exit', () => {
// The release workflow passes only the target, so the recipe's default
// version is the Node every release bundles.
const workflow = fs.readFileSync(path.resolve('.github/workflows/release.yml'), 'utf8');
const calls = workflow.match(/build-bundle\.sh[^\r\n]*/g) ?? [];
expect(calls.length).toBeGreaterThan(0);
for (const call of calls) expect(call.trim()).toBe('build-bundle.sh "$t"');

const pin = /^NODE_VERSION="\$\{2:-v(\d+)\.(\d+)\.(\d+)\}"\r?$/m.exec(recipe);
expect(pin, 'default NODE_VERSION in scripts/build-bundle.sh').not.toBeNull();
const [major, minor] = pin!.slice(1, 3).map(Number);
// The CLI refuses to run on Node 25 and later.
expect(major).toBe(24);
// Node 24 before 24.20.0 can abort on Windows as the process exits, after
// the command's work is done (0xC0000409, nodejs/node#56645).
expect(minor).toBeGreaterThanOrEqual(20);
});
});

describe('bundled Node security release (#2780)', () => {
const archive = 'node-v24.22.1-linux-x64.tar.gz';

// Step 1 of the release recipe, run against a stand-in for nodejs.org: `curl`
// copies the requested file out of `site/` and logs the URL it was asked for.
function download(sums: (sha: string) => string) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-node-download-'));
roots.push(root);
const site = path.join(root, 'site');
const work = path.join(root, 'work');
fs.mkdirSync(path.join(site, 'node-v24.22.1-linux-x64', 'bin'), { recursive: true });
fs.mkdirSync(work);
fs.writeFileSync(path.join(site, 'node-v24.22.1-linux-x64', 'bin', 'node'), '#!/bin/sh\n');
const arg = (n: number) => windows ? `$(cygpath -u "$${n}")` : `$${n}`;
// Packed the way nodejs.org packs a release: one top-level folder.
const pack = shell(`set -eu\ncd "${arg(1)}"\ntar -czf ${archive} node-v24.22.1-linux-x64`, [site]);
expect(pack.status, pack.stderr).toBe(0);
const sha = crypto.createHash('sha256').update(fs.readFileSync(path.join(site, archive))).digest('hex');
fs.writeFileSync(path.join(site, 'SHASUMS256.txt'), sums(sha));
const step = recipe.slice(recipe.indexOf('# 1. Download'), recipe.indexOf('# 2. Build'));
const result = shell(`set -euo pipefail
WORK="${arg(1)}"
SITE="${arg(2)}"
TARGET=linux-x64 OSFAM=linux ARCH=x64 NODE_VERSION=v24.22.1
curl() {
local url='' out=''
while [ $# -gt 0 ]; do
case "$1" in -o) out="$2"; shift 2 ;; -*) shift ;; *) url="$1"; shift ;; esac
done
printf '%s\\n' "$url" >> "$WORK/urls"
cp "$SITE/\${url##*/}" "$out"
}
${step}
printf 'NODE_BIN=%s\\n' "$NODE_BIN"`, [work, site]);
const urls = fs.existsSync(path.join(work, 'urls'))
? fs.readFileSync(path.join(work, 'urls'), 'utf8').trim().split('\n')
: [];
return { result, work, urls };
}

it('ships Node 24.22.1, the October 2026 security release, or a later 24.x', () => {
const pin = /^NODE_VERSION="\$\{2:-v(\d+)\.(\d+)\.(\d+)\}"\r?$/m.exec(recipe);
expect(pin, 'default NODE_VERSION in scripts/build-bundle.sh').not.toBeNull();
const [major, minor, patch] = pin!.slice(1, 4).map(Number);
expect(major).toBe(24);
// 24.22.1 fixes eight CVEs (four rated High) and ships OpenSSL 3.5.9.
expect(minor > 22 || (minor === 22 && patch >= 1), `v${major}.${minor}.${patch}`).toBe(true);
});

it('unpacks the Node download once it matches the SHA-256 nodejs.org publishes for it', () => {
const other = 'f'.repeat(64);
const { result, work, urls } = download((sha) =>
`${other} node-v24.22.1-darwin-arm64.tar.gz\n${sha} ${archive}\n${other} node-v24.22.1-win-x64.zip\n`);
expect(result.status, result.stderr).toBe(0);
expect(urls).toEqual([
`https://nodejs.org/dist/v24.22.1/${archive}`,
'https://nodejs.org/dist/v24.22.1/SHASUMS256.txt',
]);
expect(fs.existsSync(path.join(work, 'node-v24.22.1-linux-x64', 'bin', 'node'))).toBe(true);
expect(result.stdout).toMatch(/^NODE_BIN=.*node-v24\.22\.1-linux-x64\/bin\/node\r?$/m);
});

it.each([
['whose SHA-256 differs from the published one', () => `${'0'.repeat(64)} ${archive}\n`],
['that the published list leaves out', (sha: string) => `${sha} node-v24.22.1-linux-arm64.tar.gz\n`],
])('refuses a Node download %s, before unpacking it', (_, sums) => {
const { result, work } = download(sums);
expect(result.status).toBe(1);
expect(result.stderr).toContain(`${archive} does not match the SHA-256 that nodejs.org lists for v24.22.1`);
expect(fs.existsSync(path.join(work, 'node-v24.22.1-linux-x64'))).toBe(false);
});
});
174 changes: 174 additions & 0 deletions __tests__/c-macro-return-types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/**
* A C function whose return type is written as a macro call: cJSON's
* `CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)`, libgit2's
* `GIT_EXTERN(int) git_blob_dup(…);`. The pre-parse blank for line-leading
* markup macros (Unreal's `UPROPERTY(…)`, redis's `REDIS_NO_SANITIZE("bounds")`)
* took the macro for markup and blanked it with the return type it wraps. What
* was left, `cJSON_Print(…)` and a block, parses as a call: the function had
* no node, its callers resolved to nothing, and a header's prototype read as
* a call from the header (#2470).
*
* Markup above a declaration is still blanked: one with a type of its own,
* and jemalloc's attribute between a type on the line above and the name.
*/
import { describe, it, expect, afterEach } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import CodeGraph from '../src/index';
import type { Node } from '../src/types';
import { blankCppAnnotationMacroCalls } from '../src/extraction/languages/c-cpp';

const lines = (...l: string[]): string => [...l, ''].join('\n');

/** The 1-based line of `text` that contains `needle`, or is exactly it with `exact` (exactly one must). */
const lineOf = (text: string, needle: string, exact = false): number => {
const at = text.split('\n').map((l, i) => ((exact ? l === needle : l.includes(needle)) ? i + 1 : 0)).filter(Boolean);
if (at.length !== 1) throw new Error(`${JSON.stringify(needle)} is on ${at.length} lines`);
return at[0]!;
};

// The issue's reproduction, then shapes around it.
const LIB_C = lines(
'#define EXPORT(type) type',
'#define ATTR_FORMAT(a, b)',
'#define ATTR(x)',
'#define REDIS_NO_SANITIZE(s)',
'',
'EXPORT(char *) make_name(int id)',
'{',
' return id > 0 ? "named" : "anonymous";',
'}',
'',
'EXPORT(int) version(void)',
'{',
' return 3;',
'}',
'',
'/* GNU style: the name on its own line. */',
'EXPORT(const char *)',
'label_for(int id)',
'{',
' return id ? "x" : "y";',
'}',
'',
'EXPORT(int) checked_sum(int a, int b) __attribute__((warn_unused_result));',
'EXPORT(int) checked_sum(int a, int b)',
'{',
' return a + b;',
'}',
'',
'ATTR_FORMAT(1, 2)',
'void log_line(const char *fmt, ...)',
'{',
'}',
'',
'REDIS_NO_SANITIZE("bounds")',
'int key_slot(const char *key)',
'{',
' return key[0];',
'}',
'',
'unsigned long',
'ATTR(pure)',
'usable_size(const void *ptr)',
'{',
' return ptr != 0;',
'}',
'',
'int caller(void)',
'{',
' log_line("%d", version());',
' return make_name(1) != 0 && usable_size(0) && label_for(2) && checked_sum(1, 2) && key_slot("k");',
'}',
);

// libgit2's header shape: prototypes after a lone macro line and after a doc comment.
const API_H = lines(
'#ifndef API_H',
'#define API_H',
'#define GIT_EXTERN(type) extern type',
'#define GIT_BEGIN_DECL',
'GIT_BEGIN_DECL',
'',
'GIT_EXTERN(int) git_blob_dup(int **out, int *source);',
'',
'/** Looks the name up. */',
'GIT_EXTERN(const char *) git_name(int id);',
'#endif',
);

const API_C = lines(
'#include "api.h"',
'int git_blob_dup(int **out, int *source) { *out = source; return 0; }',
'const char *git_name(int id) { return id ? "a" : "b"; }',
'int use_api(void) { int *p = 0; return git_blob_dup(&p, 0) + (git_name(1) != 0); }',
);

describe('a C function whose return type is a macro call', () => {
const roots: string[] = [];
const graphs: CodeGraph[] = [];

afterEach(() => {
for (const cg of graphs.splice(0)) cg.close();
for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });
});

const index = async (files: Record<string, string>): Promise<CodeGraph> => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-c-macro-return-'));
roots.push(root);
for (const [rel, text] of Object.entries(files)) fs.writeFileSync(path.join(root, rel), text);
const cg = await CodeGraph.init(root, { index: true });
graphs.push(cg);
return cg;
};

it('is indexed as a function on its line, and its callers reach it', async () => {
const cg = await index({ 'lib.c': LIB_C });
const functions = cg.getNodesInFile('lib.c').filter((n: Node) => n.kind === 'function')
.map((n) => `${n.name}@${n.startLine}`).sort();
expect(functions).toEqual([
`caller@${lineOf(LIB_C, 'int caller(void)')}`,
`checked_sum@${lineOf(LIB_C, 'EXPORT(int) checked_sum(int a, int b)', true)}`,
`key_slot@${lineOf(LIB_C, 'int key_slot(')}`,
`label_for@${lineOf(LIB_C, 'EXPORT(const char *)')}`,
`log_line@${lineOf(LIB_C, 'void log_line(')}`,
`make_name@${lineOf(LIB_C, 'make_name(int id)')}`,
`usable_size@${lineOf(LIB_C, 'unsigned long')}`,
`version@${lineOf(LIB_C, 'EXPORT(int) version(void)')}`,
].sort());
const caller = cg.getNodesInFile('lib.c').find((n: Node) => n.name === 'caller')!;
const callees = cg.getCallees(caller.id).filter((r) => r.edge.kind === 'calls').map((r) => r.node.name).sort();
expect(callees).toEqual(['checked_sum', 'key_slot', 'label_for', 'log_line', 'make_name', 'usable_size', 'version']);
}, 120_000);

it('a header prototype declares it, and is no call from the header', async () => {
const cg = await index({ 'api.h': API_H, 'api.c': API_C });
const header = cg.getNodesInFile('api.h').find((n: Node) => n.kind === 'file')!;
expect(cg.getOutgoingEdgesFrom([header.id]).filter((e) => e.kind === 'calls')).toEqual([]);
const useApi = cg.getNodesInFile('api.c').find((n: Node) => n.name === 'use_api')!;
expect(cg.getCallees(useApi.id).filter((r) => r.edge.kind === 'calls').map((r) => `${r.node.name}@${r.node.filePath}`).sort())
.toEqual(['git_blob_dup@api.c', 'git_name@api.c']);
}, 120_000);

it('the pre-parse blank leaves it alone, and still blanks markup', () => {
const keep = (text: string): string => blankCppAnnotationMacroCalls(text, { keepCReturnTypes: true });
const blank = (text: string): string => text.replace(/[^\n\r]/g, ' ');
const kept = [
'CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)\n{\n}\n',
'GIT_EXTERN(int) git_blob_dup(git_blob **out, git_blob *source);\n',
'GIT_INLINE(void) git_free(void *p)\n{\n}\n',
];
for (const text of kept) expect(keep(text)).toBe(text);
// Markup: not a type, a declaration with its own type, the type on the line above, a statement in a body.
expect(keep('ATTR_FORMAT(1, 2)\nvoid log_line(const char *fmt, ...);\n'))
.toBe(`${blank('ATTR_FORMAT(1, 2)')}\nvoid log_line(const char *fmt, ...);\n`);
expect(keep('ATTR(cold) void die(void);\n')).toBe(`${blank('ATTR(cold)')} void die(void);\n`);
expect(keep('size_t JEMALLOC_NOTHROW\nJEMALLOC_ATTR(pure)\nje_sallocx(const void *ptr, int flags) {\n}\n'))
.toBe(`size_t JEMALLOC_NOTHROW\n${blank('JEMALLOC_ATTR(pure)')}\nje_sallocx(const void *ptr, int flags) {\n}\n`);
expect(keep('void f(void) {\n FOREACH(node) visit(node);\n}\n'))
.toBe(`void f(void) {\n ${blank('FOREACH(node)')} visit(node);\n}\n`);
// C++ gets no exemption.
expect(blankCppAnnotationMacroCalls('GIT_EXTERN(int) git_blob_dup(void);\n')).toBe(`${blank('GIT_EXTERN(int)')} git_blob_dup(void);\n`);
});
});
Loading
Loading