mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-21 08:47:04 +08:00
c9944147bb
VB.NET's resolver guessed a receiver call's target from the receiver's name whenever no class matched it, without any of the guards C# has. Indexing Structure members (#2345) gave those guesses new targets: 23 SCrawler `xxxFile.Delete()` calls on PersonalUtilities' SFile went to a nested struct's `TempFileConversion::Delete`, and staxrip's main app sent 90 calls to its AutoCrop tool's duplicate `ColorHSL`. - Declared types (new src/resolution/vbnet-receivers.ts): a receiver's `As` type decides the call (local, parameter, field, property, `For Each`, `As New`, ...). The candidates are that type's member, one it inherits, or an extension method written for it. A type from outside the project gets no link. - .NET standard method names (`Add`, `Contains`, `Clear`, `Dispose`, `ToString`, ...) on an untyped receiver need a receiver named after the owner, as in C#. VB.NET matches them case-insensitively. - A bare call, or one on `Me`, reaches the enclosing class, what it inherits, or a Module. A nested type is matched by its bare name only from inside its owner. Types resolve through enclosing namespaces, `Imports` (aliases included) and the caller's project. A tie between equally good guesses goes to the caller's file, then its project, then the nearer directory, or gets no link. - vbReceiverOf reads the name at or after the reference's column, so `Me.Size = New System.Drawing.Size(...)` is read through `System.Drawing`, not `Me`. Before/after on main (nodes unchanged): SCrawler 15,092 -> 14,893 edges, staxrip 28,002 -> 26,647. Of the non-containment edges removed, 855 / 2,802 are the same link with new resolver metadata. The rest: - .NET names on untyped receivers no longer guessed: 196 / 1,378. 731 of staxrip's are StringBuilder `sb.Append` calls that had gone to `LogBuilder::Append`. - Other receiver guesses dropped: 86 / 103, including the SFile.Delete calls. - Bare calls out of scope: 38 / 834. 590 of staxrip's are `New Point(...)` that had gone to a class nested in `ButtonEx`; others are WinForms `Refresh()`, `Focus()` and `Activate()`. - Retargeted: 227 / 532, e.g. 208 bare `Add(...)` calls in encoder classes now reach the inherited `CommandLineParams::Add`. 121 / 958 call sites resolve that did not before, e.g. `cms.Add` -> ContextMenuStripEx and `td.AddButton` -> TaskDialog. Designer -> Size links fall from 5 / 1 to 0; designer -> Add stays 0. Known trade-off, shared with C#'s guard: about 31 staxrip calls like `switches.Join(BR)` that may be the project's own `MiscExtensions` extension methods are no longer linked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.2 KiB
TypeScript
101 lines
3.2 KiB
TypeScript
/**
|
||
* A VB.NET call with no receiver — or with `Me` / `MyClass` / `MyBase`,
|
||
* which the extractor drops — is a member of the type it is written in, of a
|
||
* type around it, or of one they inherit; a `Module`'s members are reached
|
||
* from anywhere. Never another class's same-named member, however near its
|
||
* file: SCrawler's `{ToString()}` in the Instagram `UserData` (which
|
||
* `Inherits UserDataBase`) went to a nearby structure's `ToString`.
|
||
*/
|
||
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-vb-bare-'));
|
||
const files: Record<string, string> = {
|
||
'API/Base/UserDataBase.vb': `Namespace API.Base
|
||
Friend MustInherit Class UserDataBase
|
||
Public Overrides Function ToString() As String
|
||
Return "user"
|
||
End Function
|
||
Protected Sub Refresh()
|
||
End Sub
|
||
End Class
|
||
End Namespace
|
||
`,
|
||
'API/Instagram/MediaItem.vb': `Namespace API.Instagram
|
||
Friend Class MediaItem
|
||
Public Overrides Function ToString() As String
|
||
Return "media"
|
||
End Function
|
||
Friend Sub Refresh()
|
||
End Sub
|
||
End Class
|
||
End Namespace
|
||
`,
|
||
'API/Instagram/UserData.vb': `Namespace API.Instagram
|
||
Friend Class UserData : Inherits API.Base.UserDataBase
|
||
Friend Sub SetTagsLimit()
|
||
Dim aStr$ = $"Enter the number of posts from user {ToString()}"
|
||
Me.Refresh()
|
||
Log(aStr)
|
||
End Sub
|
||
Private Class Counter
|
||
Friend Sub Tick()
|
||
Report()
|
||
End Sub
|
||
End Class
|
||
Private Shared Sub Report()
|
||
End Sub
|
||
End Class
|
||
End Namespace
|
||
`,
|
||
'Tools/Logger.vb': `Public Module Logger
|
||
Public Sub Log(ByVal Text As String)
|
||
End Sub
|
||
End Module
|
||
`,
|
||
};
|
||
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 });
|
||
});
|
||
|
||
/** `Owner::member` of every call edge out of one method. */
|
||
function callsOf(file: string, qualifiedName: string): string[] {
|
||
const from = cg.getNodesInFile(file).find((n) => n.qualifiedName === qualifiedName)!;
|
||
return cg
|
||
.getOutgoingEdgesFrom([from.id])
|
||
.filter((e) => e.kind === 'calls')
|
||
.map((e) => cg.getNode(e.target)!.qualifiedName)
|
||
.sort();
|
||
}
|
||
|
||
describe('VB.NET receiver-less calls', () => {
|
||
it('reach the members the class inherits, not a nearer class’s, and a module’s', () => {
|
||
expect(callsOf('API/Instagram/UserData.vb', 'API.Instagram::UserData::SetTagsLimit')).toEqual([
|
||
'API.Base::UserDataBase::Refresh',
|
||
'API.Base::UserDataBase::ToString',
|
||
'Logger::Log',
|
||
]);
|
||
});
|
||
|
||
it('reach a member of the class around a nested one', () => {
|
||
expect(callsOf('API/Instagram/UserData.vb', 'API.Instagram::UserData::Counter::Tick')).toEqual([
|
||
'API.Instagram::UserData::Report',
|
||
]);
|
||
});
|
||
});
|