Add unsafe evolution migration code fixers - #130611

Closed
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3
Closed

Add unsafe evolution migration code fixers#130611
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Spec: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md

What it does:

  • Adds IL5005 to remove legacy or invalid unsafe modifiers.
    • Type declarations such as classes, structs, interfaces, records, record structs, and enums.
    • Delegate declarations, static constructors, and destructors.
    • Methods, instance constructors, operators, conversion operators, local functions, properties, indexers, and events when:
      • no pointer or function pointer in the signature (we keep those caller-unsafe)
      • no <safety> comment. It's a sign that this unsafe was from unsafe-v1.
    • Keeps unsafe when required by interop, partial members, overrides, or interface contracts.
  • Adds IL5006 to add required unsafe blocks, expressions, and contract modifiers.
  • Adds unsafe and <safety> placeholders to unannotated interop declarations.
  • Covers partial members, interfaces, accessors, stackalloc, async code, top-level statements, and scoped refs.

How to run it from the repo root:

C:\prj\dotnet-latest\dotnet.cmd build .\src\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj -c Debug --no-restore
$env:EnableUnsafeMigration='true'
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5005 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd build .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0-c Debug --no-restore -p:BuildProjectReferences=false -p:EnableUnsafeMigration=true

I was using the latest daily SDK in C:\prj\dotnet-latest, not sure the bootstrapped one contains all the new C# features.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:23
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 13, 2026 09:23
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 13, 2026

CopilotAI 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 adds a DEBUG-only “unsafe evolution” migration analyzer + code fixers to ILLink, introducing two new IL* diagnostics (IL5005/IL5006) to (1) remove legacy/invalid unsafe modifiers and (2) migrate unsafe usages by adding explicit unsafe contexts and safety placeholders. It also wires up additional MSBuild properties and live-ILLink behavior to enable the migration experience, and adds/extends Roslyn analyzer/codefix tests.

Changes:

  • Add UnsafeMigrationAnalyzer + UnsafeMigrationAnalysis to detect modifier removals, required declaration updates, and unsafe-operation sites (behind #if DEBUG).
  • Add two new code fix providers (modifier removal + usage migration) and supporting document/AST rewrite helpers (behind #if DEBUG).
  • Add new diagnostic strings/IDs and update build/targets/tests to enable and validate the migration workflow.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csAdds test helpers and extensive test coverage for IL5005/IL5006 codefix behavior.
src/tools/illink/src/ILLink.Shared/SharedStrings.resxAdds user-facing title/message strings for the new unsafe migration diagnostics.
src/tools/illink/src/ILLink.Shared/DiagnosticId.csAdds DEBUG-only diagnostic IDs and introduces an “Unsafe” category range mapping.
src/tools/illink/src/ILLink.Shared/DiagnosticCategory.csAdds DEBUG-only Unsafe category constant.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalyzer.csNew analyzer that reports file-level migration diagnostics when migration is enabled.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.csNew analysis engine for modifier removals, contract/declaration updates, and unsafe operation locations.
src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.csAdds DEBUG-only MSBuild property names used to toggle unsafe migration behavior.
src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.propsExposes additional compiler-visible MSBuild properties used by the migration flow.
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.csNew code fix provider that migrates unsafe usage sites (wrap blocks/expressions, etc.).
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.csNew document fixer implementing the migration transformations and heuristics.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierMigrationCodeFixProvider.csNew code fix provider that removes legacy unsafe modifiers when eligible.
src/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.csNew shared helpers for adding unsafe contexts and safety documentation.
src/tools/illink/src/ILLink.CodeFix/Resources.resxAdds code fix titles for the new migration fixers.
eng/liveILLink.targetsUpdates live-ILLink wiring to support enabling unsafe migration and design-time analyzer referencing.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:36

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:00

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.cs Outdated
Comment threadeng/liveILLink.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:38

CopilotAI 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

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

CopilotAI review requested due to automatic review settings July 13, 2026 18:12

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
EgorBoand others added 2 commits July 16, 2026 13:39
- Parse unsafe(...) with explicit preview + updated-memory-safety-rules options
- Use SyntaxFactory elastic newline trivia instead of hardcoded CRLF literals
- Remove redundant single-arm switch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 13:08

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
The IL5006 document fixer could emit code that fails to compile:
- unsafe(...) expressions at statement/void/assignment-target positions (only valid as a value sub-expression) now fall back to unsafe { } blocks
- out-var/pattern-var declarations (Foo(out int y);) are forward-declared before the unsafe block so they stay in scope
- out-vars in a local declaration initializer (int x = Foo(out int y);) fall back to the unsafe(...) expression form, keeping the variable in scope
- only interior (not leading/trailing) directives block wrapping a statement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 15:48

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
- Split multi-variable event field declarations with mixed safe/unsafe contracts so 'unsafe' is applied only to the variables that require it (avoids CS9365 where a safe interface event would be made illegally unsafe)
- Drop the statement-wrap branch that ignored requiresExpression, so using/await/directive statements that need an unsafe(...) expression fall back to whole-body wrapping instead of an unsafe { } block that would change scope
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:11

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
When splitting a multi-variable event field, keep each variable node's trivia (e.g. inline comments like 'event Action E1 /* keep */, E2;') instead of stripping it; only the outer declaration trivia is normalized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:31

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee40608f-4e94-47e1-8763-843d916bab40
CopilotAI review requested due to automatic review settings July 17, 2026 13:03

CopilotAI 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

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

@github-actions

github-actionsBot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
"version": 5,
"last_dispatched_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_reviewed_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_recorded_worker_run_id": "29686415576",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"review_id": 4730737351
}
]
}

@github-actionsgithub-actionsBot 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.

Holistic Review

Motivation: The change implements automated migration tooling (analyzer IL5005/IL5006 plus code-fix providers) for the unsafe-evolution C# language proposal, letting the runtime repo mechanically strip legacy/invalid unsafe modifiers and add required unsafe blocks, expressions, and <safety> documentation. This is a real, well-motivated need: migrating a large codebase to the new memory-safety rules by hand would be impractical and error-prone.

Approach: The approach is sound and appropriately scoped. All new code is wrapped in #if DEBUG and gated behind the opt-in EnableUnsafeMigration MSBuild property, so it adds no shipping public API surface and cannot affect production builds or normal analyzer runs. It reuses the existing ILLink analyzer/code-fix infrastructure (diagnostic IDs, categories, resx strings, CSharpCodeFixVerifier) rather than inventing parallel machinery, and the IL5005/5006 IDs and Unsafe category are correctly slotted into DiagnosticId/DiagnosticCategory without disturbing the existing AOT range mapping. The logic handles a broad matrix of C# constructs (partial members, interfaces, accessors, stackalloc, async, top-level statements, scoped refs), which the extensive test file exercises.

Summary: ⚠️ Needs Human Review. The code is well-structured, self-contained, and thoroughly tested for a migration tool, and it carries essentially zero production risk because it is DEBUG-only and opt-in. I have no blocking findings. However, this is a large (2,661-line) body of intricate syntax/semantic-model manipulation whose correctness is best judged by the ILLink area owners against the actual proposal semantics and by running it over real libraries — a level of validation this static review cannot fully substitute for. Human reviewers should focus on the correctness of the modifier-removal/retention heuristics (interop, overrides, interface contracts, pointer-in-signature detection) and the <safety>/skiplocalsinit handling, and confirm the tool's output on representative source is what the proposal intends.


Detailed Findings

✅ Scope & Risk — Correctly isolated tooling change

Every new type and every touched enum/category/resx addition is under #if DEBUG and further gated by the EnableUnsafeMigration compiler-visible property (verified in Microsoft.NET.ILLink.Analyzers.props, MSBuildPropertyOptionNames.cs, and both code-fix providers' RegisterCodeFixesAsync). No ref/ assembly or shipping public API is introduced, so the API-approval gate does not apply. Production analyzer behavior is unchanged.

✅ Codebase Fit — Consistent with existing ILLink patterns

Diagnostic IDs (5005/5006), the new Unsafe category, and the resx entries follow existing conventions. The GetDiagnosticCategory switch inserts the two new IDs before the >= 3050 and <= 6000 => AOT arm so they resolve to Unsafe rather than being swallowed by the AOT range — this ordering is correct and covered by the UnsafeMigrationDiagnostics_HaveUnsafeCategoryWithoutHelpLinks test.

✅ Test Quality — Broad behavioral coverage

The new RequiresUnsafeCodeFixTests.cs adds ~44 focused test methods covering modifier removal/retention, whole-body vs. statement vs. expression wrapping, forward declarations, ref locals, async/yield exclusions, and the migration-disabled case. Using CSharpCodeFixVerifier with before/after sources is the right harness for code-fix validation. Note: I could not build or run these tests in this environment; the human reviewer should confirm they pass in CI.

💡 Brittle syntax-node detection (inline)

One low-severity, non-blocking maintainability note is left inline in UnsafeMigrationAnalysis.cs regarding the GetType().Name == "UnsafeExpressionSyntax" string check used to recognize the preview unsafe(...) expression node.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 192.5 AIC · ⌖ 12.1 AIC · ⊞ 10K

private static bool IsInUnsafeContext(SyntaxNode node)
=> node.AncestorsAndSelf().Any(static ancestor =>
ancestor is UnsafeStatementSyntax ||
ancestor.GetType().Name == "UnsafeExpressionSyntax" ||

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 uses a stringly-typed check (ancestor.GetType().Name == "UnsafeExpressionSyntax") to detect the preview unsafe(...) expression node, presumably because the strongly-typed syntax type isn't available in the referenced Roslyn version. That's a reasonable pragmatic workaround, but it is brittle: it silently breaks if the node type is renamed, and it won't match derived/wrapped forms. Consider adding a short comment explaining why the reflection-by-name check is used and, if feasible, guarding it with a SyntaxKind check so the intent survives a Roslyn bump. Non-blocking given this is DEBUG-only tooling.

@EgorBoEgorBo closed this Jul 20, 2026
@EgorBo

Copy link
Copy Markdown
MemberAuthor

decided to split into many small analyzers/fixers and the first batch is in #131002

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

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@EgorBo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add unsafe evolution migration code fixers - #130611

Closed
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3
Closed

Add unsafe evolution migration code fixers#130611
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Spec: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md

What it does:

  • Adds IL5005 to remove legacy or invalid unsafe modifiers.
    • Type declarations such as classes, structs, interfaces, records, record structs, and enums.
    • Delegate declarations, static constructors, and destructors.
    • Methods, instance constructors, operators, conversion operators, local functions, properties, indexers, and events when:
      • no pointer or function pointer in the signature (we keep those caller-unsafe)
      • no <safety> comment. It's a sign that this unsafe was from unsafe-v1.
    • Keeps unsafe when required by interop, partial members, overrides, or interface contracts.
  • Adds IL5006 to add required unsafe blocks, expressions, and contract modifiers.
  • Adds unsafe and <safety> placeholders to unannotated interop declarations.
  • Covers partial members, interfaces, accessors, stackalloc, async code, top-level statements, and scoped refs.

How to run it from the repo root:

C:\prj\dotnet-latest\dotnet.cmd build .\src\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj -c Debug --no-restore
$env:EnableUnsafeMigration='true'
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5005 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd build .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0-c Debug --no-restore -p:BuildProjectReferences=false -p:EnableUnsafeMigration=true

I was using the latest daily SDK in C:\prj\dotnet-latest, not sure the bootstrapped one contains all the new C# features.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:23
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 13, 2026 09:23
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 13, 2026

CopilotAI 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 adds a DEBUG-only “unsafe evolution” migration analyzer + code fixers to ILLink, introducing two new IL* diagnostics (IL5005/IL5006) to (1) remove legacy/invalid unsafe modifiers and (2) migrate unsafe usages by adding explicit unsafe contexts and safety placeholders. It also wires up additional MSBuild properties and live-ILLink behavior to enable the migration experience, and adds/extends Roslyn analyzer/codefix tests.

Changes:

  • Add UnsafeMigrationAnalyzer + UnsafeMigrationAnalysis to detect modifier removals, required declaration updates, and unsafe-operation sites (behind #if DEBUG).
  • Add two new code fix providers (modifier removal + usage migration) and supporting document/AST rewrite helpers (behind #if DEBUG).
  • Add new diagnostic strings/IDs and update build/targets/tests to enable and validate the migration workflow.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csAdds test helpers and extensive test coverage for IL5005/IL5006 codefix behavior.
src/tools/illink/src/ILLink.Shared/SharedStrings.resxAdds user-facing title/message strings for the new unsafe migration diagnostics.
src/tools/illink/src/ILLink.Shared/DiagnosticId.csAdds DEBUG-only diagnostic IDs and introduces an “Unsafe” category range mapping.
src/tools/illink/src/ILLink.Shared/DiagnosticCategory.csAdds DEBUG-only Unsafe category constant.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalyzer.csNew analyzer that reports file-level migration diagnostics when migration is enabled.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.csNew analysis engine for modifier removals, contract/declaration updates, and unsafe operation locations.
src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.csAdds DEBUG-only MSBuild property names used to toggle unsafe migration behavior.
src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.propsExposes additional compiler-visible MSBuild properties used by the migration flow.
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.csNew code fix provider that migrates unsafe usage sites (wrap blocks/expressions, etc.).
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.csNew document fixer implementing the migration transformations and heuristics.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierMigrationCodeFixProvider.csNew code fix provider that removes legacy unsafe modifiers when eligible.
src/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.csNew shared helpers for adding unsafe contexts and safety documentation.
src/tools/illink/src/ILLink.CodeFix/Resources.resxAdds code fix titles for the new migration fixers.
eng/liveILLink.targetsUpdates live-ILLink wiring to support enabling unsafe migration and design-time analyzer referencing.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:36

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:00

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.cs Outdated
Comment threadeng/liveILLink.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:38

CopilotAI 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

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

CopilotAI review requested due to automatic review settings July 13, 2026 18:12

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
EgorBoand others added 2 commits July 16, 2026 13:39
- Parse unsafe(...) with explicit preview + updated-memory-safety-rules options
- Use SyntaxFactory elastic newline trivia instead of hardcoded CRLF literals
- Remove redundant single-arm switch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 13:08

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
The IL5006 document fixer could emit code that fails to compile:
- unsafe(...) expressions at statement/void/assignment-target positions (only valid as a value sub-expression) now fall back to unsafe { } blocks
- out-var/pattern-var declarations (Foo(out int y);) are forward-declared before the unsafe block so they stay in scope
- out-vars in a local declaration initializer (int x = Foo(out int y);) fall back to the unsafe(...) expression form, keeping the variable in scope
- only interior (not leading/trailing) directives block wrapping a statement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 15:48

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
- Split multi-variable event field declarations with mixed safe/unsafe contracts so 'unsafe' is applied only to the variables that require it (avoids CS9365 where a safe interface event would be made illegally unsafe)
- Drop the statement-wrap branch that ignored requiresExpression, so using/await/directive statements that need an unsafe(...) expression fall back to whole-body wrapping instead of an unsafe { } block that would change scope
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:11

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
When splitting a multi-variable event field, keep each variable node's trivia (e.g. inline comments like 'event Action E1 /* keep */, E2;') instead of stripping it; only the outer declaration trivia is normalized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:31

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee40608f-4e94-47e1-8763-843d916bab40
CopilotAI review requested due to automatic review settings July 17, 2026 13:03

CopilotAI 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

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

@github-actions

github-actionsBot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
"version": 5,
"last_dispatched_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_reviewed_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_recorded_worker_run_id": "29686415576",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"review_id": 4730737351
}
]
}

@github-actionsgithub-actionsBot 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.

Holistic Review

Motivation: The change implements automated migration tooling (analyzer IL5005/IL5006 plus code-fix providers) for the unsafe-evolution C# language proposal, letting the runtime repo mechanically strip legacy/invalid unsafe modifiers and add required unsafe blocks, expressions, and <safety> documentation. This is a real, well-motivated need: migrating a large codebase to the new memory-safety rules by hand would be impractical and error-prone.

Approach: The approach is sound and appropriately scoped. All new code is wrapped in #if DEBUG and gated behind the opt-in EnableUnsafeMigration MSBuild property, so it adds no shipping public API surface and cannot affect production builds or normal analyzer runs. It reuses the existing ILLink analyzer/code-fix infrastructure (diagnostic IDs, categories, resx strings, CSharpCodeFixVerifier) rather than inventing parallel machinery, and the IL5005/5006 IDs and Unsafe category are correctly slotted into DiagnosticId/DiagnosticCategory without disturbing the existing AOT range mapping. The logic handles a broad matrix of C# constructs (partial members, interfaces, accessors, stackalloc, async, top-level statements, scoped refs), which the extensive test file exercises.

Summary: ⚠️ Needs Human Review. The code is well-structured, self-contained, and thoroughly tested for a migration tool, and it carries essentially zero production risk because it is DEBUG-only and opt-in. I have no blocking findings. However, this is a large (2,661-line) body of intricate syntax/semantic-model manipulation whose correctness is best judged by the ILLink area owners against the actual proposal semantics and by running it over real libraries — a level of validation this static review cannot fully substitute for. Human reviewers should focus on the correctness of the modifier-removal/retention heuristics (interop, overrides, interface contracts, pointer-in-signature detection) and the <safety>/skiplocalsinit handling, and confirm the tool's output on representative source is what the proposal intends.


Detailed Findings

✅ Scope & Risk — Correctly isolated tooling change

Every new type and every touched enum/category/resx addition is under #if DEBUG and further gated by the EnableUnsafeMigration compiler-visible property (verified in Microsoft.NET.ILLink.Analyzers.props, MSBuildPropertyOptionNames.cs, and both code-fix providers' RegisterCodeFixesAsync). No ref/ assembly or shipping public API is introduced, so the API-approval gate does not apply. Production analyzer behavior is unchanged.

✅ Codebase Fit — Consistent with existing ILLink patterns

Diagnostic IDs (5005/5006), the new Unsafe category, and the resx entries follow existing conventions. The GetDiagnosticCategory switch inserts the two new IDs before the >= 3050 and <= 6000 => AOT arm so they resolve to Unsafe rather than being swallowed by the AOT range — this ordering is correct and covered by the UnsafeMigrationDiagnostics_HaveUnsafeCategoryWithoutHelpLinks test.

✅ Test Quality — Broad behavioral coverage

The new RequiresUnsafeCodeFixTests.cs adds ~44 focused test methods covering modifier removal/retention, whole-body vs. statement vs. expression wrapping, forward declarations, ref locals, async/yield exclusions, and the migration-disabled case. Using CSharpCodeFixVerifier with before/after sources is the right harness for code-fix validation. Note: I could not build or run these tests in this environment; the human reviewer should confirm they pass in CI.

💡 Brittle syntax-node detection (inline)

One low-severity, non-blocking maintainability note is left inline in UnsafeMigrationAnalysis.cs regarding the GetType().Name == "UnsafeExpressionSyntax" string check used to recognize the preview unsafe(...) expression node.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 192.5 AIC · ⌖ 12.1 AIC · ⊞ 10K

private static bool IsInUnsafeContext(SyntaxNode node)
=> node.AncestorsAndSelf().Any(static ancestor =>
ancestor is UnsafeStatementSyntax ||
ancestor.GetType().Name == "UnsafeExpressionSyntax" ||

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 uses a stringly-typed check (ancestor.GetType().Name == "UnsafeExpressionSyntax") to detect the preview unsafe(...) expression node, presumably because the strongly-typed syntax type isn't available in the referenced Roslyn version. That's a reasonable pragmatic workaround, but it is brittle: it silently breaks if the node type is renamed, and it won't match derived/wrapped forms. Consider adding a short comment explaining why the reflection-by-name check is used and, if feasible, guarding it with a SyntaxKind check so the intent survives a Roslyn bump. Non-blocking given this is DEBUG-only tooling.

@EgorBoEgorBo closed this Jul 20, 2026
@EgorBo

Copy link
Copy Markdown
MemberAuthor

decided to split into many small analyzers/fixers and the first batch is in #131002

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

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@EgorBo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add unsafe evolution migration code fixers - #130611

Closed
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3
Closed

Add unsafe evolution migration code fixers#130611
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Spec: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md

What it does:

  • Adds IL5005 to remove legacy or invalid unsafe modifiers.
    • Type declarations such as classes, structs, interfaces, records, record structs, and enums.
    • Delegate declarations, static constructors, and destructors.
    • Methods, instance constructors, operators, conversion operators, local functions, properties, indexers, and events when:
      • no pointer or function pointer in the signature (we keep those caller-unsafe)
      • no <safety> comment. It's a sign that this unsafe was from unsafe-v1.
    • Keeps unsafe when required by interop, partial members, overrides, or interface contracts.
  • Adds IL5006 to add required unsafe blocks, expressions, and contract modifiers.
  • Adds unsafe and <safety> placeholders to unannotated interop declarations.
  • Covers partial members, interfaces, accessors, stackalloc, async code, top-level statements, and scoped refs.

How to run it from the repo root:

C:\prj\dotnet-latest\dotnet.cmd build .\src\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj -c Debug --no-restore
$env:EnableUnsafeMigration='true'
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5005 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd build .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0-c Debug --no-restore -p:BuildProjectReferences=false -p:EnableUnsafeMigration=true

I was using the latest daily SDK in C:\prj\dotnet-latest, not sure the bootstrapped one contains all the new C# features.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:23
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 13, 2026 09:23
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 13, 2026

CopilotAI 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 adds a DEBUG-only “unsafe evolution” migration analyzer + code fixers to ILLink, introducing two new IL* diagnostics (IL5005/IL5006) to (1) remove legacy/invalid unsafe modifiers and (2) migrate unsafe usages by adding explicit unsafe contexts and safety placeholders. It also wires up additional MSBuild properties and live-ILLink behavior to enable the migration experience, and adds/extends Roslyn analyzer/codefix tests.

Changes:

  • Add UnsafeMigrationAnalyzer + UnsafeMigrationAnalysis to detect modifier removals, required declaration updates, and unsafe-operation sites (behind #if DEBUG).
  • Add two new code fix providers (modifier removal + usage migration) and supporting document/AST rewrite helpers (behind #if DEBUG).
  • Add new diagnostic strings/IDs and update build/targets/tests to enable and validate the migration workflow.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csAdds test helpers and extensive test coverage for IL5005/IL5006 codefix behavior.
src/tools/illink/src/ILLink.Shared/SharedStrings.resxAdds user-facing title/message strings for the new unsafe migration diagnostics.
src/tools/illink/src/ILLink.Shared/DiagnosticId.csAdds DEBUG-only diagnostic IDs and introduces an “Unsafe” category range mapping.
src/tools/illink/src/ILLink.Shared/DiagnosticCategory.csAdds DEBUG-only Unsafe category constant.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalyzer.csNew analyzer that reports file-level migration diagnostics when migration is enabled.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.csNew analysis engine for modifier removals, contract/declaration updates, and unsafe operation locations.
src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.csAdds DEBUG-only MSBuild property names used to toggle unsafe migration behavior.
src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.propsExposes additional compiler-visible MSBuild properties used by the migration flow.
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.csNew code fix provider that migrates unsafe usage sites (wrap blocks/expressions, etc.).
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.csNew document fixer implementing the migration transformations and heuristics.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierMigrationCodeFixProvider.csNew code fix provider that removes legacy unsafe modifiers when eligible.
src/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.csNew shared helpers for adding unsafe contexts and safety documentation.
src/tools/illink/src/ILLink.CodeFix/Resources.resxAdds code fix titles for the new migration fixers.
eng/liveILLink.targetsUpdates live-ILLink wiring to support enabling unsafe migration and design-time analyzer referencing.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:36

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:00

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.cs Outdated
Comment threadeng/liveILLink.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:38

CopilotAI 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

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

CopilotAI review requested due to automatic review settings July 13, 2026 18:12

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
EgorBoand others added 2 commits July 16, 2026 13:39
- Parse unsafe(...) with explicit preview + updated-memory-safety-rules options
- Use SyntaxFactory elastic newline trivia instead of hardcoded CRLF literals
- Remove redundant single-arm switch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 13:08

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
The IL5006 document fixer could emit code that fails to compile:
- unsafe(...) expressions at statement/void/assignment-target positions (only valid as a value sub-expression) now fall back to unsafe { } blocks
- out-var/pattern-var declarations (Foo(out int y);) are forward-declared before the unsafe block so they stay in scope
- out-vars in a local declaration initializer (int x = Foo(out int y);) fall back to the unsafe(...) expression form, keeping the variable in scope
- only interior (not leading/trailing) directives block wrapping a statement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 15:48

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
- Split multi-variable event field declarations with mixed safe/unsafe contracts so 'unsafe' is applied only to the variables that require it (avoids CS9365 where a safe interface event would be made illegally unsafe)
- Drop the statement-wrap branch that ignored requiresExpression, so using/await/directive statements that need an unsafe(...) expression fall back to whole-body wrapping instead of an unsafe { } block that would change scope
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:11

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
When splitting a multi-variable event field, keep each variable node's trivia (e.g. inline comments like 'event Action E1 /* keep */, E2;') instead of stripping it; only the outer declaration trivia is normalized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:31

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee40608f-4e94-47e1-8763-843d916bab40
CopilotAI review requested due to automatic review settings July 17, 2026 13:03

CopilotAI 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

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

@github-actions

github-actionsBot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
"version": 5,
"last_dispatched_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_reviewed_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_recorded_worker_run_id": "29686415576",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"review_id": 4730737351
}
]
}

@github-actionsgithub-actionsBot 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.

Holistic Review

Motivation: The change implements automated migration tooling (analyzer IL5005/IL5006 plus code-fix providers) for the unsafe-evolution C# language proposal, letting the runtime repo mechanically strip legacy/invalid unsafe modifiers and add required unsafe blocks, expressions, and <safety> documentation. This is a real, well-motivated need: migrating a large codebase to the new memory-safety rules by hand would be impractical and error-prone.

Approach: The approach is sound and appropriately scoped. All new code is wrapped in #if DEBUG and gated behind the opt-in EnableUnsafeMigration MSBuild property, so it adds no shipping public API surface and cannot affect production builds or normal analyzer runs. It reuses the existing ILLink analyzer/code-fix infrastructure (diagnostic IDs, categories, resx strings, CSharpCodeFixVerifier) rather than inventing parallel machinery, and the IL5005/5006 IDs and Unsafe category are correctly slotted into DiagnosticId/DiagnosticCategory without disturbing the existing AOT range mapping. The logic handles a broad matrix of C# constructs (partial members, interfaces, accessors, stackalloc, async, top-level statements, scoped refs), which the extensive test file exercises.

Summary: ⚠️ Needs Human Review. The code is well-structured, self-contained, and thoroughly tested for a migration tool, and it carries essentially zero production risk because it is DEBUG-only and opt-in. I have no blocking findings. However, this is a large (2,661-line) body of intricate syntax/semantic-model manipulation whose correctness is best judged by the ILLink area owners against the actual proposal semantics and by running it over real libraries — a level of validation this static review cannot fully substitute for. Human reviewers should focus on the correctness of the modifier-removal/retention heuristics (interop, overrides, interface contracts, pointer-in-signature detection) and the <safety>/skiplocalsinit handling, and confirm the tool's output on representative source is what the proposal intends.


Detailed Findings

✅ Scope & Risk — Correctly isolated tooling change

Every new type and every touched enum/category/resx addition is under #if DEBUG and further gated by the EnableUnsafeMigration compiler-visible property (verified in Microsoft.NET.ILLink.Analyzers.props, MSBuildPropertyOptionNames.cs, and both code-fix providers' RegisterCodeFixesAsync). No ref/ assembly or shipping public API is introduced, so the API-approval gate does not apply. Production analyzer behavior is unchanged.

✅ Codebase Fit — Consistent with existing ILLink patterns

Diagnostic IDs (5005/5006), the new Unsafe category, and the resx entries follow existing conventions. The GetDiagnosticCategory switch inserts the two new IDs before the >= 3050 and <= 6000 => AOT arm so they resolve to Unsafe rather than being swallowed by the AOT range — this ordering is correct and covered by the UnsafeMigrationDiagnostics_HaveUnsafeCategoryWithoutHelpLinks test.

✅ Test Quality — Broad behavioral coverage

The new RequiresUnsafeCodeFixTests.cs adds ~44 focused test methods covering modifier removal/retention, whole-body vs. statement vs. expression wrapping, forward declarations, ref locals, async/yield exclusions, and the migration-disabled case. Using CSharpCodeFixVerifier with before/after sources is the right harness for code-fix validation. Note: I could not build or run these tests in this environment; the human reviewer should confirm they pass in CI.

💡 Brittle syntax-node detection (inline)

One low-severity, non-blocking maintainability note is left inline in UnsafeMigrationAnalysis.cs regarding the GetType().Name == "UnsafeExpressionSyntax" string check used to recognize the preview unsafe(...) expression node.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 192.5 AIC · ⌖ 12.1 AIC · ⊞ 10K

private static bool IsInUnsafeContext(SyntaxNode node)
=> node.AncestorsAndSelf().Any(static ancestor =>
ancestor is UnsafeStatementSyntax ||
ancestor.GetType().Name == "UnsafeExpressionSyntax" ||

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 uses a stringly-typed check (ancestor.GetType().Name == "UnsafeExpressionSyntax") to detect the preview unsafe(...) expression node, presumably because the strongly-typed syntax type isn't available in the referenced Roslyn version. That's a reasonable pragmatic workaround, but it is brittle: it silently breaks if the node type is renamed, and it won't match derived/wrapped forms. Consider adding a short comment explaining why the reflection-by-name check is used and, if feasible, guarding it with a SyntaxKind check so the intent survives a Roslyn bump. Non-blocking given this is DEBUG-only tooling.

@EgorBoEgorBo closed this Jul 20, 2026
@EgorBo

Copy link
Copy Markdown
MemberAuthor

decided to split into many small analyzers/fixers and the first batch is in #131002

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

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@EgorBo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add unsafe evolution migration code fixers - #130611

Closed
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3
Closed

Add unsafe evolution migration code fixers#130611
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Spec: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md

What it does:

  • Adds IL5005 to remove legacy or invalid unsafe modifiers.
    • Type declarations such as classes, structs, interfaces, records, record structs, and enums.
    • Delegate declarations, static constructors, and destructors.
    • Methods, instance constructors, operators, conversion operators, local functions, properties, indexers, and events when:
      • no pointer or function pointer in the signature (we keep those caller-unsafe)
      • no <safety> comment. It's a sign that this unsafe was from unsafe-v1.
    • Keeps unsafe when required by interop, partial members, overrides, or interface contracts.
  • Adds IL5006 to add required unsafe blocks, expressions, and contract modifiers.
  • Adds unsafe and <safety> placeholders to unannotated interop declarations.
  • Covers partial members, interfaces, accessors, stackalloc, async code, top-level statements, and scoped refs.

How to run it from the repo root:

C:\prj\dotnet-latest\dotnet.cmd build .\src\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj -c Debug --no-restore
$env:EnableUnsafeMigration='true'
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5005 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd build .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0-c Debug --no-restore -p:BuildProjectReferences=false -p:EnableUnsafeMigration=true

I was using the latest daily SDK in C:\prj\dotnet-latest, not sure the bootstrapped one contains all the new C# features.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:23
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 13, 2026 09:23
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 13, 2026

CopilotAI 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 adds a DEBUG-only “unsafe evolution” migration analyzer + code fixers to ILLink, introducing two new IL* diagnostics (IL5005/IL5006) to (1) remove legacy/invalid unsafe modifiers and (2) migrate unsafe usages by adding explicit unsafe contexts and safety placeholders. It also wires up additional MSBuild properties and live-ILLink behavior to enable the migration experience, and adds/extends Roslyn analyzer/codefix tests.

Changes:

  • Add UnsafeMigrationAnalyzer + UnsafeMigrationAnalysis to detect modifier removals, required declaration updates, and unsafe-operation sites (behind #if DEBUG).
  • Add two new code fix providers (modifier removal + usage migration) and supporting document/AST rewrite helpers (behind #if DEBUG).
  • Add new diagnostic strings/IDs and update build/targets/tests to enable and validate the migration workflow.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csAdds test helpers and extensive test coverage for IL5005/IL5006 codefix behavior.
src/tools/illink/src/ILLink.Shared/SharedStrings.resxAdds user-facing title/message strings for the new unsafe migration diagnostics.
src/tools/illink/src/ILLink.Shared/DiagnosticId.csAdds DEBUG-only diagnostic IDs and introduces an “Unsafe” category range mapping.
src/tools/illink/src/ILLink.Shared/DiagnosticCategory.csAdds DEBUG-only Unsafe category constant.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalyzer.csNew analyzer that reports file-level migration diagnostics when migration is enabled.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.csNew analysis engine for modifier removals, contract/declaration updates, and unsafe operation locations.
src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.csAdds DEBUG-only MSBuild property names used to toggle unsafe migration behavior.
src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.propsExposes additional compiler-visible MSBuild properties used by the migration flow.
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.csNew code fix provider that migrates unsafe usage sites (wrap blocks/expressions, etc.).
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.csNew document fixer implementing the migration transformations and heuristics.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierMigrationCodeFixProvider.csNew code fix provider that removes legacy unsafe modifiers when eligible.
src/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.csNew shared helpers for adding unsafe contexts and safety documentation.
src/tools/illink/src/ILLink.CodeFix/Resources.resxAdds code fix titles for the new migration fixers.
eng/liveILLink.targetsUpdates live-ILLink wiring to support enabling unsafe migration and design-time analyzer referencing.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:36

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:00

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.cs Outdated
Comment threadeng/liveILLink.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:38

CopilotAI 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

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

CopilotAI review requested due to automatic review settings July 13, 2026 18:12

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
EgorBoand others added 2 commits July 16, 2026 13:39
- Parse unsafe(...) with explicit preview + updated-memory-safety-rules options
- Use SyntaxFactory elastic newline trivia instead of hardcoded CRLF literals
- Remove redundant single-arm switch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 13:08

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
The IL5006 document fixer could emit code that fails to compile:
- unsafe(...) expressions at statement/void/assignment-target positions (only valid as a value sub-expression) now fall back to unsafe { } blocks
- out-var/pattern-var declarations (Foo(out int y);) are forward-declared before the unsafe block so they stay in scope
- out-vars in a local declaration initializer (int x = Foo(out int y);) fall back to the unsafe(...) expression form, keeping the variable in scope
- only interior (not leading/trailing) directives block wrapping a statement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 15:48

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
- Split multi-variable event field declarations with mixed safe/unsafe contracts so 'unsafe' is applied only to the variables that require it (avoids CS9365 where a safe interface event would be made illegally unsafe)
- Drop the statement-wrap branch that ignored requiresExpression, so using/await/directive statements that need an unsafe(...) expression fall back to whole-body wrapping instead of an unsafe { } block that would change scope
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:11

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
When splitting a multi-variable event field, keep each variable node's trivia (e.g. inline comments like 'event Action E1 /* keep */, E2;') instead of stripping it; only the outer declaration trivia is normalized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:31

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee40608f-4e94-47e1-8763-843d916bab40
CopilotAI review requested due to automatic review settings July 17, 2026 13:03

CopilotAI 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

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

@github-actions

github-actionsBot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
"version": 5,
"last_dispatched_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_reviewed_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_recorded_worker_run_id": "29686415576",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"review_id": 4730737351
}
]
}

@github-actionsgithub-actionsBot 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.

Holistic Review

Motivation: The change implements automated migration tooling (analyzer IL5005/IL5006 plus code-fix providers) for the unsafe-evolution C# language proposal, letting the runtime repo mechanically strip legacy/invalid unsafe modifiers and add required unsafe blocks, expressions, and <safety> documentation. This is a real, well-motivated need: migrating a large codebase to the new memory-safety rules by hand would be impractical and error-prone.

Approach: The approach is sound and appropriately scoped. All new code is wrapped in #if DEBUG and gated behind the opt-in EnableUnsafeMigration MSBuild property, so it adds no shipping public API surface and cannot affect production builds or normal analyzer runs. It reuses the existing ILLink analyzer/code-fix infrastructure (diagnostic IDs, categories, resx strings, CSharpCodeFixVerifier) rather than inventing parallel machinery, and the IL5005/5006 IDs and Unsafe category are correctly slotted into DiagnosticId/DiagnosticCategory without disturbing the existing AOT range mapping. The logic handles a broad matrix of C# constructs (partial members, interfaces, accessors, stackalloc, async, top-level statements, scoped refs), which the extensive test file exercises.

Summary: ⚠️ Needs Human Review. The code is well-structured, self-contained, and thoroughly tested for a migration tool, and it carries essentially zero production risk because it is DEBUG-only and opt-in. I have no blocking findings. However, this is a large (2,661-line) body of intricate syntax/semantic-model manipulation whose correctness is best judged by the ILLink area owners against the actual proposal semantics and by running it over real libraries — a level of validation this static review cannot fully substitute for. Human reviewers should focus on the correctness of the modifier-removal/retention heuristics (interop, overrides, interface contracts, pointer-in-signature detection) and the <safety>/skiplocalsinit handling, and confirm the tool's output on representative source is what the proposal intends.


Detailed Findings

✅ Scope & Risk — Correctly isolated tooling change

Every new type and every touched enum/category/resx addition is under #if DEBUG and further gated by the EnableUnsafeMigration compiler-visible property (verified in Microsoft.NET.ILLink.Analyzers.props, MSBuildPropertyOptionNames.cs, and both code-fix providers' RegisterCodeFixesAsync). No ref/ assembly or shipping public API is introduced, so the API-approval gate does not apply. Production analyzer behavior is unchanged.

✅ Codebase Fit — Consistent with existing ILLink patterns

Diagnostic IDs (5005/5006), the new Unsafe category, and the resx entries follow existing conventions. The GetDiagnosticCategory switch inserts the two new IDs before the >= 3050 and <= 6000 => AOT arm so they resolve to Unsafe rather than being swallowed by the AOT range — this ordering is correct and covered by the UnsafeMigrationDiagnostics_HaveUnsafeCategoryWithoutHelpLinks test.

✅ Test Quality — Broad behavioral coverage

The new RequiresUnsafeCodeFixTests.cs adds ~44 focused test methods covering modifier removal/retention, whole-body vs. statement vs. expression wrapping, forward declarations, ref locals, async/yield exclusions, and the migration-disabled case. Using CSharpCodeFixVerifier with before/after sources is the right harness for code-fix validation. Note: I could not build or run these tests in this environment; the human reviewer should confirm they pass in CI.

💡 Brittle syntax-node detection (inline)

One low-severity, non-blocking maintainability note is left inline in UnsafeMigrationAnalysis.cs regarding the GetType().Name == "UnsafeExpressionSyntax" string check used to recognize the preview unsafe(...) expression node.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 192.5 AIC · ⌖ 12.1 AIC · ⊞ 10K

private static bool IsInUnsafeContext(SyntaxNode node)
=> node.AncestorsAndSelf().Any(static ancestor =>
ancestor is UnsafeStatementSyntax ||
ancestor.GetType().Name == "UnsafeExpressionSyntax" ||

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 uses a stringly-typed check (ancestor.GetType().Name == "UnsafeExpressionSyntax") to detect the preview unsafe(...) expression node, presumably because the strongly-typed syntax type isn't available in the referenced Roslyn version. That's a reasonable pragmatic workaround, but it is brittle: it silently breaks if the node type is renamed, and it won't match derived/wrapped forms. Consider adding a short comment explaining why the reflection-by-name check is used and, if feasible, guarding it with a SyntaxKind check so the intent survives a Roslyn bump. Non-blocking given this is DEBUG-only tooling.

@EgorBoEgorBo closed this Jul 20, 2026
@EgorBo

Copy link
Copy Markdown
MemberAuthor

decided to split into many small analyzers/fixers and the first batch is in #131002

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

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@EgorBo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add unsafe evolution migration code fixers - #130611

Closed
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3
Closed

Add unsafe evolution migration code fixers#130611
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Spec: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md

What it does:

  • Adds IL5005 to remove legacy or invalid unsafe modifiers.
    • Type declarations such as classes, structs, interfaces, records, record structs, and enums.
    • Delegate declarations, static constructors, and destructors.
    • Methods, instance constructors, operators, conversion operators, local functions, properties, indexers, and events when:
      • no pointer or function pointer in the signature (we keep those caller-unsafe)
      • no <safety> comment. It's a sign that this unsafe was from unsafe-v1.
    • Keeps unsafe when required by interop, partial members, overrides, or interface contracts.
  • Adds IL5006 to add required unsafe blocks, expressions, and contract modifiers.
  • Adds unsafe and <safety> placeholders to unannotated interop declarations.
  • Covers partial members, interfaces, accessors, stackalloc, async code, top-level statements, and scoped refs.

How to run it from the repo root:

C:\prj\dotnet-latest\dotnet.cmd build .\src\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj -c Debug --no-restore
$env:EnableUnsafeMigration='true'
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5005 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd build .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0-c Debug --no-restore -p:BuildProjectReferences=false -p:EnableUnsafeMigration=true

I was using the latest daily SDK in C:\prj\dotnet-latest, not sure the bootstrapped one contains all the new C# features.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:23
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 13, 2026 09:23
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 13, 2026

CopilotAI 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 adds a DEBUG-only “unsafe evolution” migration analyzer + code fixers to ILLink, introducing two new IL* diagnostics (IL5005/IL5006) to (1) remove legacy/invalid unsafe modifiers and (2) migrate unsafe usages by adding explicit unsafe contexts and safety placeholders. It also wires up additional MSBuild properties and live-ILLink behavior to enable the migration experience, and adds/extends Roslyn analyzer/codefix tests.

Changes:

  • Add UnsafeMigrationAnalyzer + UnsafeMigrationAnalysis to detect modifier removals, required declaration updates, and unsafe-operation sites (behind #if DEBUG).
  • Add two new code fix providers (modifier removal + usage migration) and supporting document/AST rewrite helpers (behind #if DEBUG).
  • Add new diagnostic strings/IDs and update build/targets/tests to enable and validate the migration workflow.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csAdds test helpers and extensive test coverage for IL5005/IL5006 codefix behavior.
src/tools/illink/src/ILLink.Shared/SharedStrings.resxAdds user-facing title/message strings for the new unsafe migration diagnostics.
src/tools/illink/src/ILLink.Shared/DiagnosticId.csAdds DEBUG-only diagnostic IDs and introduces an “Unsafe” category range mapping.
src/tools/illink/src/ILLink.Shared/DiagnosticCategory.csAdds DEBUG-only Unsafe category constant.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalyzer.csNew analyzer that reports file-level migration diagnostics when migration is enabled.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.csNew analysis engine for modifier removals, contract/declaration updates, and unsafe operation locations.
src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.csAdds DEBUG-only MSBuild property names used to toggle unsafe migration behavior.
src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.propsExposes additional compiler-visible MSBuild properties used by the migration flow.
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.csNew code fix provider that migrates unsafe usage sites (wrap blocks/expressions, etc.).
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.csNew document fixer implementing the migration transformations and heuristics.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierMigrationCodeFixProvider.csNew code fix provider that removes legacy unsafe modifiers when eligible.
src/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.csNew shared helpers for adding unsafe contexts and safety documentation.
src/tools/illink/src/ILLink.CodeFix/Resources.resxAdds code fix titles for the new migration fixers.
eng/liveILLink.targetsUpdates live-ILLink wiring to support enabling unsafe migration and design-time analyzer referencing.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:36

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:00

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.cs Outdated
Comment threadeng/liveILLink.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:38

CopilotAI 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

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

CopilotAI review requested due to automatic review settings July 13, 2026 18:12

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
EgorBoand others added 2 commits July 16, 2026 13:39
- Parse unsafe(...) with explicit preview + updated-memory-safety-rules options
- Use SyntaxFactory elastic newline trivia instead of hardcoded CRLF literals
- Remove redundant single-arm switch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 13:08

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
The IL5006 document fixer could emit code that fails to compile:
- unsafe(...) expressions at statement/void/assignment-target positions (only valid as a value sub-expression) now fall back to unsafe { } blocks
- out-var/pattern-var declarations (Foo(out int y);) are forward-declared before the unsafe block so they stay in scope
- out-vars in a local declaration initializer (int x = Foo(out int y);) fall back to the unsafe(...) expression form, keeping the variable in scope
- only interior (not leading/trailing) directives block wrapping a statement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 15:48

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
- Split multi-variable event field declarations with mixed safe/unsafe contracts so 'unsafe' is applied only to the variables that require it (avoids CS9365 where a safe interface event would be made illegally unsafe)
- Drop the statement-wrap branch that ignored requiresExpression, so using/await/directive statements that need an unsafe(...) expression fall back to whole-body wrapping instead of an unsafe { } block that would change scope
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:11

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
When splitting a multi-variable event field, keep each variable node's trivia (e.g. inline comments like 'event Action E1 /* keep */, E2;') instead of stripping it; only the outer declaration trivia is normalized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:31

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee40608f-4e94-47e1-8763-843d916bab40
CopilotAI review requested due to automatic review settings July 17, 2026 13:03

CopilotAI 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

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

@github-actions

github-actionsBot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
"version": 5,
"last_dispatched_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_reviewed_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_recorded_worker_run_id": "29686415576",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"review_id": 4730737351
}
]
}

@github-actionsgithub-actionsBot 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.

Holistic Review

Motivation: The change implements automated migration tooling (analyzer IL5005/IL5006 plus code-fix providers) for the unsafe-evolution C# language proposal, letting the runtime repo mechanically strip legacy/invalid unsafe modifiers and add required unsafe blocks, expressions, and <safety> documentation. This is a real, well-motivated need: migrating a large codebase to the new memory-safety rules by hand would be impractical and error-prone.

Approach: The approach is sound and appropriately scoped. All new code is wrapped in #if DEBUG and gated behind the opt-in EnableUnsafeMigration MSBuild property, so it adds no shipping public API surface and cannot affect production builds or normal analyzer runs. It reuses the existing ILLink analyzer/code-fix infrastructure (diagnostic IDs, categories, resx strings, CSharpCodeFixVerifier) rather than inventing parallel machinery, and the IL5005/5006 IDs and Unsafe category are correctly slotted into DiagnosticId/DiagnosticCategory without disturbing the existing AOT range mapping. The logic handles a broad matrix of C# constructs (partial members, interfaces, accessors, stackalloc, async, top-level statements, scoped refs), which the extensive test file exercises.

Summary: ⚠️ Needs Human Review. The code is well-structured, self-contained, and thoroughly tested for a migration tool, and it carries essentially zero production risk because it is DEBUG-only and opt-in. I have no blocking findings. However, this is a large (2,661-line) body of intricate syntax/semantic-model manipulation whose correctness is best judged by the ILLink area owners against the actual proposal semantics and by running it over real libraries — a level of validation this static review cannot fully substitute for. Human reviewers should focus on the correctness of the modifier-removal/retention heuristics (interop, overrides, interface contracts, pointer-in-signature detection) and the <safety>/skiplocalsinit handling, and confirm the tool's output on representative source is what the proposal intends.


Detailed Findings

✅ Scope & Risk — Correctly isolated tooling change

Every new type and every touched enum/category/resx addition is under #if DEBUG and further gated by the EnableUnsafeMigration compiler-visible property (verified in Microsoft.NET.ILLink.Analyzers.props, MSBuildPropertyOptionNames.cs, and both code-fix providers' RegisterCodeFixesAsync). No ref/ assembly or shipping public API is introduced, so the API-approval gate does not apply. Production analyzer behavior is unchanged.

✅ Codebase Fit — Consistent with existing ILLink patterns

Diagnostic IDs (5005/5006), the new Unsafe category, and the resx entries follow existing conventions. The GetDiagnosticCategory switch inserts the two new IDs before the >= 3050 and <= 6000 => AOT arm so they resolve to Unsafe rather than being swallowed by the AOT range — this ordering is correct and covered by the UnsafeMigrationDiagnostics_HaveUnsafeCategoryWithoutHelpLinks test.

✅ Test Quality — Broad behavioral coverage

The new RequiresUnsafeCodeFixTests.cs adds ~44 focused test methods covering modifier removal/retention, whole-body vs. statement vs. expression wrapping, forward declarations, ref locals, async/yield exclusions, and the migration-disabled case. Using CSharpCodeFixVerifier with before/after sources is the right harness for code-fix validation. Note: I could not build or run these tests in this environment; the human reviewer should confirm they pass in CI.

💡 Brittle syntax-node detection (inline)

One low-severity, non-blocking maintainability note is left inline in UnsafeMigrationAnalysis.cs regarding the GetType().Name == "UnsafeExpressionSyntax" string check used to recognize the preview unsafe(...) expression node.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 192.5 AIC · ⌖ 12.1 AIC · ⊞ 10K

private static bool IsInUnsafeContext(SyntaxNode node)
=> node.AncestorsAndSelf().Any(static ancestor =>
ancestor is UnsafeStatementSyntax ||
ancestor.GetType().Name == "UnsafeExpressionSyntax" ||

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 uses a stringly-typed check (ancestor.GetType().Name == "UnsafeExpressionSyntax") to detect the preview unsafe(...) expression node, presumably because the strongly-typed syntax type isn't available in the referenced Roslyn version. That's a reasonable pragmatic workaround, but it is brittle: it silently breaks if the node type is renamed, and it won't match derived/wrapped forms. Consider adding a short comment explaining why the reflection-by-name check is used and, if feasible, guarding it with a SyntaxKind check so the intent survives a Roslyn bump. Non-blocking given this is DEBUG-only tooling.

@EgorBoEgorBo closed this Jul 20, 2026
@EgorBo

Copy link
Copy Markdown
MemberAuthor

decided to split into many small analyzers/fixers and the first batch is in #131002

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

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@EgorBo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add unsafe evolution migration code fixers - #130611

Closed
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3
Closed

Add unsafe evolution migration code fixers#130611
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Spec: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md

What it does:

  • Adds IL5005 to remove legacy or invalid unsafe modifiers.
    • Type declarations such as classes, structs, interfaces, records, record structs, and enums.
    • Delegate declarations, static constructors, and destructors.
    • Methods, instance constructors, operators, conversion operators, local functions, properties, indexers, and events when:
      • no pointer or function pointer in the signature (we keep those caller-unsafe)
      • no <safety> comment. It's a sign that this unsafe was from unsafe-v1.
    • Keeps unsafe when required by interop, partial members, overrides, or interface contracts.
  • Adds IL5006 to add required unsafe blocks, expressions, and contract modifiers.
  • Adds unsafe and <safety> placeholders to unannotated interop declarations.
  • Covers partial members, interfaces, accessors, stackalloc, async code, top-level statements, and scoped refs.

How to run it from the repo root:

C:\prj\dotnet-latest\dotnet.cmd build .\src\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj -c Debug --no-restore
$env:EnableUnsafeMigration='true'
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5005 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd build .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0-c Debug --no-restore -p:BuildProjectReferences=false -p:EnableUnsafeMigration=true

I was using the latest daily SDK in C:\prj\dotnet-latest, not sure the bootstrapped one contains all the new C# features.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:23
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 13, 2026 09:23
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 13, 2026

CopilotAI 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 adds a DEBUG-only “unsafe evolution” migration analyzer + code fixers to ILLink, introducing two new IL* diagnostics (IL5005/IL5006) to (1) remove legacy/invalid unsafe modifiers and (2) migrate unsafe usages by adding explicit unsafe contexts and safety placeholders. It also wires up additional MSBuild properties and live-ILLink behavior to enable the migration experience, and adds/extends Roslyn analyzer/codefix tests.

Changes:

  • Add UnsafeMigrationAnalyzer + UnsafeMigrationAnalysis to detect modifier removals, required declaration updates, and unsafe-operation sites (behind #if DEBUG).
  • Add two new code fix providers (modifier removal + usage migration) and supporting document/AST rewrite helpers (behind #if DEBUG).
  • Add new diagnostic strings/IDs and update build/targets/tests to enable and validate the migration workflow.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csAdds test helpers and extensive test coverage for IL5005/IL5006 codefix behavior.
src/tools/illink/src/ILLink.Shared/SharedStrings.resxAdds user-facing title/message strings for the new unsafe migration diagnostics.
src/tools/illink/src/ILLink.Shared/DiagnosticId.csAdds DEBUG-only diagnostic IDs and introduces an “Unsafe” category range mapping.
src/tools/illink/src/ILLink.Shared/DiagnosticCategory.csAdds DEBUG-only Unsafe category constant.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalyzer.csNew analyzer that reports file-level migration diagnostics when migration is enabled.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.csNew analysis engine for modifier removals, contract/declaration updates, and unsafe operation locations.
src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.csAdds DEBUG-only MSBuild property names used to toggle unsafe migration behavior.
src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.propsExposes additional compiler-visible MSBuild properties used by the migration flow.
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.csNew code fix provider that migrates unsafe usage sites (wrap blocks/expressions, etc.).
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.csNew document fixer implementing the migration transformations and heuristics.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierMigrationCodeFixProvider.csNew code fix provider that removes legacy unsafe modifiers when eligible.
src/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.csNew shared helpers for adding unsafe contexts and safety documentation.
src/tools/illink/src/ILLink.CodeFix/Resources.resxAdds code fix titles for the new migration fixers.
eng/liveILLink.targetsUpdates live-ILLink wiring to support enabling unsafe migration and design-time analyzer referencing.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:36

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:00

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.cs Outdated
Comment threadeng/liveILLink.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:38

CopilotAI 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

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

CopilotAI review requested due to automatic review settings July 13, 2026 18:12

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
EgorBoand others added 2 commits July 16, 2026 13:39
- Parse unsafe(...) with explicit preview + updated-memory-safety-rules options
- Use SyntaxFactory elastic newline trivia instead of hardcoded CRLF literals
- Remove redundant single-arm switch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 13:08

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
The IL5006 document fixer could emit code that fails to compile:
- unsafe(...) expressions at statement/void/assignment-target positions (only valid as a value sub-expression) now fall back to unsafe { } blocks
- out-var/pattern-var declarations (Foo(out int y);) are forward-declared before the unsafe block so they stay in scope
- out-vars in a local declaration initializer (int x = Foo(out int y);) fall back to the unsafe(...) expression form, keeping the variable in scope
- only interior (not leading/trailing) directives block wrapping a statement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 15:48

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
- Split multi-variable event field declarations with mixed safe/unsafe contracts so 'unsafe' is applied only to the variables that require it (avoids CS9365 where a safe interface event would be made illegally unsafe)
- Drop the statement-wrap branch that ignored requiresExpression, so using/await/directive statements that need an unsafe(...) expression fall back to whole-body wrapping instead of an unsafe { } block that would change scope
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:11

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
When splitting a multi-variable event field, keep each variable node's trivia (e.g. inline comments like 'event Action E1 /* keep */, E2;') instead of stripping it; only the outer declaration trivia is normalized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:31

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee40608f-4e94-47e1-8763-843d916bab40
CopilotAI review requested due to automatic review settings July 17, 2026 13:03

CopilotAI 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

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

@github-actions

github-actionsBot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
"version": 5,
"last_dispatched_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_reviewed_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_recorded_worker_run_id": "29686415576",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"review_id": 4730737351
}
]
}

@github-actionsgithub-actionsBot 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.

Holistic Review

Motivation: The change implements automated migration tooling (analyzer IL5005/IL5006 plus code-fix providers) for the unsafe-evolution C# language proposal, letting the runtime repo mechanically strip legacy/invalid unsafe modifiers and add required unsafe blocks, expressions, and <safety> documentation. This is a real, well-motivated need: migrating a large codebase to the new memory-safety rules by hand would be impractical and error-prone.

Approach: The approach is sound and appropriately scoped. All new code is wrapped in #if DEBUG and gated behind the opt-in EnableUnsafeMigration MSBuild property, so it adds no shipping public API surface and cannot affect production builds or normal analyzer runs. It reuses the existing ILLink analyzer/code-fix infrastructure (diagnostic IDs, categories, resx strings, CSharpCodeFixVerifier) rather than inventing parallel machinery, and the IL5005/5006 IDs and Unsafe category are correctly slotted into DiagnosticId/DiagnosticCategory without disturbing the existing AOT range mapping. The logic handles a broad matrix of C# constructs (partial members, interfaces, accessors, stackalloc, async, top-level statements, scoped refs), which the extensive test file exercises.

Summary: ⚠️ Needs Human Review. The code is well-structured, self-contained, and thoroughly tested for a migration tool, and it carries essentially zero production risk because it is DEBUG-only and opt-in. I have no blocking findings. However, this is a large (2,661-line) body of intricate syntax/semantic-model manipulation whose correctness is best judged by the ILLink area owners against the actual proposal semantics and by running it over real libraries — a level of validation this static review cannot fully substitute for. Human reviewers should focus on the correctness of the modifier-removal/retention heuristics (interop, overrides, interface contracts, pointer-in-signature detection) and the <safety>/skiplocalsinit handling, and confirm the tool's output on representative source is what the proposal intends.


Detailed Findings

✅ Scope & Risk — Correctly isolated tooling change

Every new type and every touched enum/category/resx addition is under #if DEBUG and further gated by the EnableUnsafeMigration compiler-visible property (verified in Microsoft.NET.ILLink.Analyzers.props, MSBuildPropertyOptionNames.cs, and both code-fix providers' RegisterCodeFixesAsync). No ref/ assembly or shipping public API is introduced, so the API-approval gate does not apply. Production analyzer behavior is unchanged.

✅ Codebase Fit — Consistent with existing ILLink patterns

Diagnostic IDs (5005/5006), the new Unsafe category, and the resx entries follow existing conventions. The GetDiagnosticCategory switch inserts the two new IDs before the >= 3050 and <= 6000 => AOT arm so they resolve to Unsafe rather than being swallowed by the AOT range — this ordering is correct and covered by the UnsafeMigrationDiagnostics_HaveUnsafeCategoryWithoutHelpLinks test.

✅ Test Quality — Broad behavioral coverage

The new RequiresUnsafeCodeFixTests.cs adds ~44 focused test methods covering modifier removal/retention, whole-body vs. statement vs. expression wrapping, forward declarations, ref locals, async/yield exclusions, and the migration-disabled case. Using CSharpCodeFixVerifier with before/after sources is the right harness for code-fix validation. Note: I could not build or run these tests in this environment; the human reviewer should confirm they pass in CI.

💡 Brittle syntax-node detection (inline)

One low-severity, non-blocking maintainability note is left inline in UnsafeMigrationAnalysis.cs regarding the GetType().Name == "UnsafeExpressionSyntax" string check used to recognize the preview unsafe(...) expression node.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 192.5 AIC · ⌖ 12.1 AIC · ⊞ 10K

private static bool IsInUnsafeContext(SyntaxNode node)
=> node.AncestorsAndSelf().Any(static ancestor =>
ancestor is UnsafeStatementSyntax ||
ancestor.GetType().Name == "UnsafeExpressionSyntax" ||

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 uses a stringly-typed check (ancestor.GetType().Name == "UnsafeExpressionSyntax") to detect the preview unsafe(...) expression node, presumably because the strongly-typed syntax type isn't available in the referenced Roslyn version. That's a reasonable pragmatic workaround, but it is brittle: it silently breaks if the node type is renamed, and it won't match derived/wrapped forms. Consider adding a short comment explaining why the reflection-by-name check is used and, if feasible, guarding it with a SyntaxKind check so the intent survives a Roslyn bump. Non-blocking given this is DEBUG-only tooling.

@EgorBoEgorBo closed this Jul 20, 2026
@EgorBo

Copy link
Copy Markdown
MemberAuthor

decided to split into many small analyzers/fixers and the first batch is in #131002

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

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@EgorBo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add unsafe evolution migration code fixers - #130611

Closed
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3
Closed

Add unsafe evolution migration code fixers#130611
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Spec: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md

What it does:

  • Adds IL5005 to remove legacy or invalid unsafe modifiers.
    • Type declarations such as classes, structs, interfaces, records, record structs, and enums.
    • Delegate declarations, static constructors, and destructors.
    • Methods, instance constructors, operators, conversion operators, local functions, properties, indexers, and events when:
      • no pointer or function pointer in the signature (we keep those caller-unsafe)
      • no <safety> comment. It's a sign that this unsafe was from unsafe-v1.
    • Keeps unsafe when required by interop, partial members, overrides, or interface contracts.
  • Adds IL5006 to add required unsafe blocks, expressions, and contract modifiers.
  • Adds unsafe and <safety> placeholders to unannotated interop declarations.
  • Covers partial members, interfaces, accessors, stackalloc, async code, top-level statements, and scoped refs.

How to run it from the repo root:

C:\prj\dotnet-latest\dotnet.cmd build .\src\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj -c Debug --no-restore
$env:EnableUnsafeMigration='true'
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5005 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd build .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0-c Debug --no-restore -p:BuildProjectReferences=false -p:EnableUnsafeMigration=true

I was using the latest daily SDK in C:\prj\dotnet-latest, not sure the bootstrapped one contains all the new C# features.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:23
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 13, 2026 09:23
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 13, 2026

CopilotAI 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 adds a DEBUG-only “unsafe evolution” migration analyzer + code fixers to ILLink, introducing two new IL* diagnostics (IL5005/IL5006) to (1) remove legacy/invalid unsafe modifiers and (2) migrate unsafe usages by adding explicit unsafe contexts and safety placeholders. It also wires up additional MSBuild properties and live-ILLink behavior to enable the migration experience, and adds/extends Roslyn analyzer/codefix tests.

Changes:

  • Add UnsafeMigrationAnalyzer + UnsafeMigrationAnalysis to detect modifier removals, required declaration updates, and unsafe-operation sites (behind #if DEBUG).
  • Add two new code fix providers (modifier removal + usage migration) and supporting document/AST rewrite helpers (behind #if DEBUG).
  • Add new diagnostic strings/IDs and update build/targets/tests to enable and validate the migration workflow.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csAdds test helpers and extensive test coverage for IL5005/IL5006 codefix behavior.
src/tools/illink/src/ILLink.Shared/SharedStrings.resxAdds user-facing title/message strings for the new unsafe migration diagnostics.
src/tools/illink/src/ILLink.Shared/DiagnosticId.csAdds DEBUG-only diagnostic IDs and introduces an “Unsafe” category range mapping.
src/tools/illink/src/ILLink.Shared/DiagnosticCategory.csAdds DEBUG-only Unsafe category constant.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalyzer.csNew analyzer that reports file-level migration diagnostics when migration is enabled.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.csNew analysis engine for modifier removals, contract/declaration updates, and unsafe operation locations.
src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.csAdds DEBUG-only MSBuild property names used to toggle unsafe migration behavior.
src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.propsExposes additional compiler-visible MSBuild properties used by the migration flow.
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.csNew code fix provider that migrates unsafe usage sites (wrap blocks/expressions, etc.).
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.csNew document fixer implementing the migration transformations and heuristics.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierMigrationCodeFixProvider.csNew code fix provider that removes legacy unsafe modifiers when eligible.
src/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.csNew shared helpers for adding unsafe contexts and safety documentation.
src/tools/illink/src/ILLink.CodeFix/Resources.resxAdds code fix titles for the new migration fixers.
eng/liveILLink.targetsUpdates live-ILLink wiring to support enabling unsafe migration and design-time analyzer referencing.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:36

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:00

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.cs Outdated
Comment threadeng/liveILLink.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:38

CopilotAI 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

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

CopilotAI review requested due to automatic review settings July 13, 2026 18:12

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
EgorBoand others added 2 commits July 16, 2026 13:39
- Parse unsafe(...) with explicit preview + updated-memory-safety-rules options
- Use SyntaxFactory elastic newline trivia instead of hardcoded CRLF literals
- Remove redundant single-arm switch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 13:08

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
The IL5006 document fixer could emit code that fails to compile:
- unsafe(...) expressions at statement/void/assignment-target positions (only valid as a value sub-expression) now fall back to unsafe { } blocks
- out-var/pattern-var declarations (Foo(out int y);) are forward-declared before the unsafe block so they stay in scope
- out-vars in a local declaration initializer (int x = Foo(out int y);) fall back to the unsafe(...) expression form, keeping the variable in scope
- only interior (not leading/trailing) directives block wrapping a statement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 15:48

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
- Split multi-variable event field declarations with mixed safe/unsafe contracts so 'unsafe' is applied only to the variables that require it (avoids CS9365 where a safe interface event would be made illegally unsafe)
- Drop the statement-wrap branch that ignored requiresExpression, so using/await/directive statements that need an unsafe(...) expression fall back to whole-body wrapping instead of an unsafe { } block that would change scope
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:11

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
When splitting a multi-variable event field, keep each variable node's trivia (e.g. inline comments like 'event Action E1 /* keep */, E2;') instead of stripping it; only the outer declaration trivia is normalized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:31

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee40608f-4e94-47e1-8763-843d916bab40
CopilotAI review requested due to automatic review settings July 17, 2026 13:03

CopilotAI 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

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

@github-actions

github-actionsBot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
"version": 5,
"last_dispatched_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_reviewed_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_recorded_worker_run_id": "29686415576",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"review_id": 4730737351
}
]
}

@github-actionsgithub-actionsBot 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.

Holistic Review

Motivation: The change implements automated migration tooling (analyzer IL5005/IL5006 plus code-fix providers) for the unsafe-evolution C# language proposal, letting the runtime repo mechanically strip legacy/invalid unsafe modifiers and add required unsafe blocks, expressions, and <safety> documentation. This is a real, well-motivated need: migrating a large codebase to the new memory-safety rules by hand would be impractical and error-prone.

Approach: The approach is sound and appropriately scoped. All new code is wrapped in #if DEBUG and gated behind the opt-in EnableUnsafeMigration MSBuild property, so it adds no shipping public API surface and cannot affect production builds or normal analyzer runs. It reuses the existing ILLink analyzer/code-fix infrastructure (diagnostic IDs, categories, resx strings, CSharpCodeFixVerifier) rather than inventing parallel machinery, and the IL5005/5006 IDs and Unsafe category are correctly slotted into DiagnosticId/DiagnosticCategory without disturbing the existing AOT range mapping. The logic handles a broad matrix of C# constructs (partial members, interfaces, accessors, stackalloc, async, top-level statements, scoped refs), which the extensive test file exercises.

Summary: ⚠️ Needs Human Review. The code is well-structured, self-contained, and thoroughly tested for a migration tool, and it carries essentially zero production risk because it is DEBUG-only and opt-in. I have no blocking findings. However, this is a large (2,661-line) body of intricate syntax/semantic-model manipulation whose correctness is best judged by the ILLink area owners against the actual proposal semantics and by running it over real libraries — a level of validation this static review cannot fully substitute for. Human reviewers should focus on the correctness of the modifier-removal/retention heuristics (interop, overrides, interface contracts, pointer-in-signature detection) and the <safety>/skiplocalsinit handling, and confirm the tool's output on representative source is what the proposal intends.


Detailed Findings

✅ Scope & Risk — Correctly isolated tooling change

Every new type and every touched enum/category/resx addition is under #if DEBUG and further gated by the EnableUnsafeMigration compiler-visible property (verified in Microsoft.NET.ILLink.Analyzers.props, MSBuildPropertyOptionNames.cs, and both code-fix providers' RegisterCodeFixesAsync). No ref/ assembly or shipping public API is introduced, so the API-approval gate does not apply. Production analyzer behavior is unchanged.

✅ Codebase Fit — Consistent with existing ILLink patterns

Diagnostic IDs (5005/5006), the new Unsafe category, and the resx entries follow existing conventions. The GetDiagnosticCategory switch inserts the two new IDs before the >= 3050 and <= 6000 => AOT arm so they resolve to Unsafe rather than being swallowed by the AOT range — this ordering is correct and covered by the UnsafeMigrationDiagnostics_HaveUnsafeCategoryWithoutHelpLinks test.

✅ Test Quality — Broad behavioral coverage

The new RequiresUnsafeCodeFixTests.cs adds ~44 focused test methods covering modifier removal/retention, whole-body vs. statement vs. expression wrapping, forward declarations, ref locals, async/yield exclusions, and the migration-disabled case. Using CSharpCodeFixVerifier with before/after sources is the right harness for code-fix validation. Note: I could not build or run these tests in this environment; the human reviewer should confirm they pass in CI.

💡 Brittle syntax-node detection (inline)

One low-severity, non-blocking maintainability note is left inline in UnsafeMigrationAnalysis.cs regarding the GetType().Name == "UnsafeExpressionSyntax" string check used to recognize the preview unsafe(...) expression node.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 192.5 AIC · ⌖ 12.1 AIC · ⊞ 10K

private static bool IsInUnsafeContext(SyntaxNode node)
=> node.AncestorsAndSelf().Any(static ancestor =>
ancestor is UnsafeStatementSyntax ||
ancestor.GetType().Name == "UnsafeExpressionSyntax" ||

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 uses a stringly-typed check (ancestor.GetType().Name == "UnsafeExpressionSyntax") to detect the preview unsafe(...) expression node, presumably because the strongly-typed syntax type isn't available in the referenced Roslyn version. That's a reasonable pragmatic workaround, but it is brittle: it silently breaks if the node type is renamed, and it won't match derived/wrapped forms. Consider adding a short comment explaining why the reflection-by-name check is used and, if feasible, guarding it with a SyntaxKind check so the intent survives a Roslyn bump. Non-blocking given this is DEBUG-only tooling.

@EgorBoEgorBo closed this Jul 20, 2026
@EgorBo

Copy link
Copy Markdown
MemberAuthor

decided to split into many small analyzers/fixers and the first batch is in #131002

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

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@EgorBo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add unsafe evolution migration code fixers - #130611

Closed
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3
Closed

Add unsafe evolution migration code fixers#130611
EgorBo wants to merge 11 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-3

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Spec: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md

What it does:

  • Adds IL5005 to remove legacy or invalid unsafe modifiers.
    • Type declarations such as classes, structs, interfaces, records, record structs, and enums.
    • Delegate declarations, static constructors, and destructors.
    • Methods, instance constructors, operators, conversion operators, local functions, properties, indexers, and events when:
      • no pointer or function pointer in the signature (we keep those caller-unsafe)
      • no <safety> comment. It's a sign that this unsafe was from unsafe-v1.
    • Keeps unsafe when required by interop, partial members, overrides, or interface contracts.
  • Adds IL5006 to add required unsafe blocks, expressions, and contract modifiers.
  • Adds unsafe and <safety> placeholders to unannotated interop declarations.
  • Covers partial members, interfaces, accessors, stackalloc, async code, top-level statements, and scoped refs.

How to run it from the repo root:

C:\prj\dotnet-latest\dotnet.cmd build .\src\tools\illink\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj -c Debug --no-restore
$env:EnableUnsafeMigration='true'
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5005 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd format analyzers .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0--diagnostics IL5006 --severity info --no-restore
C:\prj\dotnet-latest\dotnet.cmd build .\src\libraries\System.Text.Json\src\System.Text.Json.csproj -f net11.0-c Debug --no-restore -p:BuildProjectReferences=false -p:EnableUnsafeMigration=true

I was using the latest daily SDK in C:\prj\dotnet-latest, not sure the bootstrapped one contains all the new C# features.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:23
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 13, 2026 09:23
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 13, 2026

CopilotAI 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 adds a DEBUG-only “unsafe evolution” migration analyzer + code fixers to ILLink, introducing two new IL* diagnostics (IL5005/IL5006) to (1) remove legacy/invalid unsafe modifiers and (2) migrate unsafe usages by adding explicit unsafe contexts and safety placeholders. It also wires up additional MSBuild properties and live-ILLink behavior to enable the migration experience, and adds/extends Roslyn analyzer/codefix tests.

Changes:

  • Add UnsafeMigrationAnalyzer + UnsafeMigrationAnalysis to detect modifier removals, required declaration updates, and unsafe-operation sites (behind #if DEBUG).
  • Add two new code fix providers (modifier removal + usage migration) and supporting document/AST rewrite helpers (behind #if DEBUG).
  • Add new diagnostic strings/IDs and update build/targets/tests to enable and validate the migration workflow.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csAdds test helpers and extensive test coverage for IL5005/IL5006 codefix behavior.
src/tools/illink/src/ILLink.Shared/SharedStrings.resxAdds user-facing title/message strings for the new unsafe migration diagnostics.
src/tools/illink/src/ILLink.Shared/DiagnosticId.csAdds DEBUG-only diagnostic IDs and introduces an “Unsafe” category range mapping.
src/tools/illink/src/ILLink.Shared/DiagnosticCategory.csAdds DEBUG-only Unsafe category constant.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalyzer.csNew analyzer that reports file-level migration diagnostics when migration is enabled.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.csNew analysis engine for modifier removals, contract/declaration updates, and unsafe operation locations.
src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.csAdds DEBUG-only MSBuild property names used to toggle unsafe migration behavior.
src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.propsExposes additional compiler-visible MSBuild properties used by the migration flow.
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.csNew code fix provider that migrates unsafe usage sites (wrap blocks/expressions, etc.).
src/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.csNew document fixer implementing the migration transformations and heuristics.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierMigrationCodeFixProvider.csNew code fix provider that removes legacy unsafe modifiers when eligible.
src/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.csNew shared helpers for adding unsafe contexts and safety documentation.
src/tools/illink/src/ILLink.CodeFix/Resources.resxAdds code fix titles for the new migration fixers.
eng/liveILLink.targetsUpdates live-ILLink wiring to support enabling unsafe migration and design-time analyzer referencing.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 09:36

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:00

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageMigrationCodeFixProvider.cs Outdated
Comment threadeng/liveILLink.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3ccf7c6-8a32-4c31-9e31-12f0f1d2a026
CopilotAI review requested due to automatic review settings July 13, 2026 10:38

CopilotAI 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

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

CopilotAI review requested due to automatic review settings July 13, 2026 18:12

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
EgorBoand others added 2 commits July 16, 2026 13:39
- Parse unsafe(...) with explicit preview + updated-memory-safety-rules options
- Use SyntaxFactory elastic newline trivia instead of hardcoded CRLF literals
- Remove redundant single-arm switch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 13:08

CopilotAI 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

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

Comment threadsrc/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationAnalysis.cs Outdated
The IL5006 document fixer could emit code that fails to compile:
- unsafe(...) expressions at statement/void/assignment-target positions (only valid as a value sub-expression) now fall back to unsafe { } blocks
- out-var/pattern-var declarations (Foo(out int y);) are forward-declared before the unsafe block so they stay in scope
- out-vars in a local declaration initializer (int x = Foo(out int y);) fall back to the unsafe(...) expression form, keeping the variable in scope
- only interior (not leading/trailing) directives block wrapping a statement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 15:48

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeUsageDocumentFixer.cs Outdated
- Split multi-variable event field declarations with mixed safe/unsafe contracts so 'unsafe' is applied only to the variables that require it (avoids CS9365 where a safe interface event would be made illegally unsafe)
- Drop the statement-wrap branch that ignored requiresExpression, so using/await/directive statements that need an unsafe(...) expression fall back to whole-body wrapping instead of an unsafe { } block that would change scope
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:11

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/UnsafeCodeFixHelpers.cs Outdated
When splitting a multi-variable event field, keep each variable node's trivia (e.g. inline comments like 'event Action E1 /* keep */, E2;') instead of stripping it; only the outer declaration trivia is normalized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 826b613c-e6ee-4cc6-a6a7-b06038520aad
CopilotAI review requested due to automatic review settings July 16, 2026 17:31

CopilotAI 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

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee40608f-4e94-47e1-8763-843d916bab40
CopilotAI review requested due to automatic review settings July 17, 2026 13:03

CopilotAI 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

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

@github-actions

github-actionsBot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
"version": 5,
"last_dispatched_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_reviewed_commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "53a3837195f7d8e7d714306fab5679562b2261d2",
"last_recorded_worker_run_id": "29686415576",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "74d7482707561a9b391043d905abee5cc4c733c9",
"review_id": 4730737351
}
]
}

@github-actionsgithub-actionsBot 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.

Holistic Review

Motivation: The change implements automated migration tooling (analyzer IL5005/IL5006 plus code-fix providers) for the unsafe-evolution C# language proposal, letting the runtime repo mechanically strip legacy/invalid unsafe modifiers and add required unsafe blocks, expressions, and <safety> documentation. This is a real, well-motivated need: migrating a large codebase to the new memory-safety rules by hand would be impractical and error-prone.

Approach: The approach is sound and appropriately scoped. All new code is wrapped in #if DEBUG and gated behind the opt-in EnableUnsafeMigration MSBuild property, so it adds no shipping public API surface and cannot affect production builds or normal analyzer runs. It reuses the existing ILLink analyzer/code-fix infrastructure (diagnostic IDs, categories, resx strings, CSharpCodeFixVerifier) rather than inventing parallel machinery, and the IL5005/5006 IDs and Unsafe category are correctly slotted into DiagnosticId/DiagnosticCategory without disturbing the existing AOT range mapping. The logic handles a broad matrix of C# constructs (partial members, interfaces, accessors, stackalloc, async, top-level statements, scoped refs), which the extensive test file exercises.

Summary: ⚠️ Needs Human Review. The code is well-structured, self-contained, and thoroughly tested for a migration tool, and it carries essentially zero production risk because it is DEBUG-only and opt-in. I have no blocking findings. However, this is a large (2,661-line) body of intricate syntax/semantic-model manipulation whose correctness is best judged by the ILLink area owners against the actual proposal semantics and by running it over real libraries — a level of validation this static review cannot fully substitute for. Human reviewers should focus on the correctness of the modifier-removal/retention heuristics (interop, overrides, interface contracts, pointer-in-signature detection) and the <safety>/skiplocalsinit handling, and confirm the tool's output on representative source is what the proposal intends.


Detailed Findings

✅ Scope & Risk — Correctly isolated tooling change

Every new type and every touched enum/category/resx addition is under #if DEBUG and further gated by the EnableUnsafeMigration compiler-visible property (verified in Microsoft.NET.ILLink.Analyzers.props, MSBuildPropertyOptionNames.cs, and both code-fix providers' RegisterCodeFixesAsync). No ref/ assembly or shipping public API is introduced, so the API-approval gate does not apply. Production analyzer behavior is unchanged.

✅ Codebase Fit — Consistent with existing ILLink patterns

Diagnostic IDs (5005/5006), the new Unsafe category, and the resx entries follow existing conventions. The GetDiagnosticCategory switch inserts the two new IDs before the >= 3050 and <= 6000 => AOT arm so they resolve to Unsafe rather than being swallowed by the AOT range — this ordering is correct and covered by the UnsafeMigrationDiagnostics_HaveUnsafeCategoryWithoutHelpLinks test.

✅ Test Quality — Broad behavioral coverage

The new RequiresUnsafeCodeFixTests.cs adds ~44 focused test methods covering modifier removal/retention, whole-body vs. statement vs. expression wrapping, forward declarations, ref locals, async/yield exclusions, and the migration-disabled case. Using CSharpCodeFixVerifier with before/after sources is the right harness for code-fix validation. Note: I could not build or run these tests in this environment; the human reviewer should confirm they pass in CI.

💡 Brittle syntax-node detection (inline)

One low-severity, non-blocking maintainability note is left inline in UnsafeMigrationAnalysis.cs regarding the GetType().Name == "UnsafeExpressionSyntax" string check used to recognize the preview unsafe(...) expression node.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 192.5 AIC · ⌖ 12.1 AIC · ⊞ 10K

private static bool IsInUnsafeContext(SyntaxNode node)
=> node.AncestorsAndSelf().Any(static ancestor =>
ancestor is UnsafeStatementSyntax ||
ancestor.GetType().Name == "UnsafeExpressionSyntax" ||

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 uses a stringly-typed check (ancestor.GetType().Name == "UnsafeExpressionSyntax") to detect the preview unsafe(...) expression node, presumably because the strongly-typed syntax type isn't available in the referenced Roslyn version. That's a reasonable pragmatic workaround, but it is brittle: it silently breaks if the node type is renamed, and it won't match derived/wrapped forms. Consider adding a short comment explaining why the reflection-by-name check is used and, if feasible, guarding it with a SyntaxKind check so the intent survives a Roslyn bump. Non-blocking given this is DEBUG-only tooling.

@EgorBoEgorBo closed this Jul 20, 2026
@EgorBo

Copy link
Copy Markdown
MemberAuthor

decided to split into many small analyzers/fixers and the first batch is in #131002

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

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@EgorBo