fix(resolution): extend typed-parameter receiver inference to Rust/Go/Dart/PHP (#1125) (#1130)

Completes the #1125 fix. The same typed-parameter gap fixed for TS/JS existed
in every other language whose localReceiverTypePatterns only matched
keyword-anchored locals (let/var/:=/= new) and never the bare parameter form:

- Rust: the `:`-annotation pattern required `let`, so `fn use(lg: &Logger)`
  didn't match. Dropped the `let` anchor (still covers `let lg: Logger`),
  keeping the `&?mut?` handling — now covers params and closures `|lg: T|`.
- Go: only `lg := T{}` / `var lg T` matched; a parameter/method-receiver
  `func use(lg Logger)` / `func (l Logger) M()` (name-before-type, no keyword)
  didn't. Added a PascalCase-guarded `ident Type` pattern — the guard plus the
  existing enclosing-scope bound (excludes package-level struct fields) keep
  the keyword-free shape from matching unrelated pairs.
- Dart: the type-before-name pattern's trailing `[=;]` missed a parameter's
  `)`/`,`. Widened to `[=;,)]`, mirroring Java/C#.
- PHP: only `$lg = new T` matched; a typed param `function use(Logger $lg)`
  (also `?Logger`, `\App\Logger`, `&$lg`, `catch (E $e)`) didn't. Added a
  type-before-$var pattern. Reserved words can't be class names, so the
  looser lowercase-allowing capture yields no wrong edges.

Every pattern still relies on resolveMethodOnType validating the inferred type
actually declares the method (no edge on a mis-inference) — the same safety
net the already-covered languages use. Verified with a deterministic probe:
all four now disambiguate two same-named methods via the typed param (Java +
Kotlin as passing controls), full suite green (1930), no regressions.

Adds a parameterized regression test (Rust/Go/Dart/PHP), associating method to
type by qualifiedName so it holds where the method sits outside the type's
line range (Rust impl, Go decl).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-07-02 12:17:23 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 385001398b
commit cf86fe8198
3 changed files with 64 additions and 3 deletions
+18 -2
View File
@@ -1102,12 +1102,21 @@ function localReceiverTypePatterns(language: Language, r: string): RegExp[] {
case 'rust':
return [
new RegExp(`\\blet\\s+(?:mut\\s+)?${r}\\b(?:\\s*:[^=]+)?=\\s*&?(?:mut\\s+)?([A-Z][\\w]*)`), // let lg = Logger::new()/Logger{}/Logger
new RegExp(`\\blet\\s+(?:mut\\s+)?${r}\\s*:\\s*&?(?:mut\\s+)?([A-Z][\\w]*)`), // let lg: Logger
// No `let`, so this covers a `let lg: Logger` binding AND a typed
// parameter (`fn use(lg: &Logger)`, a closure `|lg: Logger|`) — the
// parameter case the old `let`-anchored pattern excluded (#1125).
new RegExp(`\\b${r}\\s*:\\s*&?(?:mut\\s+)?([A-Z][\\w]*)`), // lg: Logger (binding or typed param)
];
case 'go':
return [
new RegExp(`\\b${r}\\b\\s*:=\\s*&?([A-Za-z_][\\w.]*)\\s*{`), // lg := Logger{} / &Logger{}
new RegExp(`\\bvar\\s+${r}\\s+\\*?([A-Za-z_][\\w.]*)`), // var lg Logger / *Logger
// A typed parameter / method receiver (`func use(lg Logger)`,
// `func (l Logger) M()`) — name-before-type with no `var`/`:=` (#1125).
// PascalCase-guarded (unlike the anchored patterns above) to keep the
// keyword-free `ident Type` shape from matching unrelated pairs; the
// enclosing-scope bound already excludes package-level struct fields.
new RegExp(`\\b${r}\\s+\\*?([A-Z][\\w.]*)`), // func use(lg Logger) / (l Logger)
];
case 'ruby':
return [
@@ -1121,11 +1130,18 @@ function localReceiverTypePatterns(language: Language, r: string): RegExp[] {
case 'dart':
return [
new RegExp(`\\b${r}\\b\\s*=\\s*([A-Z][\\w.]*)\\s*\\(`), // var lg = Logger(...)
new RegExp(`\\b([A-Z][\\w.]*)\\s+${r}\\b\\s*[=;]`), // Logger lg = ...
// Trailing `[=;,)]` (not just `[=;]`) so a typed parameter — `Logger lg)`
// / `Logger lg,` — matches too, not only `Logger lg = ...` / `Logger lg;`
// (#1125). Mirrors Java/C#.
new RegExp(`\\b([A-Z][\\w.]*)\\s+${r}\\b\\s*[=;,)]`), // Logger lg = ... / param
];
case 'php':
return [
new RegExp(`\\$?${r}\\b\\s*=\\s*new\\s+([A-Za-z_\\\\][\\w\\\\]*)`), // $lg = new Logger()
// A typed parameter (`function use(Logger $lg)`, `?Logger $lg`,
// `\\App\\Logger $lg`, `&$lg` by-ref) and a typed `catch (E $e)` — the
// type sits before the `$`-variable (#1125). Namespace `\\` allowed.
new RegExp(`\\b([A-Za-z_\\\\][\\w\\\\]*)\\s+&?\\$${r}\\b`), // Logger $lg (typed param)
];
case 'lua':
case 'luau':