File: UnsafeMethodMissingRequiresUnsafeAnalyzer.cs
Web Access
Project: src\src\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj (ILLink.RoslynAnalyzer)
// 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<DiagnosticDescriptor> 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