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