| File: UnsafeMemberMissingSafetyDocumentationAnalyzer.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>IL5005</c> when an explicit unsafe member contract has no <c><safety></c> XML documentation. /// The diagnostic is disabled by default while this migration tooling remains experimental. /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class UnsafeMemberMissingSafetyDocumentationAnalyzer : DiagnosticAnalyzer { public const string PointerSignatureProperty = nameof(PointerSignatureProperty); public const string RequiresExplicitSafetyModifierProperty = nameof(RequiresExplicitSafetyModifierProperty); private static readonly DiagnosticDescriptor s_rule = DiagnosticDescriptors.GetDiagnosticDescriptor( DiagnosticId.UnsafeMemberMissingSafetyDocumentation, 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) { if (!UnsafeMigrationAnalyzerHelpers.IsUnsafeContractMember(symbol)) return; foreach (SyntaxNode declaration in UnsafeMigrationAnalyzerHelpers.GetDeclarations(symbol, cancellationToken)) { SyntaxToken unsafeModifier = UnsafeMigrationSyntaxHelpers.GetModifier( declaration, SyntaxKind.UnsafeKeyword); if (unsafeModifier == default || UnsafeMigrationAnalyzerHelpers.HasSafetyDocumentation(declaration, symbol, cancellationToken)) { continue; } var properties = ImmutableDictionary<string, string?>.Empty; // Pointer signatures were caller-unsafe under the legacy rules, so the fixer must retain unsafe. if (UnsafeMigrationAnalyzerHelpers.HasPointerOrFunctionPointerSignature(symbol)) properties = properties.Add(PointerSignatureProperty, bool.TrueString); // Explicit and extended layouts still require an explicit safe/unsafe marker after migration. if (UnsafeMigrationAnalyzerHelpers.RequiresExplicitSafetyModifier(declaration, symbol)) properties = properties.Add(RequiresExplicitSafetyModifierProperty, bool.TrueString); reportDiagnostic(Diagnostic.Create( s_rule, unsafeModifier.GetLocation(), properties: properties)); } } } } #endif