Add unsafe context migration code fixes - #131337

Closed
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups
Closed

Add unsafe context migration code fixes#131337
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Follow-up to #131002

This PR adds the remaining code fixers (along with #131245) that assist in migrating to the new unsafe-v2 rules (unsafe evolution). The goals are:

  • Code fixers should be idempotent as migrations to the new rules could be incremental.
  • Code fixers should rely on existing Roslyn diagnostics rather than independently rediscovering unsafe operations.

New code fixers added in this PR:

  1. [fixer] AddUnsafeContextCodeFixProvider fixes CS9360, CS9361, CS9362, CS9363, and CS9376 by introducing an unsafe context around the compiler-reported operation.

    The fixer prefers an unsafe { /* ... */ } statement containing a // SAFETY: Audit comment. When a statement would change scope, lifetime, control flow, or otherwise be invalid, it uses an unsafe(/* SAFETY: Audit */ expression) expression instead.

    Local declarations are split when that preserves semantics. In particular, stackalloc-to-span declarations are rewritten with a scoped forward declaration:

    scoped Span<byte>buffer;
    unsafe
    {// SAFETY: Auditbuffer=stackallocbyte[10];}

    The fixer also handles constructor initializers, using aliases, expression-bodied members, catch filters, async/iterator restrictions, directives, top-level statements, scoped refs, implicit conversions, and generated disposal/enumeration operations. It declines to offer a fix when no semantics-preserving automated transformation is available.

  2. [fixer] SynchronizeUnsafeContractCodeFixProvider fixes Roslyn's unsafe-to-safe contract mismatch diagnostics CS9364, CS9365, and CS9366, along with partial modifier mismatches CS0764 and CS9390.

    By the time this fixer runs, removable caller-unsafe modifiers have already been handled, so surviving unsafe contracts are propagated through source base members, interface declarations, overrides, implementations, and partial declarations. A pure safe partial mismatch defaults to unsafe, since safe is not currently valid on the non-extern partial declaration.

    The fixer does not add a new diagnostic for the opposite direction when the compiler accepts a safe implementation of an unsafe contract.

  3. The old (added long ago by Andy) RequiresUnsafeCodeFixProvider is removed. It only handled CS9362, could change caller contracts by marking parent members unsafe, and did not preserve scope and lifetime reliably.

Diagnostics IDs

Just for reference:

  • CS9360 [Roslyn] - An unsafe operation may only be used in an unsafe context.
  • CS9361 [Roslyn] - A stackalloc expression without an initializer inside [SkipLocalsInit] may only be used in an unsafe context.
  • CS9362 [Roslyn] - A member marked unsafe must be used in an unsafe context.
  • CS9363 [Roslyn] - A member with pointers in its signature must be used in an unsafe context.
  • CS9376 [Roslyn] - An unsafe context is required when an unsafe constructor satisfies a new() constraint.
  • CS9364 [Roslyn] - An unsafe member cannot override a safe member.
  • CS9365 [Roslyn] - An unsafe member cannot implicitly implement a safe member.
  • CS9366 [Roslyn] - An unsafe member cannot explicitly implement a safe member.
  • CS0764 [Roslyn] - Both partial member declarations must be unsafe, or neither may be unsafe.
  • CS9390 [Roslyn] - Both partial member declarations must be marked safe, or neither may be marked safe.

Once this and #131245 are in, we should be able to perform a migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:00
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 24, 2026
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

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 expands the ILLink Roslyn analyzer/code-fix test infrastructure with new (DEBUG-only) “unsafe-v2” migration code fixers: one that introduces a minimal unsafe context for compiler-reported unsafe usages, and another that propagates intentional unsafe contracts across partials/overrides/interface implementations. It also removes the prior RequiresUnsafeCodeFixProvider and its tests/resources.

Changes:

  • Add AddUnsafeContextCodeFixProvider and a comprehensive test suite covering many syntax positions and compiler diagnostics.
  • Add SynchronizeUnsafeContractCodeFixProvider, shared contract helpers, and tests for contract propagation scenarios.
  • Remove the legacy RequiresUnsafeCodeFixProvider and its associated tests, replacing the user-facing resource strings accordingly.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMigrationTestHelpers.csMinor formatting update in shared test setup for unsafe-v2 scenarios.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/SynchronizeUnsafeContractCodeFixTests.csAdds new tests validating unsafe contract propagation behavior.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csRemoves tests for the deprecated legacy fixer.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/AddUnsafeContextCodeFixTests.csAdds new tests validating unsafe-context insertion across many code shapes.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationSyntaxHelpers.csAdds GetSafeModifier helper for safe→unsafe token replacement scenarios.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeContractHelpers.csIntroduces shared symbol/syntax helpers for unsafe contract propagation logic.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierCodeFixHelpers.csAdds SetUnsafeModifierAsync, adjusts insertion ordering for partial, and exposes WithModifiers.
src/tools/illink/src/ILLink.CodeFix/SynchronizeUnsafeContractCodeFixProvider.csNew code fix provider that computes a contract closure and applies unsafe propagation edits.
src/tools/illink/src/ILLink.CodeFix/Resources.resxReplaces removed fixer title and adds titles for the new code fixers.
src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.csRemoves the deprecated legacy fixer implementation.
src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csprojLinks in the new shared UnsafeContractHelpers.cs for code fix usage.
src/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.csNew code fix provider implementing minimal unsafe statement/expression introduction logic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:33

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 12 out of 12 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.cs Outdated
Roslyn does not treat an unsafe expression as an unsafe context for property
and indexer accessors or for method group conversions, because those are bound
by the enclosing binder. The fixer used to wrap such operations anyway, which
left the diagnostic in place and nested another unsafe expression on every
later pass. It now writes an explicit cast inside the unsafe expression for
those operations, declines the fix when no cast can express it (ref-returning
accessors, unspeakable types), and never re-wraps an expression that is already
inside an unsafe expression. Assignment targets, increments, and deconstruction
right sides are handled as statements instead.
Modifiers that the fixers add now come with a <safety>TODO: Audit</safety>
stub. Without it IL5005 removed the modifier that had just been added, so a
second migration pass undid the first one.
Contract propagation no longer marks sibling overrides and implementations: a
safe member may override or implement a caller-unsafe one, so widening them
only grew the audit surface. Replacing an explicit safe modifier is offered
under its own title because it discards a deliberate audit.
Also merges a new unsafe region with adjacent generated regions, registers the
fixes for every diagnostic in the context, and hardens top level statement
replacement, statement list access, and the identifier fallback used when
expanding a statement range.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 20:29

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/UnsafeModifierCodeFixHelpers.cs Outdated
The unsafe expression template is now parsed with the parse options of the
document being fixed instead of a fixed set, so the generated syntax always
matches the language mode the project compiles with, and the fix is declined
when that mode cannot express an unsafe expression. The template also carries
the syntax kind used to detect an enclosing unsafe expression, which removes
the static probe that guessed it.
Generated safety documentation reuses the line ending the file already uses.
Member declarations carry their indentation but not the preceding line break,
so the previous fallback emitted a carriage return into files that use line
feeds only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 21:53

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.

@EgorBo
EgorBo marked this pull request as ready for review July 25, 2026 09:38
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 25, 2026 09:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

It was split into multiple PRs (#131451)

@EgorBoEgorBo closed this Aug 1, 2026
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 context migration code fixes - #131337

Closed
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups
Closed

Add unsafe context migration code fixes#131337
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Follow-up to #131002

This PR adds the remaining code fixers (along with #131245) that assist in migrating to the new unsafe-v2 rules (unsafe evolution). The goals are:

  • Code fixers should be idempotent as migrations to the new rules could be incremental.
  • Code fixers should rely on existing Roslyn diagnostics rather than independently rediscovering unsafe operations.

New code fixers added in this PR:

  1. [fixer] AddUnsafeContextCodeFixProvider fixes CS9360, CS9361, CS9362, CS9363, and CS9376 by introducing an unsafe context around the compiler-reported operation.

    The fixer prefers an unsafe { /* ... */ } statement containing a // SAFETY: Audit comment. When a statement would change scope, lifetime, control flow, or otherwise be invalid, it uses an unsafe(/* SAFETY: Audit */ expression) expression instead.

    Local declarations are split when that preserves semantics. In particular, stackalloc-to-span declarations are rewritten with a scoped forward declaration:

    scoped Span<byte>buffer;
    unsafe
    {// SAFETY: Auditbuffer=stackallocbyte[10];}

    The fixer also handles constructor initializers, using aliases, expression-bodied members, catch filters, async/iterator restrictions, directives, top-level statements, scoped refs, implicit conversions, and generated disposal/enumeration operations. It declines to offer a fix when no semantics-preserving automated transformation is available.

  2. [fixer] SynchronizeUnsafeContractCodeFixProvider fixes Roslyn's unsafe-to-safe contract mismatch diagnostics CS9364, CS9365, and CS9366, along with partial modifier mismatches CS0764 and CS9390.

    By the time this fixer runs, removable caller-unsafe modifiers have already been handled, so surviving unsafe contracts are propagated through source base members, interface declarations, overrides, implementations, and partial declarations. A pure safe partial mismatch defaults to unsafe, since safe is not currently valid on the non-extern partial declaration.

    The fixer does not add a new diagnostic for the opposite direction when the compiler accepts a safe implementation of an unsafe contract.

  3. The old (added long ago by Andy) RequiresUnsafeCodeFixProvider is removed. It only handled CS9362, could change caller contracts by marking parent members unsafe, and did not preserve scope and lifetime reliably.

Diagnostics IDs

Just for reference:

  • CS9360 [Roslyn] - An unsafe operation may only be used in an unsafe context.
  • CS9361 [Roslyn] - A stackalloc expression without an initializer inside [SkipLocalsInit] may only be used in an unsafe context.
  • CS9362 [Roslyn] - A member marked unsafe must be used in an unsafe context.
  • CS9363 [Roslyn] - A member with pointers in its signature must be used in an unsafe context.
  • CS9376 [Roslyn] - An unsafe context is required when an unsafe constructor satisfies a new() constraint.
  • CS9364 [Roslyn] - An unsafe member cannot override a safe member.
  • CS9365 [Roslyn] - An unsafe member cannot implicitly implement a safe member.
  • CS9366 [Roslyn] - An unsafe member cannot explicitly implement a safe member.
  • CS0764 [Roslyn] - Both partial member declarations must be unsafe, or neither may be unsafe.
  • CS9390 [Roslyn] - Both partial member declarations must be marked safe, or neither may be marked safe.

Once this and #131245 are in, we should be able to perform a migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:00
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 24, 2026
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

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 expands the ILLink Roslyn analyzer/code-fix test infrastructure with new (DEBUG-only) “unsafe-v2” migration code fixers: one that introduces a minimal unsafe context for compiler-reported unsafe usages, and another that propagates intentional unsafe contracts across partials/overrides/interface implementations. It also removes the prior RequiresUnsafeCodeFixProvider and its tests/resources.

Changes:

  • Add AddUnsafeContextCodeFixProvider and a comprehensive test suite covering many syntax positions and compiler diagnostics.
  • Add SynchronizeUnsafeContractCodeFixProvider, shared contract helpers, and tests for contract propagation scenarios.
  • Remove the legacy RequiresUnsafeCodeFixProvider and its associated tests, replacing the user-facing resource strings accordingly.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMigrationTestHelpers.csMinor formatting update in shared test setup for unsafe-v2 scenarios.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/SynchronizeUnsafeContractCodeFixTests.csAdds new tests validating unsafe contract propagation behavior.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csRemoves tests for the deprecated legacy fixer.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/AddUnsafeContextCodeFixTests.csAdds new tests validating unsafe-context insertion across many code shapes.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationSyntaxHelpers.csAdds GetSafeModifier helper for safe→unsafe token replacement scenarios.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeContractHelpers.csIntroduces shared symbol/syntax helpers for unsafe contract propagation logic.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierCodeFixHelpers.csAdds SetUnsafeModifierAsync, adjusts insertion ordering for partial, and exposes WithModifiers.
src/tools/illink/src/ILLink.CodeFix/SynchronizeUnsafeContractCodeFixProvider.csNew code fix provider that computes a contract closure and applies unsafe propagation edits.
src/tools/illink/src/ILLink.CodeFix/Resources.resxReplaces removed fixer title and adds titles for the new code fixers.
src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.csRemoves the deprecated legacy fixer implementation.
src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csprojLinks in the new shared UnsafeContractHelpers.cs for code fix usage.
src/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.csNew code fix provider implementing minimal unsafe statement/expression introduction logic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:33

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 12 out of 12 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.cs Outdated
Roslyn does not treat an unsafe expression as an unsafe context for property
and indexer accessors or for method group conversions, because those are bound
by the enclosing binder. The fixer used to wrap such operations anyway, which
left the diagnostic in place and nested another unsafe expression on every
later pass. It now writes an explicit cast inside the unsafe expression for
those operations, declines the fix when no cast can express it (ref-returning
accessors, unspeakable types), and never re-wraps an expression that is already
inside an unsafe expression. Assignment targets, increments, and deconstruction
right sides are handled as statements instead.
Modifiers that the fixers add now come with a <safety>TODO: Audit</safety>
stub. Without it IL5005 removed the modifier that had just been added, so a
second migration pass undid the first one.
Contract propagation no longer marks sibling overrides and implementations: a
safe member may override or implement a caller-unsafe one, so widening them
only grew the audit surface. Replacing an explicit safe modifier is offered
under its own title because it discards a deliberate audit.
Also merges a new unsafe region with adjacent generated regions, registers the
fixes for every diagnostic in the context, and hardens top level statement
replacement, statement list access, and the identifier fallback used when
expanding a statement range.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 20:29

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/UnsafeModifierCodeFixHelpers.cs Outdated
The unsafe expression template is now parsed with the parse options of the
document being fixed instead of a fixed set, so the generated syntax always
matches the language mode the project compiles with, and the fix is declined
when that mode cannot express an unsafe expression. The template also carries
the syntax kind used to detect an enclosing unsafe expression, which removes
the static probe that guessed it.
Generated safety documentation reuses the line ending the file already uses.
Member declarations carry their indentation but not the preceding line break,
so the previous fallback emitted a carriage return into files that use line
feeds only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 21:53

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.

@EgorBo
EgorBo marked this pull request as ready for review July 25, 2026 09:38
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 25, 2026 09:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

It was split into multiple PRs (#131451)

@EgorBoEgorBo closed this Aug 1, 2026
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 context migration code fixes - #131337

Closed
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups
Closed

Add unsafe context migration code fixes#131337
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Follow-up to #131002

This PR adds the remaining code fixers (along with #131245) that assist in migrating to the new unsafe-v2 rules (unsafe evolution). The goals are:

  • Code fixers should be idempotent as migrations to the new rules could be incremental.
  • Code fixers should rely on existing Roslyn diagnostics rather than independently rediscovering unsafe operations.

New code fixers added in this PR:

  1. [fixer] AddUnsafeContextCodeFixProvider fixes CS9360, CS9361, CS9362, CS9363, and CS9376 by introducing an unsafe context around the compiler-reported operation.

    The fixer prefers an unsafe { /* ... */ } statement containing a // SAFETY: Audit comment. When a statement would change scope, lifetime, control flow, or otherwise be invalid, it uses an unsafe(/* SAFETY: Audit */ expression) expression instead.

    Local declarations are split when that preserves semantics. In particular, stackalloc-to-span declarations are rewritten with a scoped forward declaration:

    scoped Span<byte>buffer;
    unsafe
    {// SAFETY: Auditbuffer=stackallocbyte[10];}

    The fixer also handles constructor initializers, using aliases, expression-bodied members, catch filters, async/iterator restrictions, directives, top-level statements, scoped refs, implicit conversions, and generated disposal/enumeration operations. It declines to offer a fix when no semantics-preserving automated transformation is available.

  2. [fixer] SynchronizeUnsafeContractCodeFixProvider fixes Roslyn's unsafe-to-safe contract mismatch diagnostics CS9364, CS9365, and CS9366, along with partial modifier mismatches CS0764 and CS9390.

    By the time this fixer runs, removable caller-unsafe modifiers have already been handled, so surviving unsafe contracts are propagated through source base members, interface declarations, overrides, implementations, and partial declarations. A pure safe partial mismatch defaults to unsafe, since safe is not currently valid on the non-extern partial declaration.

    The fixer does not add a new diagnostic for the opposite direction when the compiler accepts a safe implementation of an unsafe contract.

  3. The old (added long ago by Andy) RequiresUnsafeCodeFixProvider is removed. It only handled CS9362, could change caller contracts by marking parent members unsafe, and did not preserve scope and lifetime reliably.

Diagnostics IDs

Just for reference:

  • CS9360 [Roslyn] - An unsafe operation may only be used in an unsafe context.
  • CS9361 [Roslyn] - A stackalloc expression without an initializer inside [SkipLocalsInit] may only be used in an unsafe context.
  • CS9362 [Roslyn] - A member marked unsafe must be used in an unsafe context.
  • CS9363 [Roslyn] - A member with pointers in its signature must be used in an unsafe context.
  • CS9376 [Roslyn] - An unsafe context is required when an unsafe constructor satisfies a new() constraint.
  • CS9364 [Roslyn] - An unsafe member cannot override a safe member.
  • CS9365 [Roslyn] - An unsafe member cannot implicitly implement a safe member.
  • CS9366 [Roslyn] - An unsafe member cannot explicitly implement a safe member.
  • CS0764 [Roslyn] - Both partial member declarations must be unsafe, or neither may be unsafe.
  • CS9390 [Roslyn] - Both partial member declarations must be marked safe, or neither may be marked safe.

Once this and #131245 are in, we should be able to perform a migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:00
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 24, 2026
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

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 expands the ILLink Roslyn analyzer/code-fix test infrastructure with new (DEBUG-only) “unsafe-v2” migration code fixers: one that introduces a minimal unsafe context for compiler-reported unsafe usages, and another that propagates intentional unsafe contracts across partials/overrides/interface implementations. It also removes the prior RequiresUnsafeCodeFixProvider and its tests/resources.

Changes:

  • Add AddUnsafeContextCodeFixProvider and a comprehensive test suite covering many syntax positions and compiler diagnostics.
  • Add SynchronizeUnsafeContractCodeFixProvider, shared contract helpers, and tests for contract propagation scenarios.
  • Remove the legacy RequiresUnsafeCodeFixProvider and its associated tests, replacing the user-facing resource strings accordingly.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMigrationTestHelpers.csMinor formatting update in shared test setup for unsafe-v2 scenarios.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/SynchronizeUnsafeContractCodeFixTests.csAdds new tests validating unsafe contract propagation behavior.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csRemoves tests for the deprecated legacy fixer.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/AddUnsafeContextCodeFixTests.csAdds new tests validating unsafe-context insertion across many code shapes.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationSyntaxHelpers.csAdds GetSafeModifier helper for safe→unsafe token replacement scenarios.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeContractHelpers.csIntroduces shared symbol/syntax helpers for unsafe contract propagation logic.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierCodeFixHelpers.csAdds SetUnsafeModifierAsync, adjusts insertion ordering for partial, and exposes WithModifiers.
src/tools/illink/src/ILLink.CodeFix/SynchronizeUnsafeContractCodeFixProvider.csNew code fix provider that computes a contract closure and applies unsafe propagation edits.
src/tools/illink/src/ILLink.CodeFix/Resources.resxReplaces removed fixer title and adds titles for the new code fixers.
src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.csRemoves the deprecated legacy fixer implementation.
src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csprojLinks in the new shared UnsafeContractHelpers.cs for code fix usage.
src/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.csNew code fix provider implementing minimal unsafe statement/expression introduction logic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:33

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 12 out of 12 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.cs Outdated
Roslyn does not treat an unsafe expression as an unsafe context for property
and indexer accessors or for method group conversions, because those are bound
by the enclosing binder. The fixer used to wrap such operations anyway, which
left the diagnostic in place and nested another unsafe expression on every
later pass. It now writes an explicit cast inside the unsafe expression for
those operations, declines the fix when no cast can express it (ref-returning
accessors, unspeakable types), and never re-wraps an expression that is already
inside an unsafe expression. Assignment targets, increments, and deconstruction
right sides are handled as statements instead.
Modifiers that the fixers add now come with a <safety>TODO: Audit</safety>
stub. Without it IL5005 removed the modifier that had just been added, so a
second migration pass undid the first one.
Contract propagation no longer marks sibling overrides and implementations: a
safe member may override or implement a caller-unsafe one, so widening them
only grew the audit surface. Replacing an explicit safe modifier is offered
under its own title because it discards a deliberate audit.
Also merges a new unsafe region with adjacent generated regions, registers the
fixes for every diagnostic in the context, and hardens top level statement
replacement, statement list access, and the identifier fallback used when
expanding a statement range.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 20:29

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/UnsafeModifierCodeFixHelpers.cs Outdated
The unsafe expression template is now parsed with the parse options of the
document being fixed instead of a fixed set, so the generated syntax always
matches the language mode the project compiles with, and the fix is declined
when that mode cannot express an unsafe expression. The template also carries
the syntax kind used to detect an enclosing unsafe expression, which removes
the static probe that guessed it.
Generated safety documentation reuses the line ending the file already uses.
Member declarations carry their indentation but not the preceding line break,
so the previous fallback emitted a carriage return into files that use line
feeds only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 21:53

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.

@EgorBo
EgorBo marked this pull request as ready for review July 25, 2026 09:38
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 25, 2026 09:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

It was split into multiple PRs (#131451)

@EgorBoEgorBo closed this Aug 1, 2026
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 context migration code fixes - #131337

Closed
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups
Closed

Add unsafe context migration code fixes#131337
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Follow-up to #131002

This PR adds the remaining code fixers (along with #131245) that assist in migrating to the new unsafe-v2 rules (unsafe evolution). The goals are:

  • Code fixers should be idempotent as migrations to the new rules could be incremental.
  • Code fixers should rely on existing Roslyn diagnostics rather than independently rediscovering unsafe operations.

New code fixers added in this PR:

  1. [fixer] AddUnsafeContextCodeFixProvider fixes CS9360, CS9361, CS9362, CS9363, and CS9376 by introducing an unsafe context around the compiler-reported operation.

    The fixer prefers an unsafe { /* ... */ } statement containing a // SAFETY: Audit comment. When a statement would change scope, lifetime, control flow, or otherwise be invalid, it uses an unsafe(/* SAFETY: Audit */ expression) expression instead.

    Local declarations are split when that preserves semantics. In particular, stackalloc-to-span declarations are rewritten with a scoped forward declaration:

    scoped Span<byte>buffer;
    unsafe
    {// SAFETY: Auditbuffer=stackallocbyte[10];}

    The fixer also handles constructor initializers, using aliases, expression-bodied members, catch filters, async/iterator restrictions, directives, top-level statements, scoped refs, implicit conversions, and generated disposal/enumeration operations. It declines to offer a fix when no semantics-preserving automated transformation is available.

  2. [fixer] SynchronizeUnsafeContractCodeFixProvider fixes Roslyn's unsafe-to-safe contract mismatch diagnostics CS9364, CS9365, and CS9366, along with partial modifier mismatches CS0764 and CS9390.

    By the time this fixer runs, removable caller-unsafe modifiers have already been handled, so surviving unsafe contracts are propagated through source base members, interface declarations, overrides, implementations, and partial declarations. A pure safe partial mismatch defaults to unsafe, since safe is not currently valid on the non-extern partial declaration.

    The fixer does not add a new diagnostic for the opposite direction when the compiler accepts a safe implementation of an unsafe contract.

  3. The old (added long ago by Andy) RequiresUnsafeCodeFixProvider is removed. It only handled CS9362, could change caller contracts by marking parent members unsafe, and did not preserve scope and lifetime reliably.

Diagnostics IDs

Just for reference:

  • CS9360 [Roslyn] - An unsafe operation may only be used in an unsafe context.
  • CS9361 [Roslyn] - A stackalloc expression without an initializer inside [SkipLocalsInit] may only be used in an unsafe context.
  • CS9362 [Roslyn] - A member marked unsafe must be used in an unsafe context.
  • CS9363 [Roslyn] - A member with pointers in its signature must be used in an unsafe context.
  • CS9376 [Roslyn] - An unsafe context is required when an unsafe constructor satisfies a new() constraint.
  • CS9364 [Roslyn] - An unsafe member cannot override a safe member.
  • CS9365 [Roslyn] - An unsafe member cannot implicitly implement a safe member.
  • CS9366 [Roslyn] - An unsafe member cannot explicitly implement a safe member.
  • CS0764 [Roslyn] - Both partial member declarations must be unsafe, or neither may be unsafe.
  • CS9390 [Roslyn] - Both partial member declarations must be marked safe, or neither may be marked safe.

Once this and #131245 are in, we should be able to perform a migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:00
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 24, 2026
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

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 expands the ILLink Roslyn analyzer/code-fix test infrastructure with new (DEBUG-only) “unsafe-v2” migration code fixers: one that introduces a minimal unsafe context for compiler-reported unsafe usages, and another that propagates intentional unsafe contracts across partials/overrides/interface implementations. It also removes the prior RequiresUnsafeCodeFixProvider and its tests/resources.

Changes:

  • Add AddUnsafeContextCodeFixProvider and a comprehensive test suite covering many syntax positions and compiler diagnostics.
  • Add SynchronizeUnsafeContractCodeFixProvider, shared contract helpers, and tests for contract propagation scenarios.
  • Remove the legacy RequiresUnsafeCodeFixProvider and its associated tests, replacing the user-facing resource strings accordingly.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMigrationTestHelpers.csMinor formatting update in shared test setup for unsafe-v2 scenarios.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/SynchronizeUnsafeContractCodeFixTests.csAdds new tests validating unsafe contract propagation behavior.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csRemoves tests for the deprecated legacy fixer.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/AddUnsafeContextCodeFixTests.csAdds new tests validating unsafe-context insertion across many code shapes.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationSyntaxHelpers.csAdds GetSafeModifier helper for safe→unsafe token replacement scenarios.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeContractHelpers.csIntroduces shared symbol/syntax helpers for unsafe contract propagation logic.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierCodeFixHelpers.csAdds SetUnsafeModifierAsync, adjusts insertion ordering for partial, and exposes WithModifiers.
src/tools/illink/src/ILLink.CodeFix/SynchronizeUnsafeContractCodeFixProvider.csNew code fix provider that computes a contract closure and applies unsafe propagation edits.
src/tools/illink/src/ILLink.CodeFix/Resources.resxReplaces removed fixer title and adds titles for the new code fixers.
src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.csRemoves the deprecated legacy fixer implementation.
src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csprojLinks in the new shared UnsafeContractHelpers.cs for code fix usage.
src/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.csNew code fix provider implementing minimal unsafe statement/expression introduction logic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:33

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 12 out of 12 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.cs Outdated
Roslyn does not treat an unsafe expression as an unsafe context for property
and indexer accessors or for method group conversions, because those are bound
by the enclosing binder. The fixer used to wrap such operations anyway, which
left the diagnostic in place and nested another unsafe expression on every
later pass. It now writes an explicit cast inside the unsafe expression for
those operations, declines the fix when no cast can express it (ref-returning
accessors, unspeakable types), and never re-wraps an expression that is already
inside an unsafe expression. Assignment targets, increments, and deconstruction
right sides are handled as statements instead.
Modifiers that the fixers add now come with a <safety>TODO: Audit</safety>
stub. Without it IL5005 removed the modifier that had just been added, so a
second migration pass undid the first one.
Contract propagation no longer marks sibling overrides and implementations: a
safe member may override or implement a caller-unsafe one, so widening them
only grew the audit surface. Replacing an explicit safe modifier is offered
under its own title because it discards a deliberate audit.
Also merges a new unsafe region with adjacent generated regions, registers the
fixes for every diagnostic in the context, and hardens top level statement
replacement, statement list access, and the identifier fallback used when
expanding a statement range.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 20:29

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/UnsafeModifierCodeFixHelpers.cs Outdated
The unsafe expression template is now parsed with the parse options of the
document being fixed instead of a fixed set, so the generated syntax always
matches the language mode the project compiles with, and the fix is declined
when that mode cannot express an unsafe expression. The template also carries
the syntax kind used to detect an enclosing unsafe expression, which removes
the static probe that guessed it.
Generated safety documentation reuses the line ending the file already uses.
Member declarations carry their indentation but not the preceding line break,
so the previous fallback emitted a carriage return into files that use line
feeds only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 21:53

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.

@EgorBo
EgorBo marked this pull request as ready for review July 25, 2026 09:38
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 25, 2026 09:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

It was split into multiple PRs (#131451)

@EgorBoEgorBo closed this Aug 1, 2026
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 context migration code fixes - #131337

Closed
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups
Closed

Add unsafe context migration code fixes#131337
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Follow-up to #131002

This PR adds the remaining code fixers (along with #131245) that assist in migrating to the new unsafe-v2 rules (unsafe evolution). The goals are:

  • Code fixers should be idempotent as migrations to the new rules could be incremental.
  • Code fixers should rely on existing Roslyn diagnostics rather than independently rediscovering unsafe operations.

New code fixers added in this PR:

  1. [fixer] AddUnsafeContextCodeFixProvider fixes CS9360, CS9361, CS9362, CS9363, and CS9376 by introducing an unsafe context around the compiler-reported operation.

    The fixer prefers an unsafe { /* ... */ } statement containing a // SAFETY: Audit comment. When a statement would change scope, lifetime, control flow, or otherwise be invalid, it uses an unsafe(/* SAFETY: Audit */ expression) expression instead.

    Local declarations are split when that preserves semantics. In particular, stackalloc-to-span declarations are rewritten with a scoped forward declaration:

    scoped Span<byte>buffer;
    unsafe
    {// SAFETY: Auditbuffer=stackallocbyte[10];}

    The fixer also handles constructor initializers, using aliases, expression-bodied members, catch filters, async/iterator restrictions, directives, top-level statements, scoped refs, implicit conversions, and generated disposal/enumeration operations. It declines to offer a fix when no semantics-preserving automated transformation is available.

  2. [fixer] SynchronizeUnsafeContractCodeFixProvider fixes Roslyn's unsafe-to-safe contract mismatch diagnostics CS9364, CS9365, and CS9366, along with partial modifier mismatches CS0764 and CS9390.

    By the time this fixer runs, removable caller-unsafe modifiers have already been handled, so surviving unsafe contracts are propagated through source base members, interface declarations, overrides, implementations, and partial declarations. A pure safe partial mismatch defaults to unsafe, since safe is not currently valid on the non-extern partial declaration.

    The fixer does not add a new diagnostic for the opposite direction when the compiler accepts a safe implementation of an unsafe contract.

  3. The old (added long ago by Andy) RequiresUnsafeCodeFixProvider is removed. It only handled CS9362, could change caller contracts by marking parent members unsafe, and did not preserve scope and lifetime reliably.

Diagnostics IDs

Just for reference:

  • CS9360 [Roslyn] - An unsafe operation may only be used in an unsafe context.
  • CS9361 [Roslyn] - A stackalloc expression without an initializer inside [SkipLocalsInit] may only be used in an unsafe context.
  • CS9362 [Roslyn] - A member marked unsafe must be used in an unsafe context.
  • CS9363 [Roslyn] - A member with pointers in its signature must be used in an unsafe context.
  • CS9376 [Roslyn] - An unsafe context is required when an unsafe constructor satisfies a new() constraint.
  • CS9364 [Roslyn] - An unsafe member cannot override a safe member.
  • CS9365 [Roslyn] - An unsafe member cannot implicitly implement a safe member.
  • CS9366 [Roslyn] - An unsafe member cannot explicitly implement a safe member.
  • CS0764 [Roslyn] - Both partial member declarations must be unsafe, or neither may be unsafe.
  • CS9390 [Roslyn] - Both partial member declarations must be marked safe, or neither may be marked safe.

Once this and #131245 are in, we should be able to perform a migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:00
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 24, 2026
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

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 expands the ILLink Roslyn analyzer/code-fix test infrastructure with new (DEBUG-only) “unsafe-v2” migration code fixers: one that introduces a minimal unsafe context for compiler-reported unsafe usages, and another that propagates intentional unsafe contracts across partials/overrides/interface implementations. It also removes the prior RequiresUnsafeCodeFixProvider and its tests/resources.

Changes:

  • Add AddUnsafeContextCodeFixProvider and a comprehensive test suite covering many syntax positions and compiler diagnostics.
  • Add SynchronizeUnsafeContractCodeFixProvider, shared contract helpers, and tests for contract propagation scenarios.
  • Remove the legacy RequiresUnsafeCodeFixProvider and its associated tests, replacing the user-facing resource strings accordingly.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMigrationTestHelpers.csMinor formatting update in shared test setup for unsafe-v2 scenarios.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/SynchronizeUnsafeContractCodeFixTests.csAdds new tests validating unsafe contract propagation behavior.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csRemoves tests for the deprecated legacy fixer.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/AddUnsafeContextCodeFixTests.csAdds new tests validating unsafe-context insertion across many code shapes.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationSyntaxHelpers.csAdds GetSafeModifier helper for safe→unsafe token replacement scenarios.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeContractHelpers.csIntroduces shared symbol/syntax helpers for unsafe contract propagation logic.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierCodeFixHelpers.csAdds SetUnsafeModifierAsync, adjusts insertion ordering for partial, and exposes WithModifiers.
src/tools/illink/src/ILLink.CodeFix/SynchronizeUnsafeContractCodeFixProvider.csNew code fix provider that computes a contract closure and applies unsafe propagation edits.
src/tools/illink/src/ILLink.CodeFix/Resources.resxReplaces removed fixer title and adds titles for the new code fixers.
src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.csRemoves the deprecated legacy fixer implementation.
src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csprojLinks in the new shared UnsafeContractHelpers.cs for code fix usage.
src/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.csNew code fix provider implementing minimal unsafe statement/expression introduction logic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:33

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 12 out of 12 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.cs Outdated
Roslyn does not treat an unsafe expression as an unsafe context for property
and indexer accessors or for method group conversions, because those are bound
by the enclosing binder. The fixer used to wrap such operations anyway, which
left the diagnostic in place and nested another unsafe expression on every
later pass. It now writes an explicit cast inside the unsafe expression for
those operations, declines the fix when no cast can express it (ref-returning
accessors, unspeakable types), and never re-wraps an expression that is already
inside an unsafe expression. Assignment targets, increments, and deconstruction
right sides are handled as statements instead.
Modifiers that the fixers add now come with a <safety>TODO: Audit</safety>
stub. Without it IL5005 removed the modifier that had just been added, so a
second migration pass undid the first one.
Contract propagation no longer marks sibling overrides and implementations: a
safe member may override or implement a caller-unsafe one, so widening them
only grew the audit surface. Replacing an explicit safe modifier is offered
under its own title because it discards a deliberate audit.
Also merges a new unsafe region with adjacent generated regions, registers the
fixes for every diagnostic in the context, and hardens top level statement
replacement, statement list access, and the identifier fallback used when
expanding a statement range.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 20:29

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/UnsafeModifierCodeFixHelpers.cs Outdated
The unsafe expression template is now parsed with the parse options of the
document being fixed instead of a fixed set, so the generated syntax always
matches the language mode the project compiles with, and the fix is declined
when that mode cannot express an unsafe expression. The template also carries
the syntax kind used to detect an enclosing unsafe expression, which removes
the static probe that guessed it.
Generated safety documentation reuses the line ending the file already uses.
Member declarations carry their indentation but not the preceding line break,
so the previous fallback emitted a carriage return into files that use line
feeds only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 21:53

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.

@EgorBo
EgorBo marked this pull request as ready for review July 25, 2026 09:38
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 25, 2026 09:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

It was split into multiple PRs (#131451)

@EgorBoEgorBo closed this Aug 1, 2026
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 context migration code fixes - #131337

Closed
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups
Closed

Add unsafe context migration code fixes#131337
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Follow-up to #131002

This PR adds the remaining code fixers (along with #131245) that assist in migrating to the new unsafe-v2 rules (unsafe evolution). The goals are:

  • Code fixers should be idempotent as migrations to the new rules could be incremental.
  • Code fixers should rely on existing Roslyn diagnostics rather than independently rediscovering unsafe operations.

New code fixers added in this PR:

  1. [fixer] AddUnsafeContextCodeFixProvider fixes CS9360, CS9361, CS9362, CS9363, and CS9376 by introducing an unsafe context around the compiler-reported operation.

    The fixer prefers an unsafe { /* ... */ } statement containing a // SAFETY: Audit comment. When a statement would change scope, lifetime, control flow, or otherwise be invalid, it uses an unsafe(/* SAFETY: Audit */ expression) expression instead.

    Local declarations are split when that preserves semantics. In particular, stackalloc-to-span declarations are rewritten with a scoped forward declaration:

    scoped Span<byte>buffer;
    unsafe
    {// SAFETY: Auditbuffer=stackallocbyte[10];}

    The fixer also handles constructor initializers, using aliases, expression-bodied members, catch filters, async/iterator restrictions, directives, top-level statements, scoped refs, implicit conversions, and generated disposal/enumeration operations. It declines to offer a fix when no semantics-preserving automated transformation is available.

  2. [fixer] SynchronizeUnsafeContractCodeFixProvider fixes Roslyn's unsafe-to-safe contract mismatch diagnostics CS9364, CS9365, and CS9366, along with partial modifier mismatches CS0764 and CS9390.

    By the time this fixer runs, removable caller-unsafe modifiers have already been handled, so surviving unsafe contracts are propagated through source base members, interface declarations, overrides, implementations, and partial declarations. A pure safe partial mismatch defaults to unsafe, since safe is not currently valid on the non-extern partial declaration.

    The fixer does not add a new diagnostic for the opposite direction when the compiler accepts a safe implementation of an unsafe contract.

  3. The old (added long ago by Andy) RequiresUnsafeCodeFixProvider is removed. It only handled CS9362, could change caller contracts by marking parent members unsafe, and did not preserve scope and lifetime reliably.

Diagnostics IDs

Just for reference:

  • CS9360 [Roslyn] - An unsafe operation may only be used in an unsafe context.
  • CS9361 [Roslyn] - A stackalloc expression without an initializer inside [SkipLocalsInit] may only be used in an unsafe context.
  • CS9362 [Roslyn] - A member marked unsafe must be used in an unsafe context.
  • CS9363 [Roslyn] - A member with pointers in its signature must be used in an unsafe context.
  • CS9376 [Roslyn] - An unsafe context is required when an unsafe constructor satisfies a new() constraint.
  • CS9364 [Roslyn] - An unsafe member cannot override a safe member.
  • CS9365 [Roslyn] - An unsafe member cannot implicitly implement a safe member.
  • CS9366 [Roslyn] - An unsafe member cannot explicitly implement a safe member.
  • CS0764 [Roslyn] - Both partial member declarations must be unsafe, or neither may be unsafe.
  • CS9390 [Roslyn] - Both partial member declarations must be marked safe, or neither may be marked safe.

Once this and #131245 are in, we should be able to perform a migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:00
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 24, 2026
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

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 expands the ILLink Roslyn analyzer/code-fix test infrastructure with new (DEBUG-only) “unsafe-v2” migration code fixers: one that introduces a minimal unsafe context for compiler-reported unsafe usages, and another that propagates intentional unsafe contracts across partials/overrides/interface implementations. It also removes the prior RequiresUnsafeCodeFixProvider and its tests/resources.

Changes:

  • Add AddUnsafeContextCodeFixProvider and a comprehensive test suite covering many syntax positions and compiler diagnostics.
  • Add SynchronizeUnsafeContractCodeFixProvider, shared contract helpers, and tests for contract propagation scenarios.
  • Remove the legacy RequiresUnsafeCodeFixProvider and its associated tests, replacing the user-facing resource strings accordingly.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMigrationTestHelpers.csMinor formatting update in shared test setup for unsafe-v2 scenarios.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/SynchronizeUnsafeContractCodeFixTests.csAdds new tests validating unsafe contract propagation behavior.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csRemoves tests for the deprecated legacy fixer.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/AddUnsafeContextCodeFixTests.csAdds new tests validating unsafe-context insertion across many code shapes.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationSyntaxHelpers.csAdds GetSafeModifier helper for safe→unsafe token replacement scenarios.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeContractHelpers.csIntroduces shared symbol/syntax helpers for unsafe contract propagation logic.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierCodeFixHelpers.csAdds SetUnsafeModifierAsync, adjusts insertion ordering for partial, and exposes WithModifiers.
src/tools/illink/src/ILLink.CodeFix/SynchronizeUnsafeContractCodeFixProvider.csNew code fix provider that computes a contract closure and applies unsafe propagation edits.
src/tools/illink/src/ILLink.CodeFix/Resources.resxReplaces removed fixer title and adds titles for the new code fixers.
src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.csRemoves the deprecated legacy fixer implementation.
src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csprojLinks in the new shared UnsafeContractHelpers.cs for code fix usage.
src/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.csNew code fix provider implementing minimal unsafe statement/expression introduction logic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:33

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 12 out of 12 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.cs Outdated
Roslyn does not treat an unsafe expression as an unsafe context for property
and indexer accessors or for method group conversions, because those are bound
by the enclosing binder. The fixer used to wrap such operations anyway, which
left the diagnostic in place and nested another unsafe expression on every
later pass. It now writes an explicit cast inside the unsafe expression for
those operations, declines the fix when no cast can express it (ref-returning
accessors, unspeakable types), and never re-wraps an expression that is already
inside an unsafe expression. Assignment targets, increments, and deconstruction
right sides are handled as statements instead.
Modifiers that the fixers add now come with a <safety>TODO: Audit</safety>
stub. Without it IL5005 removed the modifier that had just been added, so a
second migration pass undid the first one.
Contract propagation no longer marks sibling overrides and implementations: a
safe member may override or implement a caller-unsafe one, so widening them
only grew the audit surface. Replacing an explicit safe modifier is offered
under its own title because it discards a deliberate audit.
Also merges a new unsafe region with adjacent generated regions, registers the
fixes for every diagnostic in the context, and hardens top level statement
replacement, statement list access, and the identifier fallback used when
expanding a statement range.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 20:29

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/UnsafeModifierCodeFixHelpers.cs Outdated
The unsafe expression template is now parsed with the parse options of the
document being fixed instead of a fixed set, so the generated syntax always
matches the language mode the project compiles with, and the fix is declined
when that mode cannot express an unsafe expression. The template also carries
the syntax kind used to detect an enclosing unsafe expression, which removes
the static probe that guessed it.
Generated safety documentation reuses the line ending the file already uses.
Member declarations carry their indentation but not the preceding line break,
so the previous fallback emitted a carriage return into files that use line
feeds only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 21:53

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.

@EgorBo
EgorBo marked this pull request as ready for review July 25, 2026 09:38
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 25, 2026 09:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

It was split into multiple PRs (#131451)

@EgorBoEgorBo closed this Aug 1, 2026
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 context migration code fixes - #131337

Closed
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups
Closed

Add unsafe context migration code fixes#131337
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Follow-up to #131002

This PR adds the remaining code fixers (along with #131245) that assist in migrating to the new unsafe-v2 rules (unsafe evolution). The goals are:

  • Code fixers should be idempotent as migrations to the new rules could be incremental.
  • Code fixers should rely on existing Roslyn diagnostics rather than independently rediscovering unsafe operations.

New code fixers added in this PR:

  1. [fixer] AddUnsafeContextCodeFixProvider fixes CS9360, CS9361, CS9362, CS9363, and CS9376 by introducing an unsafe context around the compiler-reported operation.

    The fixer prefers an unsafe { /* ... */ } statement containing a // SAFETY: Audit comment. When a statement would change scope, lifetime, control flow, or otherwise be invalid, it uses an unsafe(/* SAFETY: Audit */ expression) expression instead.

    Local declarations are split when that preserves semantics. In particular, stackalloc-to-span declarations are rewritten with a scoped forward declaration:

    scoped Span<byte>buffer;
    unsafe
    {// SAFETY: Auditbuffer=stackallocbyte[10];}

    The fixer also handles constructor initializers, using aliases, expression-bodied members, catch filters, async/iterator restrictions, directives, top-level statements, scoped refs, implicit conversions, and generated disposal/enumeration operations. It declines to offer a fix when no semantics-preserving automated transformation is available.

  2. [fixer] SynchronizeUnsafeContractCodeFixProvider fixes Roslyn's unsafe-to-safe contract mismatch diagnostics CS9364, CS9365, and CS9366, along with partial modifier mismatches CS0764 and CS9390.

    By the time this fixer runs, removable caller-unsafe modifiers have already been handled, so surviving unsafe contracts are propagated through source base members, interface declarations, overrides, implementations, and partial declarations. A pure safe partial mismatch defaults to unsafe, since safe is not currently valid on the non-extern partial declaration.

    The fixer does not add a new diagnostic for the opposite direction when the compiler accepts a safe implementation of an unsafe contract.

  3. The old (added long ago by Andy) RequiresUnsafeCodeFixProvider is removed. It only handled CS9362, could change caller contracts by marking parent members unsafe, and did not preserve scope and lifetime reliably.

Diagnostics IDs

Just for reference:

  • CS9360 [Roslyn] - An unsafe operation may only be used in an unsafe context.
  • CS9361 [Roslyn] - A stackalloc expression without an initializer inside [SkipLocalsInit] may only be used in an unsafe context.
  • CS9362 [Roslyn] - A member marked unsafe must be used in an unsafe context.
  • CS9363 [Roslyn] - A member with pointers in its signature must be used in an unsafe context.
  • CS9376 [Roslyn] - An unsafe context is required when an unsafe constructor satisfies a new() constraint.
  • CS9364 [Roslyn] - An unsafe member cannot override a safe member.
  • CS9365 [Roslyn] - An unsafe member cannot implicitly implement a safe member.
  • CS9366 [Roslyn] - An unsafe member cannot explicitly implement a safe member.
  • CS0764 [Roslyn] - Both partial member declarations must be unsafe, or neither may be unsafe.
  • CS9390 [Roslyn] - Both partial member declarations must be marked safe, or neither may be marked safe.

Once this and #131245 are in, we should be able to perform a migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:00
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 24, 2026
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

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 expands the ILLink Roslyn analyzer/code-fix test infrastructure with new (DEBUG-only) “unsafe-v2” migration code fixers: one that introduces a minimal unsafe context for compiler-reported unsafe usages, and another that propagates intentional unsafe contracts across partials/overrides/interface implementations. It also removes the prior RequiresUnsafeCodeFixProvider and its tests/resources.

Changes:

  • Add AddUnsafeContextCodeFixProvider and a comprehensive test suite covering many syntax positions and compiler diagnostics.
  • Add SynchronizeUnsafeContractCodeFixProvider, shared contract helpers, and tests for contract propagation scenarios.
  • Remove the legacy RequiresUnsafeCodeFixProvider and its associated tests, replacing the user-facing resource strings accordingly.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMigrationTestHelpers.csMinor formatting update in shared test setup for unsafe-v2 scenarios.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/SynchronizeUnsafeContractCodeFixTests.csAdds new tests validating unsafe contract propagation behavior.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csRemoves tests for the deprecated legacy fixer.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/AddUnsafeContextCodeFixTests.csAdds new tests validating unsafe-context insertion across many code shapes.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationSyntaxHelpers.csAdds GetSafeModifier helper for safe→unsafe token replacement scenarios.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeContractHelpers.csIntroduces shared symbol/syntax helpers for unsafe contract propagation logic.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierCodeFixHelpers.csAdds SetUnsafeModifierAsync, adjusts insertion ordering for partial, and exposes WithModifiers.
src/tools/illink/src/ILLink.CodeFix/SynchronizeUnsafeContractCodeFixProvider.csNew code fix provider that computes a contract closure and applies unsafe propagation edits.
src/tools/illink/src/ILLink.CodeFix/Resources.resxReplaces removed fixer title and adds titles for the new code fixers.
src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.csRemoves the deprecated legacy fixer implementation.
src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csprojLinks in the new shared UnsafeContractHelpers.cs for code fix usage.
src/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.csNew code fix provider implementing minimal unsafe statement/expression introduction logic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:33

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 12 out of 12 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.cs Outdated
Roslyn does not treat an unsafe expression as an unsafe context for property
and indexer accessors or for method group conversions, because those are bound
by the enclosing binder. The fixer used to wrap such operations anyway, which
left the diagnostic in place and nested another unsafe expression on every
later pass. It now writes an explicit cast inside the unsafe expression for
those operations, declines the fix when no cast can express it (ref-returning
accessors, unspeakable types), and never re-wraps an expression that is already
inside an unsafe expression. Assignment targets, increments, and deconstruction
right sides are handled as statements instead.
Modifiers that the fixers add now come with a <safety>TODO: Audit</safety>
stub. Without it IL5005 removed the modifier that had just been added, so a
second migration pass undid the first one.
Contract propagation no longer marks sibling overrides and implementations: a
safe member may override or implement a caller-unsafe one, so widening them
only grew the audit surface. Replacing an explicit safe modifier is offered
under its own title because it discards a deliberate audit.
Also merges a new unsafe region with adjacent generated regions, registers the
fixes for every diagnostic in the context, and hardens top level statement
replacement, statement list access, and the identifier fallback used when
expanding a statement range.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 20:29

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/UnsafeModifierCodeFixHelpers.cs Outdated
The unsafe expression template is now parsed with the parse options of the
document being fixed instead of a fixed set, so the generated syntax always
matches the language mode the project compiles with, and the fix is declined
when that mode cannot express an unsafe expression. The template also carries
the syntax kind used to detect an enclosing unsafe expression, which removes
the static probe that guessed it.
Generated safety documentation reuses the line ending the file already uses.
Member declarations carry their indentation but not the preceding line break,
so the previous fallback emitted a carriage return into files that use line
feeds only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 21:53

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.

@EgorBo
EgorBo marked this pull request as ready for review July 25, 2026 09:38
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 25, 2026 09:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

It was split into multiple PRs (#131451)

@EgorBoEgorBo closed this Aug 1, 2026
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 context migration code fixes - #131337

Closed
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups
Closed

Add unsafe context migration code fixes#131337
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:unsafe-migrator-followups

Conversation

@EgorBo

@EgorBoEgorBo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Follow-up to #131002

This PR adds the remaining code fixers (along with #131245) that assist in migrating to the new unsafe-v2 rules (unsafe evolution). The goals are:

  • Code fixers should be idempotent as migrations to the new rules could be incremental.
  • Code fixers should rely on existing Roslyn diagnostics rather than independently rediscovering unsafe operations.

New code fixers added in this PR:

  1. [fixer] AddUnsafeContextCodeFixProvider fixes CS9360, CS9361, CS9362, CS9363, and CS9376 by introducing an unsafe context around the compiler-reported operation.

    The fixer prefers an unsafe { /* ... */ } statement containing a // SAFETY: Audit comment. When a statement would change scope, lifetime, control flow, or otherwise be invalid, it uses an unsafe(/* SAFETY: Audit */ expression) expression instead.

    Local declarations are split when that preserves semantics. In particular, stackalloc-to-span declarations are rewritten with a scoped forward declaration:

    scoped Span<byte>buffer;
    unsafe
    {// SAFETY: Auditbuffer=stackallocbyte[10];}

    The fixer also handles constructor initializers, using aliases, expression-bodied members, catch filters, async/iterator restrictions, directives, top-level statements, scoped refs, implicit conversions, and generated disposal/enumeration operations. It declines to offer a fix when no semantics-preserving automated transformation is available.

  2. [fixer] SynchronizeUnsafeContractCodeFixProvider fixes Roslyn's unsafe-to-safe contract mismatch diagnostics CS9364, CS9365, and CS9366, along with partial modifier mismatches CS0764 and CS9390.

    By the time this fixer runs, removable caller-unsafe modifiers have already been handled, so surviving unsafe contracts are propagated through source base members, interface declarations, overrides, implementations, and partial declarations. A pure safe partial mismatch defaults to unsafe, since safe is not currently valid on the non-extern partial declaration.

    The fixer does not add a new diagnostic for the opposite direction when the compiler accepts a safe implementation of an unsafe contract.

  3. The old (added long ago by Andy) RequiresUnsafeCodeFixProvider is removed. It only handled CS9362, could change caller contracts by marking parent members unsafe, and did not preserve scope and lifetime reliably.

Diagnostics IDs

Just for reference:

  • CS9360 [Roslyn] - An unsafe operation may only be used in an unsafe context.
  • CS9361 [Roslyn] - A stackalloc expression without an initializer inside [SkipLocalsInit] may only be used in an unsafe context.
  • CS9362 [Roslyn] - A member marked unsafe must be used in an unsafe context.
  • CS9363 [Roslyn] - A member with pointers in its signature must be used in an unsafe context.
  • CS9376 [Roslyn] - An unsafe context is required when an unsafe constructor satisfies a new() constraint.
  • CS9364 [Roslyn] - An unsafe member cannot override a safe member.
  • CS9365 [Roslyn] - An unsafe member cannot implicitly implement a safe member.
  • CS9366 [Roslyn] - An unsafe member cannot explicitly implement a safe member.
  • CS0764 [Roslyn] - Both partial member declarations must be unsafe, or neither may be unsafe.
  • CS9390 [Roslyn] - Both partial member declarations must be marked safe, or neither may be marked safe.

Once this and #131245 are in, we should be able to perform a migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:00
@github-actionsgithub-actionsBot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jul 24, 2026
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

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 expands the ILLink Roslyn analyzer/code-fix test infrastructure with new (DEBUG-only) “unsafe-v2” migration code fixers: one that introduces a minimal unsafe context for compiler-reported unsafe usages, and another that propagates intentional unsafe contracts across partials/overrides/interface implementations. It also removes the prior RequiresUnsafeCodeFixProvider and its tests/resources.

Changes:

  • Add AddUnsafeContextCodeFixProvider and a comprehensive test suite covering many syntax positions and compiler diagnostics.
  • Add SynchronizeUnsafeContractCodeFixProvider, shared contract helpers, and tests for contract propagation scenarios.
  • Remove the legacy RequiresUnsafeCodeFixProvider and its associated tests, replacing the user-facing resource strings accordingly.

Reviewed changes

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

Show a summary per file
FileDescription
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMigrationTestHelpers.csMinor formatting update in shared test setup for unsafe-v2 scenarios.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/SynchronizeUnsafeContractCodeFixTests.csAdds new tests validating unsafe contract propagation behavior.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.csRemoves tests for the deprecated legacy fixer.
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/AddUnsafeContextCodeFixTests.csAdds new tests validating unsafe-context insertion across many code shapes.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMigrationSyntaxHelpers.csAdds GetSafeModifier helper for safe→unsafe token replacement scenarios.
src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeContractHelpers.csIntroduces shared symbol/syntax helpers for unsafe contract propagation logic.
src/tools/illink/src/ILLink.CodeFix/UnsafeModifierCodeFixHelpers.csAdds SetUnsafeModifierAsync, adjusts insertion ordering for partial, and exposes WithModifiers.
src/tools/illink/src/ILLink.CodeFix/SynchronizeUnsafeContractCodeFixProvider.csNew code fix provider that computes a contract closure and applies unsafe propagation edits.
src/tools/illink/src/ILLink.CodeFix/Resources.resxReplaces removed fixer title and adds titles for the new code fixers.
src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.csRemoves the deprecated legacy fixer implementation.
src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csprojLinks in the new shared UnsafeContractHelpers.cs for code fix usage.
src/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.csNew code fix provider implementing minimal unsafe statement/expression introduction logic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a86742a9-ab20-4209-83f8-d7074a99b968
CopilotAI review requested due to automatic review settings July 24, 2026 18:33

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 12 out of 12 changed files in this pull request and generated 1 comment.

Comment threadsrc/tools/illink/src/ILLink.CodeFix/AddUnsafeContext.cs Outdated
Roslyn does not treat an unsafe expression as an unsafe context for property
and indexer accessors or for method group conversions, because those are bound
by the enclosing binder. The fixer used to wrap such operations anyway, which
left the diagnostic in place and nested another unsafe expression on every
later pass. It now writes an explicit cast inside the unsafe expression for
those operations, declines the fix when no cast can express it (ref-returning
accessors, unspeakable types), and never re-wraps an expression that is already
inside an unsafe expression. Assignment targets, increments, and deconstruction
right sides are handled as statements instead.
Modifiers that the fixers add now come with a <safety>TODO: Audit</safety>
stub. Without it IL5005 removed the modifier that had just been added, so a
second migration pass undid the first one.
Contract propagation no longer marks sibling overrides and implementations: a
safe member may override or implement a caller-unsafe one, so widening them
only grew the audit surface. Replacing an explicit safe modifier is offered
under its own title because it discards a deliberate audit.
Also merges a new unsafe region with adjacent generated regions, registers the
fixes for every diagnostic in the context, and hardens top level statement
replacement, statement list access, and the identifier fallback used when
expanding a statement range.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 20:29

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/UnsafeModifierCodeFixHelpers.cs Outdated
The unsafe expression template is now parsed with the parse options of the
document being fixed instead of a fixed set, so the generated syntax always
matches the language mode the project compiles with, and the fix is declined
when that mode cannot express an unsafe expression. The template also carries
the syntax kind used to detect an enclosing unsafe expression, which removes
the static probe that guessed it.
Generated safety documentation reuses the line ending the file already uses.
Member declarations carry their indentation but not the preceding line break,
so the previous fallback emitted a carriage return into files that use line
feeds only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c3f7fe4-85f4-40f3-9ccc-e6113369014b
CopilotAI review requested due to automatic review settings July 24, 2026 21:53

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.

@EgorBo
EgorBo marked this pull request as ready for review July 25, 2026 09:38
@EgorBo
EgorBo requested a review from sbomer as a code ownerJuly 25, 2026 09:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

It was split into multiple PRs (#131451)

@EgorBoEgorBo closed this Aug 1, 2026
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