| File: Analyzers\NativeMarshallingAttributeAnalyzer.cs | Web Access |
| Project: src\runtime\src\libraries\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj (Microsoft.Interop.LibraryImportGenerator) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.DotnetRuntime.Extensions; using static Microsoft.Interop.Analyzers.AnalyzerDiagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.Interop.Analyzers { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class NativeMarshallingAttributeAnalyzer : DiagnosticAnalyzer { private const string Category = "Usage"; public static readonly DiagnosticDescriptor MarshallerEntryPointTypeMustHaveCustomMarshallerAttributeWithMatchingManagedTypeRule = DiagnosticDescriptorHelper.Create( Ids.InvalidNativeMarshallingAttributeUsage, GetResourceString(nameof(SR.InvalidNativeMarshallingAttributeUsageTitle)), GetResourceString(nameof(SR.EntryPointTypeMustHaveCustomMarshallerAttributeWithMatchingManagedTypeMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: GetResourceString(nameof(SR.EntryPointTypeMustHaveCustomMarshallerAttributeWithMatchingManagedTypeDescription))); public static readonly DiagnosticDescriptor MarshallerEntryPointTypeMustBeNonNullRule = DiagnosticDescriptorHelper.Create( Ids.InvalidNativeMarshallingAttributeUsage, GetResourceString(nameof(SR.InvalidNativeMarshallingAttributeUsageTitle)), GetResourceString(nameof(SR.EntryPointTypeMustBeNonNullMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: GetResourceString(nameof(SR.EntryPointTypeMustBeNonNullDescription))); public static readonly DiagnosticDescriptor GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule = DiagnosticDescriptorHelper.Create( Ids.InvalidNativeMarshallingAttributeUsage, GetResourceString(nameof(SR.InvalidNativeMarshallingAttributeUsageTitle)), GetResourceString(nameof(SR.GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: GetResourceString(nameof(SR.GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityDescription))); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create( MarshallerEntryPointTypeMustHaveCustomMarshallerAttributeWithMatchingManagedTypeRule, MarshallerEntryPointTypeMustBeNonNullRule, GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule); public override void Initialize(AnalysisContext context) { // Don't analyze generated code context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); context.RegisterCompilationStartAction(PrepareForAnalysis); } private void PrepareForAnalysis(CompilationStartAnalysisContext context) { var perCompilationAnalyzer = new PerCompilationAnalyzer(context.Compilation); context.RegisterOperationAction(perCompilationAnalyzer.AnalyzeAttribute, OperationKind.Attribute); } private sealed partial class PerCompilationAnalyzer { private readonly Compilation _compilation; public PerCompilationAnalyzer(Compilation compilation) { _compilation = compilation; } public void AnalyzeAttribute(OperationAnalysisContext context) { IAttributeOperation attr = (IAttributeOperation)context.Operation; if (attr.Operation is IObjectCreationOperation attrCreation && attrCreation.Type.ToDisplayString() == TypeNames.NativeMarshallingAttribute) { IArgumentOperation marshallerEntryPointTypeArgument = attrCreation.GetArgumentByOrdinal(0); if (marshallerEntryPointTypeArgument.Value.IsNullLiteralOperation()) { DiagnosticReporter diagnosticFactory = DiagnosticReporter.CreateForLocation(marshallerEntryPointTypeArgument.Value.Syntax.GetLocation(), context.ReportDiagnostic); diagnosticFactory.CreateAndReportDiagnostic( MarshallerEntryPointTypeMustBeNonNullRule, GetSymbolType(context.ContainingSymbol).ToDisplayString()); } if (marshallerEntryPointTypeArgument.Value is ITypeOfOperation typeOfOp) { AnalyzeManagedTypeMarshallingInfo( GetSymbolType(context.ContainingSymbol), DiagnosticReporter.CreateForLocation(((TypeOfExpressionSyntax)typeOfOp.Syntax).Type.GetLocation(), context.ReportDiagnostic), (INamedTypeSymbol?)typeOfOp.TypeOperand); } } } private void AnalyzeManagedTypeMarshallingInfo( ITypeSymbol managedType, DiagnosticReporter diagnosticFactory, INamedTypeSymbol? entryType) { if (!ManualTypeMarshallingHelper.HasEntryPointMarshallerAttribute(entryType)) { diagnosticFactory.CreateAndReportDiagnostic( MarshallerEntryPointTypeMustHaveCustomMarshallerAttributeWithMatchingManagedTypeRule, entryType.ToDisplayString(), managedType.ToDisplayString()); return; } bool isLinearCollectionMarshaller = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryType); if (entryType.IsUnboundGenericType) { if (managedType is not INamedTypeSymbol namedManagedType) { diagnosticFactory.CreateAndReportDiagnostic( GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule, entryType.ToDisplayString(), managedType.ToDisplayString()); return; } if (!ManualTypeMarshallingHelper.TryResolveEntryPointType( namedManagedType, entryType, isLinearCollectionMarshaller, (managedType, entryType) => diagnosticFactory.CreateAndReportDiagnostic( GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule, entryType.ToDisplayString(), managedType.ToDisplayString()), out ITypeSymbol resolvedEntryType)) { return; } entryType = (INamedTypeSymbol)resolvedEntryType; } if (!ManualTypeMarshallingHelper.TryGetMarshallersFromEntryTypeIgnoringElements( entryType, managedType, _compilation, (entryType, managedType) => diagnosticFactory.CreateAndReportDiagnostic( GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule, entryType.ToDisplayString(), managedType.ToDisplayString()), out _)) { diagnosticFactory.CreateAndReportDiagnostic( MarshallerEntryPointTypeMustHaveCustomMarshallerAttributeWithMatchingManagedTypeRule, entryType.ToDisplayString(), managedType.ToDisplayString()); } } private static ITypeSymbol GetSymbolType(ISymbol symbol) { return symbol switch { IMethodSymbol method => method.ReturnType, IParameterSymbol param => param.Type, IFieldSymbol field => field.Type, ITypeSymbol type => type, _ => throw new InvalidOperationException() }; } } } }