Files
codegraph/__tests__/r-call-skips-data-binding.test.ts
T
Colby MchenryandClaude Opus 5.5 a925fa0920 fix(r): a call never reaches a data binding that shadows a base function (#2239)
R looks a call up among functions only; a calls ref named after a base R
function (c, list, df, …) is refused a variable/constant target that is not
bound to a function literal.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 03:10:36 +00:00

51 lines
1.7 KiB
TypeScript

/**
* R looks a call's name up among functions only, skipping other bindings:
* ggplot2's facet tests bind `c <- data_frame(b = 3)`, and every `c(1, 2)`
* in those files — base R's `c` — went to that data frame. A project binding
* made by a function factory (`geom_point <- make_constructor(…)`) stays.
*/
import { describe, it, expect, afterAll, beforeAll } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { CodeGraph } from '../src';
let root = '';
let cg: CodeGraph;
beforeAll(async () => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-r-call-'));
const files: Record<string, string> = {
'DESCRIPTION': 'Package: ggplot2\n',
'R/geom-point.R': `geom_point <- make_constructor(GeomPoint, position = "identity")
`,
'tests/testthat/test-facet-wrap.R': `a <- data_frame(a = 1:3)
c <- data_frame(b = 3)
test_that("facets", {
p <- geom_point()
expect_equal(c(1, 2), a$a)
})
`,
};
for (const [rel, content] of Object.entries(files)) {
fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true });
fs.writeFileSync(path.join(root, rel), content);
}
cg = await CodeGraph.init(root, { index: true });
});
afterAll(() => {
cg?.close();
if (root) fs.rmSync(root, { recursive: true, force: true });
});
describe('R calls', () => {
it('skip a data binding that shadows a base function, keep a factory-made function', () => {
const ids = cg.getNodesInFile('tests/testthat/test-facet-wrap.R').map((n) => n.id);
const targets = cg.getOutgoingEdgesFrom(ids).filter((e) => e.kind === 'calls').map((e) => cg.getNode(e.target)!.name);
expect(targets).not.toContain('c');
expect(targets).toContain('geom_point');
});
});