-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Add unsafe modifier migration code fixer #131002
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
3ca02e6
Add unsafe-v2 migration analyzers and code fixes
EgorBo 9da5c61
Address unsafe migrator review feedback
EgorBo e4876f9
Keep explicit layout fields unsafe by default
EgorBo 70612f7
Disable unsafe migration analyzers by default
EgorBo 1066a5b
Merge remote-tracking branch 'origin/main' into unsafe-migrator-4
EgorBo 7ab2348
Address unsafe migration review feedback
EgorBo d9b33a5
Share unsafe migration syntax helpers
EgorBo 02aeb51
Address remaining unsafe migration review feedback
EgorBo 0522222
Keep Roslyn helpers out of ILLink.Shared
EgorBo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
47 changes: 47 additions & 0 deletions
47
src/tools/illink/src/ILLink.CodeFix/AddUnsafeToExternCodeFixProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| #if DEBUG | ||
| using System.Collections.Immutable; | ||
| using System.Composition; | ||
| using System.Threading.Tasks; | ||
| using ILLink.CodeFixProvider; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.CodeFixes; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
|
||
| namespace ILLink.CodeFix | ||
| { | ||
| /// <summary> | ||
| /// Fixes compiler diagnostic <c>CS9389</c> by marking an unclassified extern member <c>unsafe</c>. | ||
| /// The generated contract is intentionally conservative so developers can replace it with <c>safe</c> after audit. | ||
| /// </summary> | ||
| [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AddUnsafeToExternCodeFixProvider)), Shared] | ||
| public sealed class AddUnsafeToExternCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider | ||
| { | ||
| public const string ExternMemberRequiresUnsafeOrSafeDiagnosticId = "CS9389"; | ||
|
|
||
| private static LocalizableString CodeFixTitle => | ||
| new LocalizableResourceString( | ||
| nameof(Resources.AddUnsafeToExternCodeFixTitle), | ||
| Resources.ResourceManager, | ||
| typeof(Resources)); | ||
|
|
||
| public override ImmutableArray<string> FixableDiagnosticIds => | ||
| [ExternMemberRequiresUnsafeOrSafeDiagnosticId]; | ||
|
|
||
| public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
|
||
| public override Task RegisterCodeFixesAsync(CodeFixContext context) => | ||
| UnsafeModifierCodeFixHelpers.RegisterAddUnsafeCodeFixAsync( | ||
| context, | ||
| CodeFixTitle, | ||
| static declaration => declaration is BaseMethodDeclarationSyntax | ||
| or PropertyDeclarationSyntax | ||
| or IndexerDeclarationSyntax | ||
| or EventDeclarationSyntax | ||
| or EventFieldDeclarationSyntax | ||
| or LocalFunctionStatementSyntax); | ||
| } | ||
| } | ||
| #endif | ||
44 changes: 44 additions & 0 deletions
44
src/tools/illink/src/ILLink.CodeFix/AddUnsafeToFieldCodeFixProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| #if DEBUG | ||
| using System.Collections.Immutable; | ||
| using System.Composition; | ||
| using System.Threading.Tasks; | ||
| using ILLink.CodeFixProvider; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.CodeFixes; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
|
||
| namespace ILLink.CodeFix | ||
| { | ||
| /// <summary> | ||
| /// Fixes compiler diagnostic <c>CS9392</c> by marking field-like members in explicit or extended layouts <c>unsafe</c>. | ||
| /// Primary-constructor parameters are intentionally unsupported because C# cannot place the modifier on them. | ||
| /// </summary> | ||
| [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AddUnsafeToFieldCodeFixProvider)), Shared] | ||
| public sealed class AddUnsafeToFieldCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider | ||
| { | ||
| public const string FieldRequiresUnsafeOrSafeDiagnosticId = "CS9392"; | ||
|
|
||
| private static LocalizableString CodeFixTitle => | ||
| new LocalizableResourceString( | ||
| nameof(Resources.AddUnsafeToFieldCodeFixTitle), | ||
| Resources.ResourceManager, | ||
| typeof(Resources)); | ||
|
|
||
| public override ImmutableArray<string> FixableDiagnosticIds => | ||
| [FieldRequiresUnsafeOrSafeDiagnosticId]; | ||
|
|
||
| public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
|
||
| public override Task RegisterCodeFixesAsync(CodeFixContext context) => | ||
| UnsafeModifierCodeFixHelpers.RegisterAddUnsafeCodeFixAsync( | ||
| context, | ||
| CodeFixTitle, | ||
| static declaration => declaration is FieldDeclarationSyntax | ||
| or PropertyDeclarationSyntax | ||
| or EventFieldDeclarationSyntax); | ||
| } | ||
| } | ||
| #endif |
47 changes: 47 additions & 0 deletions
47
src/tools/illink/src/ILLink.CodeFix/AddUnsafeToPointerSignatureCodeFixProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| #if DEBUG | ||
| using System.Collections.Immutable; | ||
| using System.Composition; | ||
| using System.Threading.Tasks; | ||
| using ILLink.CodeFixProvider; | ||
| using ILLink.Shared; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.CodeFixes; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
|
||
| namespace ILLink.CodeFix | ||
| { | ||
| /// <summary> | ||
| /// Fixes analyzer diagnostic <c>IL5006</c> by adding <c>unsafe</c> to a pointer or function-pointer signature. | ||
| /// This preserves the caller-unsafe behavior that legacy memory-safety rules inferred from those signatures. | ||
| /// </summary> | ||
| [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AddUnsafeToPointerSignatureCodeFixProvider)), Shared] | ||
| public sealed class AddUnsafeToPointerSignatureCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider | ||
| { | ||
| private static LocalizableString CodeFixTitle => | ||
| new LocalizableResourceString( | ||
| nameof(Resources.AddUnsafeToPointerSignatureCodeFixTitle), | ||
| Resources.ResourceManager, | ||
| typeof(Resources)); | ||
|
|
||
| public override ImmutableArray<string> FixableDiagnosticIds => | ||
| [DiagnosticId.PointerSignatureRequiresUnsafe.AsString()]; | ||
|
|
||
| public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
|
||
| public override Task RegisterCodeFixesAsync(CodeFixContext context) => | ||
| UnsafeModifierCodeFixHelpers.RegisterAddUnsafeCodeFixAsync( | ||
| context, | ||
| CodeFixTitle, | ||
| static declaration => declaration is BaseMethodDeclarationSyntax | ||
| or PropertyDeclarationSyntax | ||
| or IndexerDeclarationSyntax | ||
| or EventDeclarationSyntax | ||
| or EventFieldDeclarationSyntax | ||
| or FieldDeclarationSyntax | ||
| or LocalFunctionStatementSyntax); | ||
| } | ||
| } | ||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
src/tools/illink/src/ILLink.CodeFix/RemoveInvalidUnsafeCodeFixProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| #if DEBUG | ||
| using System.Collections.Immutable; | ||
| using System.Composition; | ||
| using System.Threading.Tasks; | ||
| using ILLink.CodeFixProvider; | ||
| using ILLink.RoslynAnalyzer; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.CodeActions; | ||
| using Microsoft.CodeAnalysis.CodeFixes; | ||
| using Microsoft.CodeAnalysis.CSharp; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
|
||
| namespace ILLink.CodeFix | ||
| { | ||
| /// <summary> | ||
| /// Fixes <c>CS9377</c> and unsafe-specific <c>CS0106</c> diagnostics by removing the invalid <c>unsafe</c> modifier. | ||
| /// The shared <c>CS0106</c> ID is filtered so modifiers unrelated to unsafe are left to their own fixes. | ||
| /// </summary> | ||
| [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RemoveInvalidUnsafeCodeFixProvider)), Shared] | ||
| public sealed class RemoveInvalidUnsafeCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider | ||
| { | ||
| public const string UnsafeModifierHasNoEffectDiagnosticId = "CS9377"; | ||
| public const string InvalidModifierDiagnosticId = "CS0106"; | ||
|
|
||
| private static LocalizableString CodeFixTitle => | ||
| new LocalizableResourceString( | ||
| nameof(Resources.RemoveInvalidUnsafeCodeFixTitle), | ||
| Resources.ResourceManager, | ||
| typeof(Resources)); | ||
|
|
||
| public override ImmutableArray<string> FixableDiagnosticIds => | ||
| [UnsafeModifierHasNoEffectDiagnosticId, InvalidModifierDiagnosticId]; | ||
|
|
||
| public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
|
||
| public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
| { | ||
| Diagnostic diagnostic = context.Diagnostics[0]; | ||
| var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
| if (root is null) | ||
| return; | ||
|
|
||
| SyntaxNode targetNode = root.FindNode( | ||
| diagnostic.Location.SourceSpan, | ||
| getInnermostNodeForTie: true); | ||
| if (UnsafeModifierCodeFixHelpers.FindDeclaration(targetNode) is not { } declaration | ||
| || !UnsafeMigrationSyntaxHelpers.HasModifier(declaration, SyntaxKind.UnsafeKeyword)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| // CS0106 is shared by every invalid modifier. Restrict this fixer to declaration shapes where | ||
| // Roslyn specifically reports unsafe as invalid, rather than inspecting localized message text. | ||
| if (diagnostic.Id == InvalidModifierDiagnosticId && !IsInvalidUnsafeDeclaration(declaration)) | ||
| return; | ||
|
|
||
| string title = CodeFixTitle.ToString(); | ||
| context.RegisterCodeFix( | ||
| CodeAction.Create( | ||
| title, | ||
| cancellationToken => UnsafeModifierCodeFixHelpers.RemoveUnsafeModifierAsync( | ||
| context.Document, | ||
| declaration, | ||
| cancellationToken), | ||
| title), | ||
| diagnostic); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Identifies the well-formed declarations for which Roslyn reports unsafe-specific <c>CS0106</c>. | ||
| /// </summary> | ||
| private static bool IsInvalidUnsafeDeclaration(SyntaxNode declaration) => | ||
| declaration switch | ||
| { | ||
| EnumDeclarationSyntax => true, | ||
| FieldDeclarationSyntax { Modifiers: var modifiers } => modifiers.Any(SyntaxKind.ConstKeyword), | ||
| _ => false, | ||
| }; | ||
| } | ||
| } | ||
| #endif |
74 changes: 74 additions & 0 deletions
74
src/tools/illink/src/ILLink.CodeFix/RemoveUndocumentedUnsafeCodeFixProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| #if DEBUG | ||
| using System.Collections.Immutable; | ||
| using System.Composition; | ||
| using System.Threading.Tasks; | ||
| using ILLink.CodeFixProvider; | ||
| using ILLink.RoslynAnalyzer; | ||
| using ILLink.Shared; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.CodeActions; | ||
| using Microsoft.CodeAnalysis.CodeFixes; | ||
| using Microsoft.CodeAnalysis.CSharp; | ||
|
|
||
| namespace ILLink.CodeFix | ||
| { | ||
| /// <summary> | ||
| /// Fixes <c>IL5005</c> by removing undocumented legacy unsafe scopes that became caller contracts under unsafe-v2. | ||
| /// Pointer signatures and field-like <c>CS9392</c> declarations retain <c>unsafe</c> for compatibility and safety. | ||
| /// </summary> | ||
| [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RemoveUndocumentedUnsafeCodeFixProvider)), Shared] | ||
| public sealed class RemoveUndocumentedUnsafeCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider | ||
| { | ||
| private static LocalizableString RemoveCodeFixTitle => | ||
| new LocalizableResourceString( | ||
| nameof(Resources.RemoveUndocumentedUnsafeCodeFixTitle), | ||
| Resources.ResourceManager, | ||
| typeof(Resources)); | ||
|
|
||
| public override ImmutableArray<string> FixableDiagnosticIds => | ||
| [DiagnosticId.UnsafeMemberMissingSafetyDocumentation.AsString()]; | ||
|
|
||
| public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
|
||
| public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
| { | ||
| Diagnostic diagnostic = context.Diagnostics[0]; | ||
| // Pointer signatures were already caller-unsafe under legacy rules. Field-like explicit and extended | ||
| // layout declarations must also keep a marker for CS9392, and default to unsafe until manually audited. | ||
| if (diagnostic.Properties.ContainsKey(UnsafeMemberMissingSafetyDocumentationAnalyzer.PointerSignatureProperty) | ||
| || diagnostic.Properties.ContainsKey(UnsafeMemberMissingSafetyDocumentationAnalyzer.RequiresExplicitSafetyModifierProperty)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
| if (root is null) | ||
| return; | ||
|
|
||
| SyntaxNode targetNode = root.FindNode( | ||
| diagnostic.Location.SourceSpan, | ||
| getInnermostNodeForTie: true); | ||
| if (UnsafeModifierCodeFixHelpers.FindDeclaration(targetNode) is not { } declaration | ||
| || !UnsafeMigrationSyntaxHelpers.HasModifier(declaration, SyntaxKind.UnsafeKeyword)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| string title = RemoveCodeFixTitle.ToString(); | ||
|
|
||
| context.RegisterCodeFix( | ||
| CodeAction.Create( | ||
| title, | ||
| cancellationToken => UnsafeModifierCodeFixHelpers.RemoveUnsafeModifierAsync( | ||
| context.Document, | ||
| declaration, | ||
| cancellationToken), | ||
| title), | ||
| diagnostic); | ||
| } | ||
| } | ||
| } | ||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.