feat(LANG-rust): add implicit crate path fallback, 35 scope tests

- Import resolver now falls back to crate-relative for unqualified
  module paths (Rust 2015 edition compat)
- Added 7 more test cases: re-exports, shadowing, closures, default
  trait methods (35 total, exceeding ≥30 requirement)

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/8f14b730-79d4-4356-9505-325750d71f84

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-05-16 10:59:32 +00:00 committed by GitHub
parent d2ff873c38
commit 97dc5b791a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 206 additions and 1 deletions

View file

@ -42,7 +42,12 @@ export function resolveRustImportTarget(
}
// External crate — try workspace-level resolution
return resolveWorkspaceCrate(segments, allFilePaths);
const workspaceResult = resolveWorkspaceCrate(segments, allFilePaths);
if (workspaceResult !== null) return workspaceResult;
// Fallback: treat as implicit crate-relative (Rust 2015 edition or
// when the first segment matches a sibling module name).
return resolveModulePath(segments, findSrcRoot(fromNormalized), allFilePaths);
}
function findSrcRoot(filePath: string): string {

View file

@ -864,3 +864,203 @@ impl User {
expect(saveCall).toBeDefined();
});
});
// ---------------------------------------------------------------------------
// 16. Re-export chain: pub use re-exports
// ---------------------------------------------------------------------------
describe('Rust scope: pub use re-exports', () => {
let result: PipelineResult;
let tmpDir: string;
beforeAll(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rust-scope-reexport-'));
writeFixtureRepo(tmpDir, {
'src/main.rs': `
mod models;
use crate::models::User;
fn process() {
let u = User { name: String::new() };
u.save();
}
fn main() {}
`,
'src/models.rs': `
mod user;
pub use self::user::User;
`,
'src/models/user.rs': `
pub struct User {
pub name: String,
}
impl User {
pub fn save(&self) {}
}
`,
});
result = await runPipelineFromRepo(tmpDir, () => {});
}, 60000);
afterAll(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it('emits IMPORTS edges through re-export chain', () => {
const imports = getRelationships(result, 'IMPORTS');
expect(imports.length).toBeGreaterThanOrEqual(1);
});
});
// ---------------------------------------------------------------------------
// 17. Local shadow: inner variable shadows outer
// ---------------------------------------------------------------------------
describe('Rust scope: local variable shadowing', () => {
let result: PipelineResult;
let tmpDir: string;
beforeAll(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rust-scope-shadow-'));
writeFixtureRepo(tmpDir, {
'src/main.rs': `
mod user;
use crate::user::User;
fn process() {
let x = 42;
let x = User { name: String::new() };
x.save();
}
fn main() {}
`,
'src/user.rs': `
pub struct User {
pub name: String,
}
impl User {
pub fn save(&self) {}
}
`,
});
result = await runPipelineFromRepo(tmpDir, () => {});
}, 60000);
afterAll(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it('resolves x.save() to User#save after shadow rebind', () => {
const calls = getRelationships(result, 'CALLS');
const saveCall = calls.find(
(c) => c.target === 'save' && c.source === 'process',
);
expect(saveCall).toBeDefined();
});
});
// ---------------------------------------------------------------------------
// 18. Closure / nested function scope
// ---------------------------------------------------------------------------
describe('Rust scope: closure scope isolation', () => {
let result: PipelineResult;
let tmpDir: string;
beforeAll(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rust-scope-closure-'));
writeFixtureRepo(tmpDir, {
'src/main.rs': `
fn adder(x: i32) -> i32 {
let f = |y: i32| -> i32 { x + y };
f(10)
}
fn main() {
adder(5);
}
`,
});
result = await runPipelineFromRepo(tmpDir, () => {});
}, 60000);
afterAll(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it('detects adder function', () => {
const fns = getNodesByLabel(result, 'Function');
expect(fns).toContain('adder');
});
it('resolves main → adder() call', () => {
const calls = getRelationships(result, 'CALLS');
const call = calls.find(
(c) => c.target === 'adder' && c.source === 'main',
);
expect(call).toBeDefined();
});
});
// ---------------------------------------------------------------------------
// 19. Trait default method
// ---------------------------------------------------------------------------
describe('Rust scope: trait default methods', () => {
let result: PipelineResult;
let tmpDir: string;
beforeAll(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rust-scope-default-method-'));
writeFixtureRepo(tmpDir, {
'src/main.rs': `
trait Greeter {
fn name(&self) -> String;
fn greet(&self) -> String {
format!("Hello, {}!", self.name())
}
}
struct User {
username: String,
}
impl Greeter for User {
fn name(&self) -> String {
self.username.clone()
}
}
fn main() {
let u = User { username: String::from("alice") };
u.greet();
}
`,
});
result = await runPipelineFromRepo(tmpDir, () => {});
}, 60000);
afterAll(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it('detects Greeter trait', () => {
expect(getNodesByLabel(result, 'Trait')).toContain('Greeter');
});
it('detects User struct', () => {
expect(getNodesByLabel(result, 'Struct')).toContain('User');
});
it('emits IMPLEMENTS edge from User to Greeter', () => {
const impls = getRelationships(result, 'IMPLEMENTS');
const edge = impls.find(
(e) => e.source === 'User' && e.target === 'Greeter',
);
expect(edge).toBeDefined();
});
});