| File: PointerSignatureRequiresUnsafeAnalyzer.cs | Web Access |
| Project: src\runtime\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; using System.Collections.Immutable; using System.Threading; using ILLink.Shared; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace ILLink.RoslynAnalyzer { /// <summary> /// Reports <c>IL5006</c> for pointer or function-pointer signatures that lost their legacy caller-unsafe contract. /// The diagnostic is disabled by default while this migration tooling remains experimental. /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class PointerSignatureRequiresUnsafeAnalyzer : DiagnosticAnalyzer { private static readonly DiagnosticDescriptor s_rule = DiagnosticDescriptors.GetDiagnosticDescriptor( DiagnosticId.PointerSignatureRequiresUnsafe, isEnabledByDefault: false); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [s_rule]; public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); if (!System.Diagnostics.Debugger.IsAttached) context.EnableConcurrentExecution(); context.RegisterSymbolAction( AnalyzeSymbol, SymbolKind.Method, SymbolKind.Property, SymbolKind.Field, SymbolKind.Event); context.RegisterOperationAction(AnalyzeLocalFunction, OperationKind.LocalFunction); } private static void AnalyzeSymbol(SymbolAnalysisContext context) => AnalyzeSymbol(context.Symbol, context.CancellationToken, context.ReportDiagnostic); private static void AnalyzeLocalFunction(OperationAnalysisContext context) => AnalyzeSymbol( ((ILocalFunctionOperation)context.Operation).Symbol, context.CancellationToken, context.ReportDiagnostic); private static void AnalyzeSymbol( ISymbol symbol, CancellationToken cancellationToken, Action<Diagnostic> reportDiagnostic) { // Property and event accessors are represented by method symbols, but their containing declaration // already carries the pointer-signature contract. if (symbol is IMethodSymbol { AssociatedSymbol: not null } || !UnsafeMigrationAnalyzerHelpers.HasPointerOrFunctionPointerSignature(symbol)) { return; } foreach (SyntaxNode declaration in UnsafeMigrationAnalyzerHelpers.GetDeclarations(symbol, cancellationToken)) { // safe is an explicit audited contract on the limited declaration kinds where Roslyn permits it. if (UnsafeMigrationSyntaxHelpers.HasModifier(declaration, SyntaxKind.UnsafeKeyword) || UnsafeMigrationSyntaxHelpers.HasSafeModifier(declaration) || UnsafeMigrationAnalyzerHelpers.HasSafetyDocumentation(declaration, symbol, cancellationToken)) { continue; } reportDiagnostic(Diagnostic.Create( s_rule, UnsafeMigrationAnalyzerHelpers.GetIdentifierLocation(declaration))); } } } } #endif