|
5 | 5 | * one dot segment (e.g. 'Namespace.ClassName.method'), the same-class dispatch |
6 | 6 | * must use only the segment immediately before the method name ('ClassName'), |
7 | 7 | * not the full qualified prefix ('Namespace.ClassName'). |
| 8 | + * |
| 9 | + * Also covers the static receiver confidence filter (#1398): the direct qualified |
| 10 | + * method fallback must apply computeConfidence >= 0.5 to avoid false edges from |
| 11 | + * distant files in a polyglot project. |
| 12 | + * |
| 13 | + * Also covers the bare-call JS/TS module-scope guard (#1407): bare `foo()` calls |
| 14 | + * (no receiver) inside a JS/TS class method must NOT fall through to the same-class |
| 15 | + * lookup, because bare calls in those languages are module-scoped, not class-scoped. |
8 | 16 | */ |
9 | 17 | import { describe, expect, it } from 'vitest'; |
10 | 18 | import type { CallNodeLookup } from '../../src/domain/graph/builder/call-resolver.js'; |
@@ -88,3 +96,87 @@ describe('resolveByMethodOrGlobal — same-class this-dispatch with qualified ca |
88 | 96 | expect(result).toEqual([]); |
89 | 97 | }); |
90 | 98 | }); |
| 99 | + |
| 100 | +describe('resolveByMethodOrGlobal — static receiver confidence filter (#1398)', () => { |
| 101 | + it('returns same-directory static target (confidence 0.7 >= 0.5)', () => { |
| 102 | + const target = { id: 1, file: 'app/Validators.cs', kind: 'method' }; |
| 103 | + const lookup = makeLookup({ 'Validators.IsValidEmail': [target] }); |
| 104 | + const result = resolveByMethodOrGlobal( |
| 105 | + lookup, |
| 106 | + { name: 'IsValidEmail', receiver: 'Validators' }, |
| 107 | + 'app/Program.cs', |
| 108 | + new Map(), |
| 109 | + ); |
| 110 | + expect(result).toEqual([target]); |
| 111 | + }); |
| 112 | + |
| 113 | + it('filters out distant static target (confidence 0.3 < 0.5)', () => { |
| 114 | + const target = { id: 2, file: 'lib/util/Validators.cs', kind: 'method' }; |
| 115 | + const lookup = makeLookup({ 'Validators.IsValidEmail': [target] }); |
| 116 | + const result = resolveByMethodOrGlobal( |
| 117 | + lookup, |
| 118 | + { name: 'IsValidEmail', receiver: 'Validators' }, |
| 119 | + 'app/main/Program.cs', |
| 120 | + new Map(), |
| 121 | + ); |
| 122 | + expect(result).toEqual([]); |
| 123 | + }); |
| 124 | +}); |
| 125 | + |
| 126 | +describe('resolveByMethodOrGlobal — bare-call JS/TS module-scope guard (#1407)', () => { |
| 127 | + // `flush()` inside `Processor.run` — no receiver, JS/TS file. |
| 128 | + // Must NOT resolve to `Processor.flush` (class-scoped lookup is incorrect for JS/TS). |
| 129 | + const flushMethod = { id: 10, file: 'processor.ts', kind: 'method' }; |
| 130 | + |
| 131 | + it('does NOT resolve bare call to same-class method in a .ts file', () => { |
| 132 | + const lookup = makeLookup({ 'Processor.flush': [flushMethod] }); |
| 133 | + const result = resolveByMethodOrGlobal( |
| 134 | + lookup, |
| 135 | + { name: 'flush', receiver: null }, |
| 136 | + 'processor.ts', |
| 137 | + new Map(), |
| 138 | + 'Processor.run', |
| 139 | + ); |
| 140 | + // bare call + .ts → module-scoped language → same-class fallback skipped |
| 141 | + expect(result).toEqual([]); |
| 142 | + }); |
| 143 | + |
| 144 | + it('does NOT resolve bare call to same-class method in a .js file', () => { |
| 145 | + const lookup = makeLookup({ 'Processor.flush': [flushMethod] }); |
| 146 | + const result = resolveByMethodOrGlobal( |
| 147 | + lookup, |
| 148 | + { name: 'flush', receiver: null }, |
| 149 | + 'processor.js', |
| 150 | + new Map(), |
| 151 | + 'Processor.run', |
| 152 | + ); |
| 153 | + expect(result).toEqual([]); |
| 154 | + }); |
| 155 | + |
| 156 | + it('DOES resolve this.flush() in a .ts file (receiver present — not a bare call)', () => { |
| 157 | + const lookup = makeLookup({ 'Processor.flush': [flushMethod] }); |
| 158 | + const result = resolveByMethodOrGlobal( |
| 159 | + lookup, |
| 160 | + { name: 'flush', receiver: 'this' }, |
| 161 | + 'processor.ts', |
| 162 | + new Map(), |
| 163 | + 'Processor.run', |
| 164 | + ); |
| 165 | + // this.flush() has a receiver → not a bare call → same-class fallback runs |
| 166 | + expect(result).toEqual([flushMethod]); |
| 167 | + }); |
| 168 | + |
| 169 | + it('DOES resolve bare call to same-class method in a .cs file (C# is not module-scoped)', () => { |
| 170 | + const csMethod = { id: 20, file: 'Processor.cs', kind: 'method' }; |
| 171 | + const lookup = makeLookup({ 'Processor.Flush': [csMethod] }); |
| 172 | + const result = resolveByMethodOrGlobal( |
| 173 | + lookup, |
| 174 | + { name: 'Flush', receiver: null }, |
| 175 | + 'Processor.cs', |
| 176 | + new Map(), |
| 177 | + 'Processor.Run', |
| 178 | + ); |
| 179 | + // C# is not module-scoped → same-class fallback runs → Processor.Flush found |
| 180 | + expect(result).toEqual([csMethod]); |
| 181 | + }); |
| 182 | +}); |
0 commit comments