Skip to content

fix(csharp): apply Blazor _Imports.razor usings in the razor type resolver (#3187) - #3264

Open
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/razor-imports-usings
Open

fix(csharp): apply Blazor _Imports.razor usings in the razor type resolver (#3187)#3264
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/razor-imports-usings

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

Addresses the remaining _Imports.razor half of #3187 (follow-up to #3188).

The problem

#3188 routed a Razor @inject through the C# cross-file type resolver, but only directives written in the page itself count. The Razor compiler applies _Imports.razor @using/alias directives to every Razor file in the same directory and below — and the standard Blazor template keeps the app's namespaces there — so the issue's repro still dangles on 0.9.53: with Pages/_Imports.razor carrying @using Demo.Services, @inject WidgetService _widgets in Pages/AlphaPage.razor produces a sourceless per-file stub while the canonical Demo.Services.WidgetService definition sits in the graph.

(The other still-open sub-case named in the #3188 comment — a bare @inject with no using in scope anywhere — dangles on purpose: that Razor file would not compile either, and the resolver treats it exactly like a bare C# cross-namespace reference. A control test documents that.)

The change

CsharpNameResolver now indexes each _Imports.razor's using/alias entries by the directory they govern, and lookups for razor-family files only (.razor/.cshtml) fold in every governing ancestor's entries — same-directory or above, root _Imports.razor governing the whole tree, mirroring the Razor compiler's rule. Namespace usings extend _scopes_for; aliases merge into the alias view used by resolve_type_name/resolve_qualified. .cs files never inherit them, sibling directories never leak, and the existing single-candidate soundness gate is unchanged — two same-named types both brought in scope by inherited usings still dangle rather than picking a winner.

Tests

tests/test_razor_imports_usings.py — 7 tests: same-directory and root _Imports.razor resolve the issue's @inject repro to the canonical definition (stub gone); an alias directive in _Imports.razor resolves; and four scoping controls — sibling directories don't leak, .cs files don't inherit, ambiguous inherited usings still dangle, and no-using-anywhere still dangles. With the fix reverted, the 3 positive tests fail and the 4 controls keep passing. The razor/csharp suites are unchanged (188 passed); the full suite matches a fresh same-version v8 baseline.

Copilot AI lite review requested due to automatic review settings September 1, 2026 18:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes the Blazor _Imports.razor portion of the Razor → C# cross-file type resolution work by making _Imports.razor @using and alias directives apply to .razor/.cshtml files according to Razor’s directory scoping rules, eliminating per-file sourceless stubs for @inject when the only usings are inherited.

Changes:

  • Indexes _Imports.razor namespace usings and aliases by governing directory and merges applicable ancestors for Razor-family files during resolution.
  • Ensures .cs files do not inherit _Imports.razor directives and that sibling directories do not leak.
  • Adds focused regression tests covering positive resolution cases, alias support, and scoping/ambiguity controls.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
graphify/extractors/csharp.py Extends CsharpNameResolver to inherit _Imports.razor usings/aliases for .razor/.cshtml based on directory ancestry.
tests/test_razor_imports_usings.py Adds regression tests validating _Imports.razor scoping rules and resolver behavior for Razor @inject resolution.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Extends the C# name resolver so Blazor _Imports.razor @using and alias directives apply to every Razor/.cshtml file in the same directory and below, folding those inherited usings into scope resolution (_scopes_for) and alias lookups (_aliases_for) so a bare @inject WidgetService resolves to its canonical definition instead of dangling on a sourceless stub. Directory governance is computed by _razor_imports_dir and _razor_dir_applies, with the root _Imports.razor covering the whole tree; inheritance is restricted to Razor-family files, so sibling directories and same-directory .cs files never pick up these usings, and ambiguous inherited names still refuse to guess a winner.

No blocking issues surfaced. 4 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1148 functions depend on the 55 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 522 callers, 43 callees
  • new: _rebuild_code() — 113 callers, 50 callees
  • new: dispatch_command() — 2 callers, 123 callees
  • new: run_pipeline() — 8 callers, 13 callees
  • new: watch() — 5 callers, 7 callees
  • new: _build() — 7 callers, 3 callees
  • new: _resolve_csharp_type_references() — 2 callers, 10 callees
  • new: main() — 1 callers, 6 callees
  • …and 6 more — each is listed as a finding

Verification — 1148 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 498 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_\_init\_\_.

The verifier did not have enough to check \_\_init\_\_, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 27 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly __init__ raised on the synthesized receiver args — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_resolve\_alias.

The verifier did not have enough to check \_resolve\_alias, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly __init__ raised on the synthesized receiver args — names the real obstacle, not a sampling gap)

Could not verify: Could not verify resolve\_qualified.

The verifier did not have enough to check resolve\_qualified, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `qualifier` is annotated `object` — outside the synthesizable primitive/collection set

Could not verify: Could not verify resolve\_type\_name.

The verifier did not have enough to check resolve\_type\_name, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly __init__ raised on the synthesized receiver args — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_scopes\_for.

The verifier did not have enough to check \_scopes\_for, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly __init__ raised on the synthesized receiver args — names the real obstacle, not a sampling gap)

· 14 more finding(s) on lines outside this diff (see the check run).

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