rust-self-owner.test.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import { afterEach, beforeEach, expect, it } from 'vitest';
  2. import * as fs from 'node:fs';
  3. import * as os from 'node:os';
  4. import * as path from 'node:path';
  5. import { CodeGraph } from '../src';
  6. let root: string;
  7. let cg: CodeGraph | undefined;
  8. beforeEach(() => { root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-rust-self-owner-')); });
  9. afterEach(() => { cg?.close(); cg = undefined; fs.rmSync(root, { recursive: true, force: true }); });
  10. async function index(files: Record<string, string>) {
  11. fs.mkdirSync(path.join(root, 'src'));
  12. fs.writeFileSync(path.join(root, 'Cargo.toml'), '[package]\nname="owners"\nversion="0.1.0"\nedition="2021"\n');
  13. for (const [file, text] of Object.entries(files)) fs.writeFileSync(path.join(root, 'src', file), text);
  14. cg = await CodeGraph.init(root, { index: true });
  15. }
  16. function targets(file: string) {
  17. const caller = cg!.getNodesByKind('method').find(n => n.filePath === `src/${file}` && n.qualifiedName === 'Target::run');
  18. expect(caller).toBeDefined();
  19. return cg!.getOutgoingEdges(caller!.id).filter(e => e.kind === 'calls').map(e => {
  20. const target = cg!.getNode(e.target)!;
  21. return `${target.filePath}:${target.qualifiedName}`;
  22. });
  23. }
  24. it('does not borrow a missing method from a same-named type in another module (#1861)', async () => {
  25. await index({
  26. 'lib.rs': 'pub mod caller; pub mod decoy;',
  27. 'caller.rs': 'pub struct Target;\nimpl Target { pub fn run(&self) { self.reset(); } }',
  28. 'decoy.rs': 'pub struct Target;\nimpl Target { pub fn reset(&self) {} }',
  29. });
  30. expect(targets('caller.rs')).toEqual([]);
  31. });
  32. it('keeps a proven local owner despite a same-named type and method in another module (#1861)', async () => {
  33. await index({
  34. 'lib.rs': 'pub mod caller; pub mod decoy;',
  35. 'caller.rs': 'pub struct Target;\nimpl Target { pub fn reset(&self) {} }\nimpl Target { pub fn run(&self) { self.reset(); } }',
  36. 'decoy.rs': 'pub struct Target;\nimpl Target { pub fn reset(&self) {} }',
  37. });
  38. expect(targets('caller.rs')).toEqual(['src/caller.rs:Target::reset']);
  39. fs.writeFileSync(path.join(root, 'src/caller.rs'), 'pub struct Target;\nimpl Target { pub fn run(&self) { self.reset(); } }');
  40. await cg!.sync();
  41. expect(targets('caller.rs')).toEqual([]);
  42. fs.writeFileSync(path.join(root, 'src/caller.rs'), 'pub struct Target;\nimpl Target { pub fn reset(&self) {} }\nimpl Target { pub fn run(&self) { self.reset(); } }');
  43. await cg!.sync();
  44. expect(targets('caller.rs')).toEqual(['src/caller.rs:Target::reset']);
  45. });
  46. it('keeps a unique owner whose impl is split across files (#1861)', async () => {
  47. await index({
  48. 'lib.rs': 'pub mod caller;\npub struct Target;\nimpl Target { pub fn reset(&self) {} }',
  49. 'caller.rs': 'use crate::Target;\nimpl Target { pub fn run(&self) { self.reset(); } }',
  50. });
  51. expect(targets('caller.rs')).toEqual(['src/lib.rs:Target::reset']);
  52. });
  53. it('declines indistinguishable inline-module owners instead of claiming one (#1861)', async () => {
  54. await index({ 'lib.rs': `mod a {
  55. pub struct Target;
  56. impl Target { pub fn reset(&self) {} }
  57. }
  58. mod b {
  59. pub struct Target;
  60. impl Target { pub fn run(&self) { self.reset(); } }
  61. }` });
  62. expect(targets('lib.rs')).toEqual([]);
  63. });