Skip to content

Add Trim/NativeAOT safety rules to code review skill#34962

Open
PureWeen wants to merge 1 commit into
mainfrom
guidance/trim-aot-code-review-rules
Open

Add Trim/NativeAOT safety rules to code review skill#34962
PureWeen wants to merge 1 commit into
mainfrom
guidance/trim-aot-code-review-rules

Conversation

@PureWeen

Copy link
Copy Markdown
Member

Summary

Add Section 23 (Trim / NativeAOT Safety) to the code review skill's review-rules.md, covering 6 checks for IL2026/IL3050 annotation chain analysis.

Motivation

During investigation of Issue #34867 (HybridWebViewHandler IL3050 warnings), we ran a controlled experiment where multiple AI models were asked to diagnose and fix the issue:

Model Without reading code With reading code
Sonnet 4 [UnconditionalSuppressMessage]
Opus 4.5 ✅ Extract method
Sonnet 4.6 ✅ Extract method
Opus 4.6 ✅ Extract method
GPT-5.4 ✅ Extract method

Models that failed shared a shallow heuristic: feature switch guards the code → warning is false positive → suppress it. They didn't reason about why [FeatureGuard] wasn't working (indirect annotation chain through [DynamicallyAccessedMembers] on generic type params).

The correct fix (PR #34958 by @simonrozsival) extracts the annotated type reference into a separate method with matching [RequiresDynamicCode]/[RequiresUnreferencedCode] attributes, converting the indirect chain into a direct call that the [FeatureGuard] can suppress.

What changed

  • review-rules.md: New Section 23 with 6 review checks:

    1. [UnconditionalSuppressMessage] for IL2026/IL3050 is almost always wrong
    2. [FeatureGuard] does NOT suppress indirect annotation chains
    3. Extract-method pattern for feature-guarded annotated types
    4. Don't reference annotated types from extracted helper attributes
    5. Read the full annotation chain before proposing any fix
    6. #pragma warning disable ILxxxx is equally wrong
  • SKILL.md: Added "Safety" category referencing the new section

Why the code review skill (not just an instruction file)

The existing trim-aot.instructions.md uses applyTo globs (**/RuntimeFeature.cs, **/*Handler*.cs, etc.) that only fire when editing matching files. This means:

  • ❌ Issue triage (commenting on issues) — never fires
  • ❌ PR review where agent reads diffs but doesn't open matching files — may not fire
  • ✅ Code review skill — always invoked explicitly, reliable

The code review skill is the right home because it's always explicitly invoked and will be integrated into the try-fix pipeline.

Add Section 23 to review-rules.md covering IL2026/IL3050 annotation
chain analysis, the extract-method pattern for feature-guarded types,
and why [UnconditionalSuppressMessage] is almost always wrong for
these warnings.

Sourced from the HybridWebViewHandler incident (Issue #34867,
PR #34958) where AI agents consistently recommended suppression
instead of the correct structural fix when they didn't read the
source code first.

Co-authored-by: Copilot <[email protected]>
Copilot AI review requested due to automatic review settings April 14, 2026 18:42
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34962

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34962"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 updates the .github/skills/code-review skill documentation to add explicit Trim/NativeAOT safety guidance (focused on IL2026/IL3050 annotation-chain analysis) motivated by the HybridWebViewHandler IL3050 incident.

Changes:

  • Added Section 23 (“Trim / NativeAOT Safety”) to review-rules.md with six concrete review checks to discourage suppression-based “fixes” and encourage correct structural patterns.
  • Updated SKILL.md to reflect the expanded rule set and added a new “Safety” category callout.

Reviewed changes

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

File Description
.github/skills/code-review/references/review-rules.md Adds a new Section 23 with reviewer heuristics for Trim/NativeAOT (IL2026/IL3050) issues and recommended fix patterns.
.github/skills/code-review/SKILL.md Updates the skill metadata/description and lists “Safety” as a review category referencing the new section.

|-------|-----------------|
| **`[UnconditionalSuppressMessage]` for IL2026/IL3050 is almost always wrong** | If a PR adds `[UnconditionalSuppressMessage("AOT", "IL3050:...")]` or `[UnconditionalSuppressMessage("Trimming", "IL2026:...")]`, it is very likely hiding a real problem rather than fixing it. The correct response is to restructure the code so the analyzer can prove safety — typically via the extract-method pattern below. Only accept suppression if the PR includes proof that the code path is genuinely unconditionally safe (e.g., the type is explicitly preserved). (Issue #34867 — simonrozsival) |
| **`[FeatureGuard]` does NOT suppress indirect annotation chains** | `[FeatureGuard(typeof(RequiresDynamicCodeAttribute))]` on a `RuntimeFeature` property suppresses IL3050 only for **direct** calls to `[RequiresDynamicCode]` methods inside the guarded block. It does **not** suppress warnings from indirect chains — e.g., generic type parameters with `[DynamicallyAccessedMembers]` that trace back to an annotated type. If a PR claims "the feature guard handles it" but the warning goes through a generic like `AddHandler<T, TRender>()`, the guard is NOT sufficient. (Issue #34867 — simonrozsival) |
| **Extract-method pattern for feature-guarded annotated types** | When registering a type annotated with `[RequiresDynamicCode]` or `[RequiresUnreferencedCode]` inside a `[FeatureGuard]`-protected block, and the registration goes through a generic API with `[DynamicallyAccessedMembers]`, extract the call into a separate method annotated with the same attributes. This converts the indirect chain into a direct call that the `[FeatureGuard]` can suppress. Reference: `AppHostBuilderExtensions.AddHybridWebViewHandler()` in `src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs`. (PR #34958 — simonrozsival) |

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

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

The reference to AppHostBuilderExtensions.AddHybridWebViewHandler() appears to be stale/incorrect: there is no AddHybridWebViewHandler method in src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs (current code registers HybridWebView inside AddControlsHandlers / SetupDefaults). Please update this reference to the actual helper/method name used in the repo (or link to a stable permalink to the relevant PR/commit) so reviewers can find the example quickly.

Suggested change
| **Extract-method pattern for feature-guarded annotated types** | When registering a type annotated with `[RequiresDynamicCode]` or `[RequiresUnreferencedCode]` inside a `[FeatureGuard]`-protected block, and the registration goes through a generic API with `[DynamicallyAccessedMembers]`, extract the call into a separate method annotated with the same attributes. This converts the indirect chain into a direct call that the `[FeatureGuard]` can suppress. Reference: `AppHostBuilderExtensions.AddHybridWebViewHandler()` in `src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs`. (PR #34958 — simonrozsival) |
| **Extract-method pattern for feature-guarded annotated types** | When registering a type annotated with `[RequiresDynamicCode]` or `[RequiresUnreferencedCode]` inside a `[FeatureGuard]`-protected block, and the registration goes through a generic API with `[DynamicallyAccessedMembers]`, extract the call into a separate method annotated with the same attributes. This converts the indirect chain into a direct call that the `[FeatureGuard]` can suppress. Reference: `HybridWebView` registration in `AddControlsHandlers` / `SetupDefaults` in `src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs`. (PR #34958 — simonrozsival) |

Copilot uses AI. Check for mistakes.
description: >-
Deep code review of PR changes for correctness, safety, and MAUI conventions.
Uses independence-first assessment (code before narrative) with 345 lines of
Uses independence-first assessment (code before narrative) with 364 lines of

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

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

The SKILL description says the rules file has "364 lines", but references/review-rules.md currently has 365 lines. Consider either updating the number or (more robust) removing the hard-coded line count so it doesn't drift each time rules are edited.

Suggested change
Uses independence-first assessment (code before narrative) with 364 lines of
Uses independence-first assessment (code before narrative) with

Copilot uses AI. Check for mistakes.
@kubaflo

kubaflo commented May 24, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/refactor-copilot-yml

@MauiBot

This comment has been minimized.

@kubaflo

kubaflo commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer -p android

@MauiBot

MauiBot commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Multi-model code review — Request changes

Three models reviewed this PR independently (Claude Opus 4.8, GPT-5.5, Gemini 3.1 Pro), then cross-pollinated.

Consensus: ❌ Request changes — valuable section, but two technical inaccuracies in a skill that shapes future reviews.

The core guidance is sound ✅

The headline technical claim — that [FeatureGuard(typeof(RequiresDynamicCodeAttribute))] suppresses IL3050 only for direct [RequiresDynamicCode] calls, not for indirect chains (generic params with [DynamicallyAccessedMembers] that trace back to an annotated type) — is correct (all three models independently verified it against how the trimmer/ILC propagates annotations). The "suppression is almost always the wrong fix" framing and the #pragma warning disable ILxxxx equivalence are also fair. The line-count bump (364) and markdown are fine.

Two accuracy fixes before merge

  1. Branch-mismatched code reference (line 344) — the extract-method example cites AppHostBuilderExtensions.AddHybridWebViewHandler(), but that method exists only on net11.0 (where the fix PR #34958 merged), not on this PR's main base — here the registration is still inline (AddHandler<HybridWebView, HybridWebViewHandler>() inside the RuntimeFeature.IsHybridWebViewSupported guard, ~lines 130-133). A rule added to main that points at code absent from main will confuse reviewers — update it to match main (or note it's net11.0-only / pending #34958).
  2. Incorrect IL claim (line 345) — "accessing a const on the type embeds the type reference in the caller's IL metadata" is false: a const string is folded to an ldstr literal at compile time with no TypeRef/MemberRef to the declaring type (Opus also noted the originating commit 2 was a cosmetic "address review" edit, not a trimmer fix). The "use a local const instead" advice is harmless but its stated reason is a misconception (it would be true for a static readonly field, not a const).

Since this is the code-review skill itself, getting the rules technically exact matters — otherwise reviewers will cite phantom code or flag non-issues.

Independent verdicts: GPT-5.5 — NEEDS_CHANGES (high) · Gemini 3.1 Pro — NEEDS_CHANGES · Opus 4.8 — NEEDS_DISCUSSION (high). Consensus: keep the section (the FeatureGuard insight is genuinely useful and was empirically verified), fix the two inaccuracies.

|-------|-----------------|
| **`[UnconditionalSuppressMessage]` for IL2026/IL3050 is almost always wrong** | If a PR adds `[UnconditionalSuppressMessage("AOT", "IL3050:...")]` or `[UnconditionalSuppressMessage("Trimming", "IL2026:...")]`, it is very likely hiding a real problem rather than fixing it. The correct response is to restructure the code so the analyzer can prove safety — typically via the extract-method pattern below. Only accept suppression if the PR includes proof that the code path is genuinely unconditionally safe (e.g., the type is explicitly preserved). (Issue #34867 — simonrozsival) |
| **`[FeatureGuard]` does NOT suppress indirect annotation chains** | `[FeatureGuard(typeof(RequiresDynamicCodeAttribute))]` on a `RuntimeFeature` property suppresses IL3050 only for **direct** calls to `[RequiresDynamicCode]` methods inside the guarded block. It does **not** suppress warnings from indirect chains — e.g., generic type parameters with `[DynamicallyAccessedMembers]` that trace back to an annotated type. If a PR claims "the feature guard handles it" but the warning goes through a generic like `AddHandler<T, TRender>()`, the guard is NOT sufficient. (Issue #34867 — simonrozsival) |
| **Extract-method pattern for feature-guarded annotated types** | When registering a type annotated with `[RequiresDynamicCode]` or `[RequiresUnreferencedCode]` inside a `[FeatureGuard]`-protected block, and the registration goes through a generic API with `[DynamicallyAccessedMembers]`, extract the call into a separate method annotated with the same attributes. This converts the indirect chain into a direct call that the `[FeatureGuard]` can suppress. Reference: `AppHostBuilderExtensions.AddHybridWebViewHandler()` in `src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs`. (PR #34958 — simonrozsival) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ This row cites AppHostBuilderExtensions.AddHybridWebViewHandler() as THE canonical extract-method example, but that method isn't on this PR's main basegrep -rn AddHybridWebViewHandler src/ finds nothing here, and the actual HybridWebView registration in that file (lines ~130-133) is still inline: if (RuntimeFeature.IsHybridWebViewSupported) { handlersCollection.AddHandler<HybridWebView, HybridWebViewHandler>(); }. The extracted method exists only on net11.0, where the fix PR #34958 merged. So this rule (added against main) points reviewers at code that isn't present on the branch it lives on. Please update the reference to match main (or explicitly note it's net11.0-only / pending #34958 flowing to main), so reviewers on main aren't sent to absent code.

| **`[UnconditionalSuppressMessage]` for IL2026/IL3050 is almost always wrong** | If a PR adds `[UnconditionalSuppressMessage("AOT", "IL3050:...")]` or `[UnconditionalSuppressMessage("Trimming", "IL2026:...")]`, it is very likely hiding a real problem rather than fixing it. The correct response is to restructure the code so the analyzer can prove safety — typically via the extract-method pattern below. Only accept suppression if the PR includes proof that the code path is genuinely unconditionally safe (e.g., the type is explicitly preserved). (Issue #34867 — simonrozsival) |
| **`[FeatureGuard]` does NOT suppress indirect annotation chains** | `[FeatureGuard(typeof(RequiresDynamicCodeAttribute))]` on a `RuntimeFeature` property suppresses IL3050 only for **direct** calls to `[RequiresDynamicCode]` methods inside the guarded block. It does **not** suppress warnings from indirect chains — e.g., generic type parameters with `[DynamicallyAccessedMembers]` that trace back to an annotated type. If a PR claims "the feature guard handles it" but the warning goes through a generic like `AddHandler<T, TRender>()`, the guard is NOT sufficient. (Issue #34867 — simonrozsival) |
| **Extract-method pattern for feature-guarded annotated types** | When registering a type annotated with `[RequiresDynamicCode]` or `[RequiresUnreferencedCode]` inside a `[FeatureGuard]`-protected block, and the registration goes through a generic API with `[DynamicallyAccessedMembers]`, extract the call into a separate method annotated with the same attributes. This converts the indirect chain into a direct call that the `[FeatureGuard]` can suppress. Reference: `AppHostBuilderExtensions.AddHybridWebViewHandler()` in `src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs`. (PR #34958 — simonrozsival) |
| **Don't reference annotated types from the extracted helper's attributes** | When creating the extracted helper method, use a local `const string` for the annotation message instead of referencing a const on the annotated type (e.g., `HybridWebViewHandler.DynamicFeatures`). Accessing a const on the type embeds the type reference in the caller's IL metadata, re-introducing the very problem the extraction was meant to solve. (PR #34958, commit 2 — simonrozsival) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ "Accessing a const on the type embeds the type reference in the caller's IL metadata" is not correct. A const (compile-time constant) like HybridWebViewHandler.DynamicFeatures is folded to its literal value at compile time — the C# compiler emits an ldstr of the string and does not emit a TypeRef/MemberRef to the declaring type. So referencing another type's const string (even in an attribute argument) does not re-introduce a type reference, and the stated rationale for "use a local const string instead" is a misconception. (Using a local const is harmless, but teaching reviewers this false IL claim will cause them to flag non-issues.) If the intent was about a static readonly field — which would emit a member/type reference — please say that instead, since the const case behaves differently.

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

Labels

area-ai-agents Copilot CLI agents, agent skills, AI-assisted development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants