diff --git a/eng/Version.Details.props b/eng/Version.Details.props index a26fecf2431294..674023668e4b6b 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,9 +6,9 @@ This file should be imported by eng/Versions.props - 5.7.0-1.26257.113 - 5.7.0-1.26257.113 - 5.7.0-1.26257.113 + 5.8.0-1.26266.103 + 5.8.0-1.26266.103 + 5.8.0-1.26266.103 11.0.100-preview.5.26257.113 11.0.100-preview.5.26257.113 11.0.0-beta.26257.113 @@ -31,7 +31,7 @@ This file should be imported by eng/Versions.props 3.2.2-beta.26257.113 2.9.3-beta.26257.113 11.0.0-beta.26257.113 - 5.7.0-1.26257.113 + 5.8.0-1.26266.103 11.0.0-preview.5.26257.113 11.0.100-preview.5.26257.113 11.0.0-preview.5.26257.113 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index dba8cb8f5612a7..26f7a563052520 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -327,21 +327,21 @@ https://github.com/dotnet/runtime-assets 6af4bc0beb5fd7bc49a4a986a04ff6f6e4b83ab1 - + https://github.com/dotnet/dotnet - 0eae08ed2f094f44e0151e4815e7cdd1a334fcdf + 3a62cc842c76761d4742f1d103892f19fd06dd3a - + https://github.com/dotnet/dotnet - 0eae08ed2f094f44e0151e4815e7cdd1a334fcdf + 3a62cc842c76761d4742f1d103892f19fd06dd3a - + https://github.com/dotnet/dotnet - 0eae08ed2f094f44e0151e4815e7cdd1a334fcdf + 3a62cc842c76761d4742f1d103892f19fd06dd3a - + https://github.com/dotnet/dotnet - 0eae08ed2f094f44e0151e4815e7cdd1a334fcdf + 3a62cc842c76761d4742f1d103892f19fd06dd3a https://github.com/dotnet/dotnet diff --git a/src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.cs b/src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.cs index 654bd74905366c..fd9c7d22ad55cd 100644 --- a/src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.cs +++ b/src/tools/illink/src/ILLink.CodeFix/RequiresUnsafeCodeFixProvider.cs @@ -9,8 +9,6 @@ using System.Threading; using System.Threading.Tasks; using ILLink.CodeFixProvider; -using ILLink.RoslynAnalyzer; -using ILLink.Shared; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; @@ -21,34 +19,39 @@ namespace ILLink.CodeFix { [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RequiresUnsafeCodeFixProvider)), Shared] - public sealed class RequiresUnsafeCodeFixProvider : BaseAttributeCodeFixProvider + public sealed class RequiresUnsafeCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider { private const string WrapInUnsafeBlockTitle = "Wrap in unsafe block"; - public static ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresUnsafe)); + public const string UnsafeMemberOperationDiagnosticId = "CS9362"; - public sealed override ImmutableArray FixableDiagnosticIds => SupportedDiagnostics.Select(dd => dd.Id).ToImmutableArray(); + public sealed override ImmutableArray FixableDiagnosticIds => [UnsafeMemberOperationDiagnosticId]; - private protected override LocalizableString CodeFixTitle => new LocalizableResourceString(nameof(Resources.RequiresUnsafeCodeFixTitle), Resources.ResourceManager, typeof(Resources)); - - private protected override string FullyQualifiedAttributeName => RequiresUnsafeAnalyzer.FullyQualifiedRequiresUnsafeAttribute; - - private protected override AttributeableParentTargets AttributableParentTargets => AttributeableParentTargets.MethodOrConstructor; + private static LocalizableString CodeFixTitle => new LocalizableResourceString(nameof(Resources.RequiresUnsafeCodeFixTitle), Resources.ResourceManager, typeof(Resources)); public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { - // Register the base code fix (add RequiresUnsafe attribute) - await BaseRegisterCodeFixesAsync(context).ConfigureAwait(false); - - // Register the "wrap in unsafe block" code fix var document = context.Document; var diagnostic = context.Diagnostics.First(); + var codeFixTitle = CodeFixTitle.ToString(); if (await document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false) is not { } root) return; SyntaxNode targetNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + // Register "add unsafe modifier" code fix + var unsafeModifierTarget = GetUnsafeModifierTarget(targetNode); + if (unsafeModifierTarget is not null && !HasUnsafeModifier(unsafeModifierTarget)) + { + context.RegisterCodeFix(CodeAction.Create( + title: codeFixTitle, + createChangedDocument: ct => AddUnsafeModifierAsync(document, unsafeModifierTarget, ct), + equivalenceKey: codeFixTitle), diagnostic); + } + + // Register the "wrap in unsafe block" code fix + // Find the statement containing the unsafe call var containingStatement = targetNode.AncestorsAndSelf().OfType().FirstOrDefault(); @@ -127,6 +130,39 @@ private static bool IsEmbeddedStatement(StatementSyntax statement) || statement.Parent is FixedStatementSyntax; } + private static async Task AddUnsafeModifierAsync( + Document document, + SyntaxNode declaration, + CancellationToken cancellationToken) + { + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var modifiers = editor.Generator.GetModifiers(declaration); + editor.SetModifiers(declaration, modifiers.WithIsUnsafe(true)); + return editor.GetChangedDocument(); + } + + private static SyntaxNode? GetUnsafeModifierTarget(SyntaxNode targetNode) + => targetNode.AncestorsAndSelf().FirstOrDefault(static node => node is MethodDeclarationSyntax + or ConstructorDeclarationSyntax + or DestructorDeclarationSyntax + or LocalFunctionStatementSyntax + or PropertyDeclarationSyntax + or IndexerDeclarationSyntax + or AccessorDeclarationSyntax); + + private static bool HasUnsafeModifier(SyntaxNode declaration) + => declaration switch + { + MethodDeclarationSyntax method => method.Modifiers.Any(SyntaxKind.UnsafeKeyword), + ConstructorDeclarationSyntax constructor => constructor.Modifiers.Any(SyntaxKind.UnsafeKeyword), + DestructorDeclarationSyntax destructor => destructor.Modifiers.Any(SyntaxKind.UnsafeKeyword), + LocalFunctionStatementSyntax localFunction => localFunction.Modifiers.Any(SyntaxKind.UnsafeKeyword), + PropertyDeclarationSyntax property => property.Modifiers.Any(SyntaxKind.UnsafeKeyword), + IndexerDeclarationSyntax indexer => indexer.Modifiers.Any(SyntaxKind.UnsafeKeyword), + AccessorDeclarationSyntax accessor => accessor.Modifiers.Any(SyntaxKind.UnsafeKeyword), + _ => false + }; + private static async Task WrapStatementsInUnsafeBlockAsync( Document document, BlockSyntax parentBlock, @@ -480,9 +516,6 @@ private static async Task ConvertExpressionBodyToUnsafeBlockAsync( return editor.GetChangedDocument(); } - protected override SyntaxNode[] GetAttributeArguments(ISymbol? attributableSymbol, ISymbol targetSymbol, SyntaxGenerator syntaxGenerator, Diagnostic diagnostic) => - RequiresHelpers.GetAttributeArgumentsForRequires(targetSymbol, syntaxGenerator, HasPublicAccessibility(attributableSymbol)); - /// /// Checks if the arrow expression clause or its expression has preprocessor directive trivia. /// Converting expression bodies with directives to block bodies is error-prone, so we skip the fix. diff --git a/src/tools/illink/src/ILLink.CodeFix/Resources.resx b/src/tools/illink/src/ILLink.CodeFix/Resources.resx index 0bd940a98334e2..d552d7228b3e62 100644 --- a/src/tools/illink/src/ILLink.CodeFix/Resources.resx +++ b/src/tools/illink/src/ILLink.CodeFix/Resources.resx @@ -127,7 +127,7 @@ Add RequiresDynamicCode attribute to parent method - Add RequiresUnsafe attribute to parent method + Add 'unsafe' to parent method Add UnconditionalSuppressMessage attribute to parent method @@ -135,7 +135,4 @@ Add DynamicallyAccessedMembers attribute to source of warning - - Add RequiresUnsafe attribute to method with pointer types - \ No newline at end of file diff --git a/src/tools/illink/src/ILLink.CodeFix/UnsafeMethodMissingRequiresUnsafeCodeFixProvider.cs b/src/tools/illink/src/ILLink.CodeFix/UnsafeMethodMissingRequiresUnsafeCodeFixProvider.cs deleted file mode 100644 index 67769e6394bb4f..00000000000000 --- a/src/tools/illink/src/ILLink.CodeFix/UnsafeMethodMissingRequiresUnsafeCodeFixProvider.cs +++ /dev/null @@ -1,83 +0,0 @@ -// 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.Linq; -using System.Threading; -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.Editing; -using Microsoft.CodeAnalysis.Simplification; - -namespace ILLink.CodeFix -{ - [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UnsafeMethodMissingRequiresUnsafeCodeFixProvider)), Shared] - public sealed class UnsafeMethodMissingRequiresUnsafeCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider - { - public static ImmutableArray SupportedDiagnostics => - ImmutableArray.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.UnsafeMethodMissingRequiresUnsafe)); - - public sealed override ImmutableArray FixableDiagnosticIds => - SupportedDiagnostics.Select(dd => dd.Id).ToImmutableArray(); - - public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; - - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - var document = context.Document; - var diagnostic = context.Diagnostics.First(); - - if (await document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false) is not { } root) - return; - - var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); - var declarationNode = node.AncestorsAndSelf().FirstOrDefault( - n => n is Microsoft.CodeAnalysis.CSharp.Syntax.BaseMethodDeclarationSyntax - or Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax - or Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax - or Microsoft.CodeAnalysis.CSharp.Syntax.IndexerDeclarationSyntax); - if (declarationNode is null) - return; - - var title = new LocalizableResourceString( - nameof(Resources.UnsafeMethodMissingRequiresUnsafeCodeFixTitle), - Resources.ResourceManager, - typeof(Resources)).ToString(); - - context.RegisterCodeFix(CodeAction.Create( - title: title, - createChangedDocument: ct => AddRequiresUnsafeAttributeAsync(document, declarationNode, ct), - equivalenceKey: title), diagnostic); - } - - private static async Task AddRequiresUnsafeAttributeAsync( - Document document, - SyntaxNode declarationNode, - CancellationToken cancellationToken) - { - if (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false) is not { } model) - return document; - - if (model.Compilation.GetBestTypeByMetadataName(RequiresUnsafeAnalyzer.FullyQualifiedRequiresUnsafeAttribute) is not { } attributeSymbol) - return document; - - var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); - var generator = editor.Generator; - - var attribute = generator.Attribute(generator.TypeExpression(attributeSymbol)) - .WithAdditionalAnnotations(Simplifier.Annotation, Simplifier.AddImportsAnnotation); - - editor.AddAttribute(declarationNode, attribute); - - return editor.GetChangedDocument(); - } - } -} -#endif diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs index 4492be99e291fc..5aea7858179e71 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs @@ -31,9 +31,6 @@ private static ImmutableArray GetRequiresAnalyzers() builder.Add(new RequiresAssemblyFilesAnalyzer()); builder.Add(new RequiresUnreferencedCodeAnalyzer()); builder.Add(new RequiresDynamicCodeAnalyzer()); -#if DEBUG - builder.Add(new RequiresUnsafeAnalyzer()); -#endif return builder.ToImmutable(); } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs index e924fce0a24d3e..51a5645b4318c6 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs @@ -9,9 +9,6 @@ public static class MSBuildPropertyOptionNames public const string IncludeAllContentForSelfExtract = nameof(IncludeAllContentForSelfExtract); public const string EnableTrimAnalyzer = nameof(EnableTrimAnalyzer); public const string EnableAotAnalyzer = nameof(EnableAotAnalyzer); -#if DEBUG - public const string EnableUnsafeAnalyzer = nameof(EnableUnsafeAnalyzer); -#endif public const string VerifyReferenceAotCompatibility = nameof(VerifyReferenceAotCompatibility); public const string VerifyReferenceTrimCompatibility = nameof(VerifyReferenceTrimCompatibility); } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresUnsafeAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresUnsafeAnalyzer.cs deleted file mode 100644 index 57f8bc961df4f9..00000000000000 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresUnsafeAnalyzer.cs +++ /dev/null @@ -1,103 +0,0 @@ -// 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; -using System.Collections.Immutable; -using ILLink.Shared; -using ILLink.Shared.TrimAnalysis; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace ILLink.RoslynAnalyzer -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class RequiresUnsafeAnalyzer : RequiresAnalyzerBase - { - internal const string RequiresUnsafeAttributeName = "RequiresUnsafeAttribute"; - public const string FullyQualifiedRequiresUnsafeAttribute = "System.Diagnostics.CodeAnalysis." + RequiresUnsafeAttributeName; - - private static readonly DiagnosticDescriptor s_requiresUnsafeOnStaticCtor = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresUnsafeOnStaticConstructor); - private static readonly DiagnosticDescriptor s_requiresUnsafeOnEntryPoint = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresUnsafeOnEntryPoint); - private static readonly DiagnosticDescriptor s_requiresUnsafeRule = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresUnsafe); - private static readonly DiagnosticDescriptor s_requiresUnsafeAttributeMismatch = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresUnsafeAttributeMismatch); - - public override ImmutableArray SupportedDiagnostics => - ImmutableArray.Create(s_requiresUnsafeRule, s_requiresUnsafeAttributeMismatch, s_requiresUnsafeOnStaticCtor, s_requiresUnsafeOnEntryPoint); - - private protected override string RequiresAttributeName => RequiresUnsafeAttributeName; - - internal override string RequiresAttributeFullyQualifiedName => FullyQualifiedRequiresUnsafeAttribute; - - private protected override DiagnosticTargets AnalyzerDiagnosticTargets => DiagnosticTargets.MethodOrConstructor; - - private protected override DiagnosticDescriptor RequiresDiagnosticRule => s_requiresUnsafeRule; - - private protected override DiagnosticId RequiresDiagnosticId => DiagnosticId.RequiresUnsafe; - - private protected override DiagnosticDescriptor RequiresAttributeMismatch => s_requiresUnsafeAttributeMismatch; - - private protected override DiagnosticDescriptor RequiresOnStaticCtor => s_requiresUnsafeOnStaticCtor; - - private protected override DiagnosticDescriptor RequiresOnEntryPoint => s_requiresUnsafeOnEntryPoint; - - internal override bool IsAnalyzerEnabled(AnalyzerOptions options) => - options.IsMSBuildPropertyValueTrue(MSBuildPropertyOptionNames.EnableUnsafeAnalyzer); - - private protected override bool IsRequiresCheck(IPropertySymbol propertySymbol, Compilation compilation) - { - // No feature check property for RequiresUnsafe - return false; - } - - protected override bool IsInRequiresScope(ISymbol containingSymbol, in DiagnosticContext context) - { - if (base.IsInRequiresScope(containingSymbol, context)) - return true; - - if (!context.Location.IsInSource) - return false; - - // Check to see if we're in an unsafe block or unsafe member - var syntaxTree = context.Location.SourceTree!; - var root = syntaxTree.GetRoot(); - var node = root.FindNode(context.Location.SourceSpan); - while (node != null && node != root) - { - if (node.IsKind(SyntaxKind.UnsafeStatement)) - return true; - - // Check for unsafe modifier on the containing member or type - if (node is MethodDeclarationSyntax method && method.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - if (node is LocalFunctionStatementSyntax localFunc && localFunc.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - if (node is PropertyDeclarationSyntax prop && prop.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - if (node is IndexerDeclarationSyntax indexer && indexer.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - if (node is OperatorDeclarationSyntax op && op.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - if (node is ConversionOperatorDeclarationSyntax conv && conv.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - if (node is ConstructorDeclarationSyntax ctor && ctor.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - if (node is FieldDeclarationSyntax field && field.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - if (node is TypeDeclarationSyntax type && type.Modifiers.Any(SyntaxKind.UnsafeKeyword)) - return true; - - node = node.Parent; - } - - return false; - } - - protected override bool VerifyAttributeArguments(AttributeData attribute) => true; - - protected override string GetMessageFromAttribute(AttributeData? requiresAttribute) => ""; - } -} -#endif diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMethodMissingRequiresUnsafeAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMethodMissingRequiresUnsafeAnalyzer.cs deleted file mode 100644 index 279a328f70ccaa..00000000000000 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeMethodMissingRequiresUnsafeAnalyzer.cs +++ /dev/null @@ -1,76 +0,0 @@ -// 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 ILLink.Shared; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace ILLink.RoslynAnalyzer -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class UnsafeMethodMissingRequiresUnsafeAnalyzer : DiagnosticAnalyzer - { - private static readonly DiagnosticDescriptor s_rule = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.UnsafeMethodMissingRequiresUnsafe, diagnosticSeverity: DiagnosticSeverity.Info); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(s_rule); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.RegisterCompilationStartAction(context => - { - if (!context.Options.IsMSBuildPropertyValueTrue(MSBuildPropertyOptionNames.EnableUnsafeAnalyzer)) - return; - - if (context.Compilation.GetTypeByMetadataName(RequiresUnsafeAnalyzer.FullyQualifiedRequiresUnsafeAttribute) is null) - return; - - context.RegisterSymbolAction( - AnalyzeMethod, - SymbolKind.Method); - }); - } - - private static void AnalyzeMethod(SymbolAnalysisContext context) - { - if (context.Symbol is not IMethodSymbol method) - return; - - if (!HasPointerInSignature(method)) - return; - - if (method.HasAttribute(RequiresUnsafeAnalyzer.RequiresUnsafeAttributeName)) - return; - - // For property/indexer accessors, check the containing property instead - if (method.AssociatedSymbol is IPropertySymbol property - && property.HasAttribute(RequiresUnsafeAnalyzer.RequiresUnsafeAttributeName)) - return; - - foreach (var location in method.Locations) - { - context.ReportDiagnostic(Diagnostic.Create(s_rule, location, method.GetDisplayName())); - } - } - - private static bool HasPointerInSignature(IMethodSymbol method) - { - if (IsPointerType(method.ReturnType)) - return true; - - foreach (var param in method.Parameters) - { - if (IsPointerType(param.Type)) - return true; - } - - return false; - } - - private static bool IsPointerType(ITypeSymbol type) => type is IPointerTypeSymbol or IFunctionPointerTypeSymbol; - } -} -#endif diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.props b/src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.props index e8a5e17d0c5725..18ae6cb8fd5e8b 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.props +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.props @@ -3,7 +3,6 @@ - diff --git a/src/tools/illink/src/ILLink.Shared/DiagnosticId.cs b/src/tools/illink/src/ILLink.Shared/DiagnosticId.cs index d1224b487188f3..997f3f232ae018 100644 --- a/src/tools/illink/src/ILLink.Shared/DiagnosticId.cs +++ b/src/tools/illink/src/ILLink.Shared/DiagnosticId.cs @@ -219,16 +219,6 @@ public enum DiagnosticId // Feature guard diagnostic ids. ReturnValueDoesNotMatchFeatureGuards = 4000, InvalidFeatureGuard = 4001, - -#if DEBUG - // RequiresUnsafe diagnostics are in the 5000 range, separate from other diagnostics. - RequiresUnsafe = 5000, - RequiresUnsafeAttributeMismatch = 5001, - RequiresUnsafeOnStaticConstructor = 5002, - RequiresUnsafeOnEntryPoint = 5003, - UnsafeMethodMissingRequiresUnsafe = 5004, - _EndRequiresUnsafeWarningsSentinel, -#endif } public static class DiagnosticIdExtensions @@ -253,9 +243,6 @@ public static string GetDiagnosticSubcategory(this DiagnosticId diagnosticId) => >= 2109 and < (int)DiagnosticId._EndTrimAnalysisWarningsSentinel => MessageSubCategory.TrimAnalysis, >= 3050 and <= 3052 => MessageSubCategory.AotAnalysis, >= 3054 and <= 3058 => MessageSubCategory.AotAnalysis, -#if DEBUG - >= 5000 and < (int)DiagnosticId._EndRequiresUnsafeWarningsSentinel => MessageSubCategory.None, -#endif _ => MessageSubCategory.None, }; diff --git a/src/tools/illink/src/ILLink.Shared/SharedStrings.resx b/src/tools/illink/src/ILLink.Shared/SharedStrings.resx index 4e177adb3eb645..77eb755610ecb3 100644 --- a/src/tools/illink/src/ILLink.Shared/SharedStrings.resx +++ b/src/tools/illink/src/ILLink.Shared/SharedStrings.resx @@ -1269,34 +1269,4 @@ Trim dataflow analysis of member '{0}' took too long to complete. Trim safety cannot be guaranteed. - - Calling methods annotated with 'RequiresUnsafeAttribute' may break functionality in environments that do not support unsafe code. - - - Using member '{0}' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'.{1}{2} - - - 'RequiresUnsafeAttribute' annotations must match across all interface implementations or overrides. - - - {0}. 'RequiresUnsafeAttribute' annotations must match across all interface implementations or overrides. - - - 'RequiresUnsafeAttribute' cannot be placed directly on static constructor '{0}'. - - - 'RequiresUnsafeAttribute' cannot be used on static constructors because they are not directly callable. Consider placing the attribute on the type instead. - - - 'RequiresUnsafeAttribute' cannot be placed directly on application entry point '{0}'. - - - The use of 'RequiresUnsafeAttribute' on entry points is disallowed since the method will be called from outside the visible app. - - - Methods with pointer types in their signature should be annotated with 'RequiresUnsafeAttribute'. - - - Method '{0}' has pointer types in its signature but is not annotated with 'RequiresUnsafeAttribute'. - diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeAnalyzerTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeAnalyzerTests.cs deleted file mode 100644 index 13372701fad9bf..00000000000000 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeAnalyzerTests.cs +++ /dev/null @@ -1,467 +0,0 @@ -// 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; -using System.Threading.Tasks; -using ILLink.Shared; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Testing; -using Xunit; -using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpAnalyzerVerifier< - ILLink.RoslynAnalyzer.DynamicallyAccessedMembersAnalyzer>; - -namespace ILLink.RoslynAnalyzer.Tests -{ - public class RequiresUnsafeAnalyzerTests - { - static readonly string unsafeAttribute = @" -#nullable enable - -namespace System.Diagnostics.CodeAnalysis -{ - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute - { } -}"; - - static async Task VerifyRequiresUnsafeAnalyzer( - string source, - params DiagnosticResult[] expected) - { - await VerifyCS.VerifyAnalyzerAsync( - source + unsafeAttribute, - consoleApplication: false, - TestCaseUtils.UseMSBuildProperties(MSBuildPropertyOptionNames.EnableUnsafeAnalyzer), - Array.Empty(), - allowUnsafe: true, - expected); - } - - [Fact] - public async Task SimpleDiagnostic() - { - var test = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public int M1() => 0; - - int M2() => M1(); - } - class D - { - public int M3(C c) => c.M1(); - - public class E - { - public int M4(C c) => c.M1(); - } - } - public class E - { - public class F - { - public int M5(C c) => c.M1(); - } - } - """; - - await VerifyRequiresUnsafeAnalyzer( - source: test, - new[] { - // /0/Test0.cs(8,17): warning IL3059: Using member 'C.M1()' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe).WithSpan(8, 17, 8, 19).WithArguments("C.M1()", "", ""), - // /0/Test0.cs(12,27): warning IL3059: Using member 'C.M1()' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe).WithSpan(12, 27, 12, 31).WithArguments("C.M1()", "", ""), - // /0/Test0.cs(16,31): warning IL3059: Using member 'C.M1()' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe).WithSpan(16, 31, 16, 35).WithArguments("C.M1()", "", ""), - // /0/Test0.cs(23,31): warning IL3059: Using member 'C.M1()' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe).WithSpan(23, 31, 23, 35).WithArguments("C.M1()", "", "") - }); - } - - [Fact] - public Task InLambda() - { - var src = """ - using System; - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafeAttribute] - public int M1() => 0; - - Action M2() - { - return () => M1(); - } - } - """; - var diag = new[] { - // /0/Test0.cs(11,22): warning IL3059: Using member 'C.M1()' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe).WithSpan(11, 22, 11, 24).WithArguments("C.M1()", "", "") - }; - return VerifyRequiresUnsafeAnalyzer(src, diag); - } - - [Fact] - public Task InLocalFunc() - { - var src = """ - using System; - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public int M1() => 0; - - Action M2() - { - void Wrapper() => M1(); - return Wrapper; - } - } - """; - return VerifyRequiresUnsafeAnalyzer( - source: src, - new[] { - // /0/Test0.cs(11,27): warning IL3059: Using member 'C.M1()' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe).WithSpan(11, 27, 11, 29).WithArguments("C.M1()", "", "") - }); - } - - [Fact] - public Task InCtor() - { - var src = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public static int M1() => 0; - - public C() => M1(); - } - """; - return VerifyRequiresUnsafeAnalyzer( - source: src, - expected: new[] { - // /0/Test0.cs(9,19): warning IL3059: Using member 'C.M1()' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe).WithSpan(8, 19, 8, 21).WithArguments("C.M1()", "", "") - }); - } - - [Fact] - public async Task RequiresUnsafeOnConstructor() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public C() { } - - public void M() - { - new C(); - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source, - // /0/Test0.cs(10,9): warning IL3059: Using member 'C.C()' which has 'RequiresUnsafeAttribute' requires an unsafe context, such as an unsafe block or a method marked with 'RequiresUnsafeAttribute'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe).WithSpan(10, 9, 10, 16).WithArguments("C.C()", "", "") - ); - } - - [Fact] - public async Task RequiresUnsafeInSameScope() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public void M1() { } - - [RequiresUnsafe] - public void M2() - { - M1(); // Should not warn - already in RequiresUnsafe scope - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source); - } - - [Fact] - public async Task RequiresUnsafeOnStaticConstructor() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - static C() { } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source, - // /0/Test0.cs(6,12): warning IL3061: 'RequiresUnsafeAttribute' cannot be placed directly on static constructor 'C.C()'. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafeOnStaticConstructor).WithSpan(6, 12, 6, 13).WithArguments("C..cctor()"), - // Why is this an compiler error? - DiagnosticResult.CompilerError("CS9367").WithSpan(5, 6, 5, 20) - ); - } - - [Fact] - public async Task RequiresUnsafeAttributeMismatchOnOverride() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class Base - { - [RequiresUnsafe] - public virtual void M() { } - } - - public class Derived : Base - { - public override void M() { } // Should warn about mismatch - } - """; - - await VerifyRequiresUnsafeAnalyzer(source, - // (11,26): warning IL3060: Base member 'Base.M()' with 'RequiresUnsafeAttribute' has a derived member 'Derived.M()' without 'RequiresUnsafeAttribute'. 'RequiresUnsafeAttribute' annotations must match across all interface implementations or overrides. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafeAttributeMismatch).WithSpan(11, 26, 11, 27).WithArguments("Base member 'Base.M()' with 'RequiresUnsafeAttribute' has a derived member 'Derived.M()' without 'RequiresUnsafeAttribute'") - ); - } - - [Fact] - public async Task RequiresUnsafeAttributeMismatchOnInterface() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public interface IFoo - { - [RequiresUnsafe] - void M(); - } - - public class Foo : IFoo - { - public void M() { } // Should warn about mismatch - } - """; - - await VerifyRequiresUnsafeAnalyzer(source, - // (11,17): warning IL3060: Interface member 'IFoo.M()' with 'RequiresUnsafeAttribute' has an implementation member 'Foo.M()' without 'RequiresUnsafeAttribute'. 'RequiresUnsafeAttribute' annotations must match across all interface implementations or overrides. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafeAttributeMismatch).WithSpan(11, 17, 11, 18).WithArguments("Interface member 'IFoo.M()' with 'RequiresUnsafeAttribute' has an implementation member 'Foo.M()' without 'RequiresUnsafeAttribute'") - ); - } - - [Fact] - public async Task RequiresUnsafeInsideUnsafeBlock() - { - var src = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public int M1() => 0; - - public int M2() - { - unsafe - { - return M1(); - } - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - - [Fact] - public async Task RequiresUnsafeInsideUnsafeMethod() - { - var src = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public int M1() => 0; - - public unsafe int M2() - { - return M1(); - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - - [Fact] - public async Task RequiresUnsafeInsideUnsafeClass() - { - var src = """ - using System.Diagnostics.CodeAnalysis; - - public unsafe class C - { - [RequiresUnsafe] - public int M1() => 0; - - public int M2() - { - return M1(); - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - - [Fact] - public async Task RequiresUnsafeInsideUnsafeProperty() - { - var src = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public int M1() => 0; - - public unsafe int P => M1(); - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - - [Fact] - public async Task RequiresUnsafeInsideUnsafeLocalFunction() - { - var src = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public int M1() => 0; - - public int M2() - { - unsafe int Local() => M1(); - return Local(); - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - - [Fact] - public async Task RequiresUnsafeInsideUnsafeConstructor() - { - var src = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public static int M1() => 0; - - public unsafe C() - { - _ = M1(); - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - - [Fact] - public async Task RequiresUnsafeInsideLambdaInUnsafeMethod() - { - var src = """ - using System; - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public int M1() => 0; - - public unsafe void M2() - { - Action a = () => M1(); - a(); - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - - [Fact] - public async Task RequiresUnsafeInsideAnonymousDelegateInUnsafeMethod() - { - var src = """ - using System; - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public int M1() => 0; - - public unsafe void M2() - { - Action a = delegate { M1(); }; - a(); - } - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - - [Fact] - public async Task RequiresUnsafeInsideUnsafeFieldInitializer() - { - var src = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public static int M1() => 0; - - private static unsafe int _field = M1(); - } - """; - - await VerifyRequiresUnsafeAnalyzer(source: src); - } - } -} -#endif diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.cs index 3ef160f71e6eb9..aab096802e3cf5 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnsafeCodeFixTests.cs @@ -4,7 +4,9 @@ #if DEBUG using System; using System.Threading.Tasks; +using ILLink.CodeFix; using ILLink.Shared; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; @@ -17,6 +19,18 @@ namespace ILLink.RoslynAnalyzer.Tests { public class RequiresUnsafeCodeFixTests { + static Solution SetOptions(Solution solution, ProjectId projectId) + { + var project = solution.GetProject(projectId)!; + var parseOptions = (CSharpParseOptions)project.ParseOptions!; + parseOptions = parseOptions.WithLanguageVersion(LanguageVersion.Preview) + .WithFeatures([.. parseOptions.Features, new("updated-memory-safety-rules", "")]); + var compilationOptions = (CSharpCompilationOptions)project.CompilationOptions!; + compilationOptions = compilationOptions.WithAllowUnsafe(true); + return solution.WithProjectParseOptions(projectId, parseOptions) + .WithProjectCompilationOptions(projectId, compilationOptions); + } + static Task VerifyRequiresUnsafeCodeFix( string source, string fixedSource, @@ -32,18 +46,7 @@ static Task VerifyRequiresUnsafeCodeFix( CodeActionIndex = codeActionIndex }; test.ExpectedDiagnostics.AddRange(baselineExpected); - test.TestState.AnalyzerConfigFiles.Add( - ("/.editorconfig", SourceText.From(@$" -is_global = true -build_property.{MSBuildPropertyOptionNames.EnableUnsafeAnalyzer} = true"))); - // Enable unsafe code compilation - test.SolutionTransforms.Add((solution, projectId) => - { - var project = solution.GetProject(projectId)!; - var compilationOptions = (CSharpCompilationOptions)project.CompilationOptions!; - compilationOptions = compilationOptions.WithAllowUnsafe(true); - return solution.WithProjectCompilationOptions(projectId, compilationOptions); - }); + test.SolutionTransforms.Add(SetOptions); if (numberOfIterations != null) { test.NumberOfIncrementalIterations = numberOfIterations; @@ -61,20 +64,13 @@ public async Task CodeFix_WrapInUnsafeBlock_SimpleStatement() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { int x = M1(); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -82,8 +78,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -95,21 +90,15 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 17, 10, 19) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 17, 9, 21) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -122,20 +111,13 @@ public async Task CodeFix_WrapInUnsafeBlock_ExpressionStatement() public class C { - [RequiresUnsafe] - public static void M1() { } + public static unsafe void M1() { } public void M2() { M1(); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -143,8 +125,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static void M1() { } + public static unsafe void M1() { } public void M2() { @@ -155,21 +136,15 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 9, 10, 11) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 9, 9, 13) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -182,20 +157,13 @@ public async Task CodeFix_WrapInUnsafeBlock_ReturnStatement() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2() { return M1(); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -203,8 +171,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2() { @@ -215,21 +182,15 @@ public int M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 16, 10, 18) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 16, 9, 20) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -242,8 +203,7 @@ public async Task CodeFix_WrapInUnsafeBlock_IfStatement() public class C { - [RequiresUnsafe] - public static bool M1() => true; + public static unsafe bool M1() => true; public void M2() { @@ -253,12 +213,6 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -266,8 +220,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static bool M1() => true; + public static unsafe bool M1() => true; public void M2() { @@ -281,21 +234,15 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 13, 10, 15) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 13, 9, 17) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -308,8 +255,7 @@ public async Task NoWarning_InsideUnsafeBlock() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -319,12 +265,6 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; // No diagnostics expected - already in unsafe block @@ -336,132 +276,210 @@ await VerifyRequiresUnsafeCodeFix( } [Fact] - public async Task NoWarning_InsideUnsafeMethod() + public async Task CodeFix_InsideUnsafeMethod() { var test = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public unsafe void M2() { int x = M1(); } } + """; + + var fixedSource = """ + using System.Diagnostics.CodeAnalysis; - namespace System.Diagnostics.CodeAnalysis + public class C { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } + public static unsafe int M1() => 0; + + public unsafe void M2() + { + int x; + // TODO(unsafe): Baselining unsafe usage + unsafe + { + x = M1(); + } + } } """; - // No diagnostics expected - method has unsafe modifier await VerifyRequiresUnsafeCodeFix( source: test, - fixedSource: test, // No change expected - baselineExpected: Array.Empty(), - fixedExpected: Array.Empty()); + fixedSource: fixedSource, + baselineExpected: new[] { + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 17, 9, 21) + .WithArguments("C.M1()") + }, + fixedExpected: Array.Empty(), + codeActionIndex: 0); } [Fact] - public async Task NoWarning_InsideUnsafeClass() + public async Task CodeFix_InsideUnsafeClass() { var test = """ using System.Diagnostics.CodeAnalysis; public unsafe class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { int x = M1(); } } + """; - namespace System.Diagnostics.CodeAnalysis + var fixedSource = """ + using System.Diagnostics.CodeAnalysis; + + public unsafe class C { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } + public static unsafe int M1() => 0; + + public void M2() + { + int x; + // TODO(unsafe): Baselining unsafe usage + unsafe + { + x = M1(); + } + } } """; - // No diagnostics expected - class has unsafe modifier await VerifyRequiresUnsafeCodeFix( source: test, - fixedSource: test, // No change expected - baselineExpected: Array.Empty(), + fixedSource: fixedSource, + baselineExpected: new[] { + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 17, 9, 21) + .WithArguments("C.M1()") + }, fixedExpected: Array.Empty()); } [Fact] - public async Task NoWarning_InsideUnsafeProperty() + public async Task CodeFix_InsideUnsafeProperty() { var test = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public unsafe int P { get => M1(); } } + """; - namespace System.Diagnostics.CodeAnalysis + var fixedSource = """ + using System.Diagnostics.CodeAnalysis; + + public class C { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } + public static unsafe int M1() => 0; + + public unsafe int P + { + get + { + // TODO(unsafe): Baselining unsafe usage + unsafe + { + return M1(); + } + } + } } """; - // No diagnostics expected - property has unsafe modifier await VerifyRequiresUnsafeCodeFix( source: test, - fixedSource: test, // No change expected - baselineExpected: Array.Empty(), - fixedExpected: Array.Empty()); + fixedSource: fixedSource, + baselineExpected: new[] { + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 16, 9, 20) + .WithArguments("C.M1()") + }, + fixedExpected: Array.Empty(), + codeActionIndex: 1); } [Fact] - public async Task NoWarning_InsideUnsafeLocalFunction() + public async Task CodeFix_InsideUnsafeLocalFunction() { var test = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { unsafe int Local() => M1(); + _ = Local(); } } + """; - namespace System.Diagnostics.CodeAnalysis + var fixedSource = """ + using System.Diagnostics.CodeAnalysis; + + public class C { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } + public static unsafe int M1() => 0; + + public unsafe void M2() + { + unsafe int Local() + { + // TODO(unsafe): Baselining unsafe usage + unsafe + { + return M1(); + } + } + // TODO(unsafe): Baselining unsafe usage + + unsafe + { + _ = Local(); + } + } } """; - // No diagnostics expected - local function has unsafe modifier await VerifyRequiresUnsafeCodeFix( source: test, - fixedSource: test, // No change expected - baselineExpected: Array.Empty(), - fixedExpected: Array.Empty()); + fixedSource: fixedSource, + baselineExpected: new[] { + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 31, 9, 35) + .WithArguments("C.M1()"), + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(11, 13, 11, 20) + .WithArguments("Local()") + }, + fixedExpected: Array.Empty(), + numberOfIterations: 3, + codeActionIndex: 0); } [Fact] @@ -472,17 +490,10 @@ public async Task CodeFix_WrapInUnsafeBlock_ExpressionBodiedMethod() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2() => M1(); } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -490,8 +501,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2() { @@ -502,21 +512,15 @@ public int M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(8, 24, 8, 26) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(7, 24, 7, 28) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -529,17 +533,10 @@ public async Task CodeFix_WrapInUnsafeBlock_ExpressionBodiedVoidMethod() public class C { - [RequiresUnsafe] - public static void M1() { } + public static unsafe void M1() { } public void M2() => M1(); } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -547,8 +544,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static void M1() { } + public static unsafe void M1() { } public void M2() { @@ -559,21 +555,15 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(8, 25, 8, 27) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(7, 25, 7, 29) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -586,17 +576,10 @@ public async Task CodeFix_WrapInUnsafeBlock_ExpressionBodiedDestructor() public class C { - [RequiresUnsafe] - public static void M1() { } + public static unsafe void M1() { } ~C() => M1(); } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -604,8 +587,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static void M1() { } + public static unsafe void M1() { } ~C() { @@ -616,21 +598,15 @@ public static void M1() { } } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(8, 13, 8, 15) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(7, 13, 7, 17) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -643,17 +619,10 @@ public async Task CodeFix_WrapInUnsafeBlock_ExpressionBodiedProperty() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int P => M1(); } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -661,8 +630,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int P { @@ -676,24 +644,18 @@ public int P } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(8, 21, 8, 23) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(7, 21, 7, 25) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty(), - codeActionIndex: 0); + codeActionIndex: 1); } [Fact] @@ -704,20 +666,13 @@ public async Task CodeFix_WrapInUnsafeBlock_ExpressionBodiedAccessor() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int P { get => M1(); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -725,33 +680,32 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int P { - [RequiresUnsafe()] - get => M1(); + get + { + // TODO(unsafe): Baselining unsafe usage + unsafe + { + return M1(); + } + } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 16, 10, 18) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 16, 9, 20) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty(), - codeActionIndex: 0); + codeActionIndex: 1); } [Fact] @@ -764,8 +718,7 @@ public async Task CodeFix_WrapInUnsafeBlock_LocalFunction_ConvertsExpressionBody public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -773,12 +726,6 @@ public void M2() _ = Local(); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -786,8 +733,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -803,21 +749,15 @@ int Local() _ = Local(); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 24, 10, 26) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 24, 9, 28) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -832,8 +772,7 @@ public async Task CodeFix_WrapInUnsafeBlock_ForwardDeclaration() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -842,12 +781,6 @@ public void M2() System.Console.WriteLine(y); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -855,8 +788,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -870,21 +802,15 @@ public void M2() System.Console.WriteLine(y); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 17, 10, 19) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 17, 9, 21) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -899,8 +825,7 @@ public async Task CodeFix_WrapInUnsafeBlock_UnusedVariable() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -909,12 +834,6 @@ public void M2() System.Console.WriteLine(y); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -922,8 +841,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -937,21 +855,15 @@ public void M2() System.Console.WriteLine(y); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 17, 10, 19) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 17, 9, 21) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -965,8 +877,7 @@ public async Task CodeFix_WrapInUnsafeBlock_ForwardDeclaration_VarType() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -974,12 +885,6 @@ public void M2() System.Console.WriteLine(x); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -987,8 +892,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -1001,21 +905,15 @@ public void M2() System.Console.WriteLine(x); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 17, 10, 19) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 17, 9, 21) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -1034,20 +932,13 @@ public class C { private int _field; - [RequiresUnsafe] - public static ref int M1(ref int x) => ref x; + public static unsafe ref int M1(ref int x) => ref x; public void M2() { ref int x = ref M1(ref _field); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; // ref locals can't be forward-declared, so wrap the whole declaration @@ -1058,8 +949,7 @@ public class C { private int _field; - [RequiresUnsafe] - public static ref int M1(ref int x) => ref x; + public static unsafe ref int M1(ref int x) => ref x; public void M2() { @@ -1070,21 +960,15 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(12, 25, 12, 27) - .WithArguments("C.M1(ref Int32)", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(11, 25, 11, 39) + .WithArguments("C.M1(ref int)") }, fixedExpected: Array.Empty()); } @@ -1093,7 +977,7 @@ await VerifyRequiresUnsafeCodeFix( public async Task CodeFix_WrapInUnsafeBlock_RefLocalFromUnsafeAs_NoForwardDeclaration() { // Pattern matching real-world usage: ref byte x = ref Unsafe.As(ref source) - // The Unsafe.As call has [RequiresUnsafe], and the result is assigned to a ref local. + // The Unsafe.As call is unsafe, and the result is assigned to a ref local. // When the ref local is used after the declaration, those statements must be included // in the unsafe block since ref locals can't be forward-declared. var test = """ @@ -1110,18 +994,11 @@ public void M2() } } - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } - namespace System.Runtime.CompilerServices { public static class Unsafe { - [RequiresUnsafe] - public static ref TTo As(ref TFrom source) => throw null!; + public static unsafe ref TTo As(ref TFrom source) => throw null!; } } """; @@ -1145,18 +1022,11 @@ public void M2() } } - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } - namespace System.Runtime.CompilerServices { public static class Unsafe { - [RequiresUnsafe] - public static ref TTo As(ref TFrom source) => throw null!; + public static unsafe ref TTo As(ref TFrom source) => throw null!; } } """; @@ -1165,9 +1035,9 @@ await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(9, 26, 9, 47) - .WithArguments("System.Runtime.CompilerServices.Unsafe.As(ref TFrom)", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 26, 9, 54) + .WithArguments("System.Runtime.CompilerServices.Unsafe.As(ref char)") }, fixedExpected: Array.Empty()); } @@ -1193,18 +1063,11 @@ public void M2() } } - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } - namespace System.Runtime.CompilerServices { public static class Unsafe { - [RequiresUnsafe] - public static ref TTo As(ref TFrom source) => throw null!; + public static unsafe ref TTo As(ref TFrom source) => throw null!; public static ref T Add(ref T source, int elementOffset) => throw null!; } } @@ -1230,18 +1093,11 @@ public void M2() } } - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } - namespace System.Runtime.CompilerServices { public static class Unsafe { - [RequiresUnsafe] - public static ref TTo As(ref TFrom source) => throw null!; + public static unsafe ref TTo As(ref TFrom source) => throw null!; public static ref T Add(ref T source, int elementOffset) => throw null!; } } @@ -1251,9 +1107,9 @@ await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(9, 34, 9, 57) - .WithArguments("System.Runtime.CompilerServices.Unsafe.As(ref TFrom)", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 34, 9, 64) + .WithArguments("System.Runtime.CompilerServices.Unsafe.As(ref byte)") }, fixedExpected: Array.Empty()); } @@ -1280,18 +1136,11 @@ public int M2() } } - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } - namespace System.Runtime.CompilerServices { public static class Unsafe { - [RequiresUnsafe] - public static ref TTo As(ref TFrom source) => throw null!; + public static unsafe ref TTo As(ref TFrom source) => throw null!; public static ref T Add(ref T source, int elementOffset) => throw null!; } } @@ -1318,18 +1167,11 @@ public int M2() } } - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } - namespace System.Runtime.CompilerServices { public static class Unsafe { - [RequiresUnsafe] - public static ref TTo As(ref TFrom source) => throw null!; + public static unsafe ref TTo As(ref TFrom source) => throw null!; public static ref T Add(ref T source, int elementOffset) => throw null!; } } @@ -1339,9 +1181,9 @@ await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(9, 34, 9, 57) - .WithArguments("System.Runtime.CompilerServices.Unsafe.As(ref TFrom)", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 34, 9, 64) + .WithArguments("System.Runtime.CompilerServices.Unsafe.As(ref byte)") }, fixedExpected: Array.Empty()); } @@ -1355,8 +1197,7 @@ public async Task CodeFix_WrapInUnsafeBlock_RefReadonlyLocal_NoForwardDeclaratio public class C { - [RequiresUnsafe] - public static ref readonly int M1(in int x) => ref x; + public static unsafe ref readonly int M1(in int x) => ref x; public void M2() { @@ -1364,12 +1205,6 @@ public void M2() ref readonly int x = ref M1(in value); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -1377,8 +1212,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static ref readonly int M1(in int x) => ref x; + public static unsafe ref readonly int M1(in int x) => ref x; public void M2() { @@ -1390,21 +1224,15 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(11, 34, 11, 36) - .WithArguments("C.M1(in Int32)", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(10, 34, 10, 46) + .WithArguments("C.M1(in int)") }, fixedExpected: Array.Empty()); } @@ -1412,16 +1240,14 @@ await VerifyRequiresUnsafeCodeFix( [Fact] public async Task CodeFix_WrapInUnsafeBlock_NotOfferedForStatementsWithPragmaDirectives() { - // When statements to wrap have #pragma directives in their leading trivia, - // the "Wrap in unsafe block" fix should NOT be offered because it would - // destroy the directive structure. Only the "Add attribute" fix should be available. + // When statements to wrap have #pragma directives after the diagnostic statement, + // the unsafe block should not include the directive trivia. var test = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public void M2() { @@ -1433,27 +1259,23 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; - // The fix adds [RequiresUnsafe] attribute to the method instead of wrapping in unsafe block var fixedSource = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; - [RequiresUnsafe()] - public void M2() + public unsafe void M2() { - int x = M1(); + int x; + // TODO(unsafe): Baselining unsafe usage + unsafe + { + x = M1(); + } #pragma warning disable CS0168 if (x > 0) #pragma warning restore CS0168 @@ -1461,70 +1283,104 @@ public void M2() } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; - // Use codeActionIndex: 0 since only "Add attribute" is offered (not "Wrap in unsafe block") await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 17, 10, 19) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 17, 9, 21) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty(), + numberOfIterations: 2, codeActionIndex: 0); } [Fact] - public async Task CodeFix_AddRequiresUnsafeAttribute_Method() + public async Task CodeFix_AddUnsafeModifier_Method() { var test = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2() { return M1(); } } + """; - namespace System.Diagnostics.CodeAnalysis + var fixedSource = """ + using System.Diagnostics.CodeAnalysis; + + public class C { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } + public static unsafe int M1() => 0; + + public unsafe int M2() + { + // TODO(unsafe): Baselining unsafe usage + unsafe + { + return M1(); + } + } } """; - var fixedSource = """ + var addUnsafeTest = new VerifyCS.Test + { + TestCode = test, + FixedCode = fixedSource, + CodeActionIndex = 0, + NumberOfIncrementalIterations = 2, + NumberOfFixAllIterations = 2 + }; + addUnsafeTest.ExpectedDiagnostics.Add( + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 16, 9, 20) + .WithArguments("C.M1()")); + addUnsafeTest.SolutionTransforms.Add(SetOptions); + await addUnsafeTest.RunAsync(); + } + + [Fact] + public async Task CodeFix_WrapInUnsafeBlock_Method() + { + var test = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; - [RequiresUnsafe()] public int M2() { return M1(); } } + """; - namespace System.Diagnostics.CodeAnalysis + var fixedSource = """ + using System.Diagnostics.CodeAnalysis; + + public class C { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } + public static unsafe int M1() => 0; + + public int M2() + { + // TODO(unsafe): Baselining unsafe usage + unsafe + { + return M1(); + } + } } """; @@ -1532,48 +1388,31 @@ public sealed class RequiresUnsafeAttribute : Attribute { } { TestCode = test, FixedCode = fixedSource, - CodeActionIndex = 0 + CodeActionIndex = 1 }; addAttributeTest.ExpectedDiagnostics.Add( - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 16, 10, 18) - .WithArguments("C.M1()", "", "")); - addAttributeTest.TestState.AnalyzerConfigFiles.Add( - ("/.editorconfig", SourceText.From(@$" -is_global = true -build_property.{MSBuildPropertyOptionNames.EnableUnsafeAnalyzer} = true"))); - addAttributeTest.SolutionTransforms.Add((solution, projectId) => - { - var project = solution.GetProject(projectId)!; - var compilationOptions = (CSharpCompilationOptions)project.CompilationOptions!; - compilationOptions = compilationOptions.WithAllowUnsafe(true); - return solution.WithProjectCompilationOptions(projectId, compilationOptions); - }); + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 16, 9, 20) + .WithArguments("C.M1()")); + addAttributeTest.SolutionTransforms.Add(SetOptions); await addAttributeTest.RunAsync(); } [Fact] - public async Task CodeFix_AddRequiresUnsafeAttribute_Constructor() + public async Task CodeFix_WrapInUnsafeBlock_Constructor() { var test = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static void M1() { } + public static unsafe void M1() { } public C() { M1(); } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -1581,44 +1420,30 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static void M1() { } + public static unsafe void M1() { } - [RequiresUnsafe()] public C() { - M1(); + // TODO(unsafe): Baselining unsafe usage + unsafe + { + M1(); + } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var addAttributeTest = new VerifyCS.Test { TestCode = test, FixedCode = fixedSource, - CodeActionIndex = 0 + CodeActionIndex = 1 }; addAttributeTest.ExpectedDiagnostics.Add( - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(10, 9, 10, 11) - .WithArguments("C.M1()", "", "")); - addAttributeTest.TestState.AnalyzerConfigFiles.Add( - ("/.editorconfig", SourceText.From(@$" -is_global = true -build_property.{MSBuildPropertyOptionNames.EnableUnsafeAnalyzer} = true"))); - addAttributeTest.SolutionTransforms.Add((solution, projectId) => - { - var project = solution.GetProject(projectId)!; - var compilationOptions = (CSharpCompilationOptions)project.CompilationOptions!; - compilationOptions = compilationOptions.WithAllowUnsafe(true); - return solution.WithProjectCompilationOptions(projectId, compilationOptions); - }); + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(9, 9, 9, 13) + .WithArguments("C.M1()")); + addAttributeTest.SolutionTransforms.Add(SetOptions); await addAttributeTest.RunAsync(); } @@ -1627,14 +1452,13 @@ public async Task CodeFix_WrapInUnsafeBlock_NotOfferedForExpressionBodyWithPrepr { // When an expression-bodied member has preprocessor directives (#if/#else/#endif), // the "Wrap in unsafe block" fix should NOT be offered because it would destroy - // the conditional compilation structure. Only the "Add attribute" fix should be available. + // the conditional compilation structure. var test = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2() #if SOME_DEFINE @@ -1643,40 +1467,26 @@ public int M2() => M1(); #endif } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; - // The fix should add [RequiresUnsafe] attribute (CodeActionIndex = 0) - // The "Wrap in unsafe block" fix (CodeActionIndex = 1) should NOT be available var fixedSource = """ using System.Diagnostics.CodeAnalysis; public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; - [RequiresUnsafe()] - public int M2() + public unsafe int M2() #if SOME_DEFINE => 42; #else => M1(); #endif } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; + // The "Wrap in unsafe block" fix should NOT be available. + var addAttributeTest = new VerifyCS.Test { TestCode = test, @@ -1686,20 +1496,14 @@ public sealed class RequiresUnsafeAttribute : Attribute { } NumberOfFixAllIterations = 1 }; addAttributeTest.ExpectedDiagnostics.Add( - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(12, 12, 12, 14) - .WithArguments("C.M1()", "", "")); - addAttributeTest.TestState.AnalyzerConfigFiles.Add( - ("/.editorconfig", SourceText.From(@$" -is_global = true -build_property.{MSBuildPropertyOptionNames.EnableUnsafeAnalyzer} = true"))); - addAttributeTest.SolutionTransforms.Add((solution, projectId) => - { - var project = solution.GetProject(projectId)!; - var compilationOptions = (CSharpCompilationOptions)project.CompilationOptions!; - compilationOptions = compilationOptions.WithAllowUnsafe(true); - return solution.WithProjectCompilationOptions(projectId, compilationOptions); - }); + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(11, 12, 11, 16) + .WithArguments("C.M1()")); + addAttributeTest.FixedState.ExpectedDiagnostics.Add( + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(11, 12, 11, 16) + .WithArguments("C.M1()")); + addAttributeTest.SolutionTransforms.Add(SetOptions); await addAttributeTest.RunAsync(); } @@ -1711,8 +1515,7 @@ public async Task CodeFix_WrapInUnsafeBlock_SwitchCaseSection() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2(int x) { @@ -1725,12 +1528,6 @@ public int M2(int x) } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -1738,8 +1535,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2(int x) { @@ -1756,21 +1552,15 @@ public int M2(int x) } } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(13, 24, 13, 26) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(12, 24, 12, 28) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -1783,8 +1573,7 @@ public async Task CodeFix_WrapInUnsafeBlock_IfStatementWithoutBraces() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2(bool condition) { @@ -1793,12 +1582,6 @@ public int M2(bool condition) return 0; } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -1806,8 +1589,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2(bool condition) { @@ -1822,21 +1604,15 @@ public int M2(bool condition) return 0; } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(11, 20, 11, 22) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(10, 20, 10, 24) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } @@ -1849,8 +1625,7 @@ public async Task CodeFix_WrapInUnsafeBlock_ExpressionBodiedLocalFunction() public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2() { @@ -1859,12 +1634,6 @@ public int M2() return LocalFunc() + x; } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; var fixedSource = """ @@ -1872,8 +1641,7 @@ public sealed class RequiresUnsafeAttribute : Attribute { } public class C { - [RequiresUnsafe] - public static int M1() => 0; + public static unsafe int M1() => 0; public int M2() { @@ -1890,21 +1658,15 @@ static int LocalFunc() return LocalFunc() + x; } } - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } """; await VerifyRequiresUnsafeCodeFix( source: test, fixedSource: fixedSource, baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.RequiresUnsafe) - .WithSpan(11, 35, 11, 37) - .WithArguments("C.M1()", "", "") + DiagnosticResult.CompilerError(RequiresUnsafeCodeFixProvider.UnsafeMemberOperationDiagnosticId) + .WithSpan(10, 35, 10, 39) + .WithArguments("C.M1()") }, fixedExpected: Array.Empty()); } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs index 6e1d5b070749f9..5bfbc2ac5dcd6b 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs @@ -26,9 +26,6 @@ private static ImmutableArray CreateSupportedDiagnosticAnaly builder.Add(new RequiresAssemblyFilesAnalyzer()); builder.Add(new RequiresUnreferencedCodeAnalyzer()); builder.Add(new DynamicallyAccessedMembersAnalyzer()); -#if DEBUG - builder.Add(new RequiresUnsafeAnalyzer()); -#endif return builder.ToImmutable(); } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMethodMissingRequiresUnsafeTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMethodMissingRequiresUnsafeTests.cs deleted file mode 100644 index b606022621af78..00000000000000 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnsafeMethodMissingRequiresUnsafeTests.cs +++ /dev/null @@ -1,278 +0,0 @@ -// 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; -using System.Threading.Tasks; -using ILLink.Shared; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Text; -using Xunit; -using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpCodeFixVerifier< - ILLink.RoslynAnalyzer.UnsafeMethodMissingRequiresUnsafeAnalyzer, - ILLink.CodeFix.UnsafeMethodMissingRequiresUnsafeCodeFixProvider>; - -namespace ILLink.RoslynAnalyzer.Tests -{ - public class UnsafeMethodMissingRequiresUnsafeTests - { - static readonly string RequiresUnsafeAttributeDefinition = """ - - namespace System.Diagnostics.CodeAnalysis - { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] - public sealed class RequiresUnsafeAttribute : Attribute { } - } - """; - - static Task VerifyCodeFix( - string source, - string fixedSource, - DiagnosticResult[] baselineExpected, - DiagnosticResult[] fixedExpected, - int? numberOfIterations = null) - { - var test = new VerifyCS.Test { - TestCode = source, - FixedCode = fixedSource, - }; - test.ExpectedDiagnostics.AddRange(baselineExpected); - test.TestState.AnalyzerConfigFiles.Add( - ("/.editorconfig", SourceText.From(@$" -is_global = true -build_property.{MSBuildPropertyOptionNames.EnableUnsafeAnalyzer} = true"))); - test.SolutionTransforms.Add((solution, projectId) => { - var project = solution.GetProject(projectId)!; - var compilationOptions = (CSharpCompilationOptions)project.CompilationOptions!; - compilationOptions = compilationOptions.WithAllowUnsafe(true); - return solution.WithProjectCompilationOptions(projectId, compilationOptions); - }); - if (numberOfIterations != null) { - test.NumberOfIncrementalIterations = numberOfIterations; - test.NumberOfFixAllIterations = numberOfIterations; - } - test.FixedState.ExpectedDiagnostics.AddRange(fixedExpected); - return test.RunAsync(); - } - - static Task VerifyNoDiagnostic(string source) - { - var test = new VerifyCS.Test { - TestCode = source, - FixedCode = source, - }; - test.TestState.AnalyzerConfigFiles.Add( - ("/.editorconfig", SourceText.From(@$" -is_global = true -build_property.{MSBuildPropertyOptionNames.EnableUnsafeAnalyzer} = true"))); - test.SolutionTransforms.Add((solution, projectId) => { - var project = solution.GetProject(projectId)!; - var compilationOptions = (CSharpCompilationOptions)project.CompilationOptions!; - compilationOptions = compilationOptions.WithAllowUnsafe(true); - return solution.WithProjectCompilationOptions(projectId, compilationOptions); - }); - return test.RunAsync(); - } - - [Fact] - public async Task MethodAlreadyAttributed_NoDiagnostic() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public unsafe int* M() => default; - } - """ + RequiresUnsafeAttributeDefinition; - - await VerifyNoDiagnostic(source); - } - - [Fact] - public async Task UnsafeMethodWithoutPointerTypes_NoDiagnostic() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - public unsafe void M() { } - } - """ + RequiresUnsafeAttributeDefinition; - - await VerifyNoDiagnostic(source); - } - - [Fact] - public async Task NonUnsafeMethod_NoDiagnostic() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - public void M() { } - } - """ + RequiresUnsafeAttributeDefinition; - - await VerifyNoDiagnostic(source); - } - - [Fact] - public async Task CodeFix_MethodReturningPointer_AddsAttribute() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - public unsafe int* M() => default; - } - """ + RequiresUnsafeAttributeDefinition; - - var fixedSource = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public unsafe int* M() => default; - } - """ + RequiresUnsafeAttributeDefinition; - - await VerifyCodeFix( - source, - fixedSource, - baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.UnsafeMethodMissingRequiresUnsafe) - .WithSpan(5, 24, 5, 25) - .WithArguments("C.M()") - .WithSeverity(DiagnosticSeverity.Info) - }, - fixedExpected: Array.Empty ()); - } - - [Fact] - public async Task CodeFix_MethodTakingPointerParam_AddsAttribute() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - public unsafe void M(int* p) { } - } - """ + RequiresUnsafeAttributeDefinition; - - var fixedSource = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public unsafe void M(int* p) { } - } - """ + RequiresUnsafeAttributeDefinition; - - await VerifyCodeFix( - source, - fixedSource, - baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.UnsafeMethodMissingRequiresUnsafe) - .WithSpan(5, 24, 5, 25) - .WithArguments("C.M(Int32*)") - .WithSeverity(DiagnosticSeverity.Info) - }, - fixedExpected: Array.Empty ()); - } - - [Fact] - public async Task CodeFix_MethodTakingFunctionPointer_AddsAttribute() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - public unsafe void M(delegate* f) { } - } - """ + RequiresUnsafeAttributeDefinition; - - var fixedSource = """ - using System.Diagnostics.CodeAnalysis; - - public class C - { - [RequiresUnsafe] - public unsafe void M(delegate* f) { } - } - """ + RequiresUnsafeAttributeDefinition; - - await VerifyCodeFix( - source, - fixedSource, - baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.UnsafeMethodMissingRequiresUnsafe) - .WithSpan(5, 24, 5, 25) - .WithArguments("C.M(delegate*)") - .WithSeverity(DiagnosticSeverity.Info) - }, - fixedExpected: Array.Empty ()); - } - - [Fact] - public async Task CodeFix_PropertyReturningPointer_AddsAttribute() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public unsafe class C - { - public int* P => default; - } - """ + RequiresUnsafeAttributeDefinition; - - var fixedSource = """ - using System.Diagnostics.CodeAnalysis; - - public unsafe class C - { - [RequiresUnsafe] - public int* P => default; - } - """ + RequiresUnsafeAttributeDefinition; - - await VerifyCodeFix( - source, - fixedSource, - baselineExpected: new[] { - VerifyCS.Diagnostic(DiagnosticId.UnsafeMethodMissingRequiresUnsafe) - .WithSpan(5, 22, 5, 29) - .WithArguments("C.P.get") - .WithSeverity(DiagnosticSeverity.Info) - }, - fixedExpected: Array.Empty ()); - } - - [Fact] - public async Task PropertyAlreadyAttributed_NoDiagnostic() - { - var source = """ - using System.Diagnostics.CodeAnalysis; - - public unsafe class C - { - [RequiresUnsafe] - public int* P => default; - } - """ + RequiresUnsafeAttributeDefinition; - - await VerifyNoDiagnostic(source); - } - } -} -#endif