Skip to content

Fix/caller import leak - #457

Open
arcticfulmar wants to merge 2 commits into
PHPantom-dev:mainfrom
arcticfulmar:fix/caller-import-leak
Open

arcticfulmar wants to merge 2 commits into
PHPantom-dev:mainfrom
arcticfulmar:fix/caller-import-leak

Conversation

@arcticfulmar

Copy link
Copy Markdown

Caller's imports no longer rename another file's types

A global function such as is_enrolled(context $context) refers to the global \context. When the calling file imported an unrelated class with the same short name (e.g. use App\context;), or declared one in its own namespace, the argument check looked context up through the caller's imports. This produces a false type_mismatch_argument.

I found this when using phpantom on a Moodle codebase where namespaced plugin code sometimes imports its own context class (e.g. local_plugin\context) and can also call the core Moodle class (\context).

Commits

  1. A parameter's class type hint now names the class that the file intends. Native hints on method and closure parameters are now resolved against their own file's imports and namespace, the same way @param tags already were. Before, a bare name could land on a global class of the same name. In laravel/framework's Console/Scheduling, Event $event was read as the global Event, which produced 48 false positives (unknown_member and type_mismatch_argument).
  2. A caller's imports no longer change what another file's types mean. The argument check now looks up parameter types, and argument types that are already fully qualified, by their full name. The caller's use statements and namespace no longer rename them. This also covers values returned from another file's functions. It depends on the first commit - on its own it would reject arguments whose native hint was still unresolved.

As a side effect, passing an App\context where the global context is expected is now reported. It was silently accepted before.

Testing

  • One new test in diagnostics_unknown_members.rs (commit 1) and six in diagnostics_type_errors.rs (commit 2).
  • The test suite, clippy (with and without semantic-export) and fmt pass on each commit.
  • analyze on examples/php and examples/laravel gives the same output as main.
  • On laravel/framework against a real Laravel 13 install, the only changes from main are the 48 false positives removed and one message now naming the correct global Stringable.
  • The completion benchmarks are within noise of main, from 9% faster to 6% slower.

Checklist

If applicable:

  • I have updated CHANGELOG.md
  • (N/A) I have updated the documentation (README.md, docs/, examples/)
  • (N/A) I have updated the config schema (config-schema.json)
  • I have added/updated tests to cover my changes
  • I fully understand the code that I am submitting (what it does,
    how it works, how it's organized), including any code drafted by an LLM.
  • For any prose generated by an LLM, I have proof-read and copy-edited with
    an eye towards deleting anything that is irrelevant, clarifying anything
    that is confusing, and adding details that are relevant. This includes,
    for example, commit descriptions, PR descriptions, and code comments.

🤖 Generated with Claude Code
🤓 Reviewed and tweaked by @arcticfulmar

arcticfulmar and others added 2 commits September 28, 2026 17:41
A native hint like `Event $event` was seeded into scope as the raw
spelling, so the class it named depended on whoever read it later: a
bare name reached a global class of that name before the file's own
namespace. In `Illuminate\Console\Scheduling` that turned every use of
`$event` into "not found on class 'Event'" and every hand-off into a
type mismatch.

Method and closure parameter hints are now qualified against the
file's imports and namespace when they are seeded, as `@param` tags
already were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The argument check looked up the callee's parameter types through the
calling file's class loader, which reads a bare name as one written in
that file. A global function typed `context $context`, called from a
file with `use App\context;` (or from a namespace declaring its own
`context`), was checked against `App\context` and rejected a valid
argument. The same applied to a value typed by another file, such as
the global `context` returned from a global function.

Parameter types and already-qualified argument types are now looked up
by their fully-qualified names, so only the caller's own unresolved
spellings go through its imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants