Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.DotnetRuntime.Extensions;

namespace Microsoft.Interop.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class VtableIndexStubDiagnosticsAnalyzer : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
ImmutableArray.Create(
GeneratorDiagnostics.InvalidAttributedMethodSignature,
GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers,
GeneratorDiagnostics.ReturnConfigurationNotSupported,
GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttribute,
GeneratorDiagnostics.InvalidStringMarshallingConfigurationOnMethod,
GeneratorDiagnostics.InvalidExceptionMarshallingConfiguration,
GeneratorDiagnostics.ConfigurationNotSupported,
GeneratorDiagnostics.ParameterTypeNotSupported,
GeneratorDiagnostics.ReturnTypeNotSupported,
GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails,
GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails,
GeneratorDiagnostics.ParameterConfigurationNotSupported,
GeneratorDiagnostics.MarshalAsParameterConfigurationNotSupported,
GeneratorDiagnostics.MarshalAsReturnConfigurationNotSupported,
GeneratorDiagnostics.ConfigurationValueNotSupported,
GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported,
GeneratorDiagnostics.UnnecessaryParameterMarshallingInfo,
GeneratorDiagnostics.UnnecessaryReturnMarshallingInfo,
GeneratorDiagnostics.GeneratedComInterfaceUsageDoesNotFollowBestPractices);

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(compilationContext =>
{
INamedTypeSymbol? virtualMethodIndexAttrType = compilationContext.Compilation.GetBestTypeByMetadataName(TypeNames.VirtualMethodIndexAttribute);
if (virtualMethodIndexAttrType is null)
return;

StubEnvironment env = new StubEnvironment(
compilationContext.Compilation,
compilationContext.Compilation.GetEnvironmentFlags());

compilationContext.RegisterSymbolAction(symbolContext =>
{
IMethodSymbol method = (IMethodSymbol)symbolContext.Symbol;
AttributeData? virtualMethodIndexAttr = null;
foreach (AttributeData attr in method.GetAttributes())
{
if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, virtualMethodIndexAttrType))
{
virtualMethodIndexAttr = attr;
break;
}
}

if (virtualMethodIndexAttr is null)
return;

foreach (SyntaxReference syntaxRef in method.DeclaringSyntaxReferences)
{
if (syntaxRef.GetSyntax(symbolContext.CancellationToken) is MethodDeclarationSyntax methodSyntax)
{
AnalyzeMethod(symbolContext, methodSyntax, method, env);
break;
}
}
}, SymbolKind.Method);
});
}

private static void AnalyzeMethod(SymbolAnalysisContext context, MethodDeclarationSyntax methodSyntax, IMethodSymbol method, StubEnvironment env)
{
DiagnosticInfo? invalidMethodDiagnostic = GetDiagnosticIfInvalidMethodForGeneration(methodSyntax, method);
if (invalidMethodDiagnostic is not null)
{
context.ReportDiagnostic(invalidMethodDiagnostic.ToDiagnostic());
return;
}

SourceAvailableIncrementalMethodStubGenerationContext stubContext = VtableIndexStubGenerator.CalculateStubInformation(methodSyntax, method, env, context.CancellationToken);

if (stubContext.VtableIndexData.Direction is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional)
{
var (_, diagnostics) = VirtualMethodPointerStubGenerator.GenerateManagedToNativeStub(stubContext, VtableIndexStubGeneratorHelpers.GetGeneratorResolver);
foreach (DiagnosticInfo diag in diagnostics)
context.ReportDiagnostic(diag.ToDiagnostic());
}

if (stubContext.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional)
{
var (_, diagnostics) = VirtualMethodPointerStubGenerator.GenerateNativeToManagedStub(stubContext, VtableIndexStubGeneratorHelpers.GetGeneratorResolver);
foreach (DiagnosticInfo diag in diagnostics)
context.ReportDiagnostic(diag.ToDiagnostic());
}
}

internal static DiagnosticInfo? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax methodSyntax, IMethodSymbol method)
{
// Verify the method has no generic types or defined implementation
// and is not marked static or sealed
if (methodSyntax.TypeParameterList is not null
|| methodSyntax.Body is not null
|| methodSyntax.Modifiers.Any(SyntaxKind.StaticKeyword)
|| methodSyntax.Modifiers.Any(SyntaxKind.SealedKeyword))
{
return DiagnosticInfo.Create(GeneratorDiagnostics.InvalidAttributedMethodSignature, methodSyntax.Identifier.GetLocation(), method.Name);
}

// Verify that the types the method is declared in are marked partial.
for (SyntaxNode? parentNode = methodSyntax.Parent; parentNode is TypeDeclarationSyntax typeDecl; parentNode = parentNode.Parent)
{
if (!typeDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
{
return DiagnosticInfo.Create(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers, methodSyntax.Identifier.GetLocation(), method.Name, typeDecl.Identifier);
}
}

// Verify the method does not have a ref return
if (method.ReturnsByRef || method.ReturnsByRefReadonly)
{
return DiagnosticInfo.Create(GeneratorDiagnostics.ReturnConfigurationNotSupported, methodSyntax.Identifier.GetLocation(), "ref return", method.ToDisplayString());
}

// Verify there is an [UnmanagedObjectUnwrapperAttribute<TMapper>]
if (!method.ContainingType.GetAttributes().Any(att => att.AttributeClass.IsOfType(TypeNames.UnmanagedObjectUnwrapperAttribute)))
{
return DiagnosticInfo.Create(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttribute, methodSyntax.Identifier.GetLocation(), method.Name);
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,16 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
{
return
(
Diagnostics: ImmutableArray<DiagnosticInfo>.Empty.ToSequenceEqual(),
InterfaceContexts: ImmutableArray<ComInterfaceContext>.Empty.ToSequenceEqual(),
MethodContexts: ImmutableArray<ComMethodContext>.Empty.ToSequenceEqual()
);
}
StubEnvironment stubEnvironment = input.Right;
List<(ComInterfaceInfo, INamedTypeSymbol)> interfaceInfos = new();
HashSet<(ComInterfaceInfo, INamedTypeSymbol)> externalIfaces = new(ComInterfaceInfo.EqualityComparerForExternalIfaces.Instance);
List<DiagnosticInfo> diags = new();
foreach (var (syntax, symbol) in input.Left)
{
var cii = ComInterfaceInfo.From(symbol, syntax, stubEnvironment, CancellationToken.None);
if (cii.HasDiagnostic)
{
foreach (var diag in cii.Diagnostics)
diags.Add(diag);
}
if (cii.HasValue)
interfaceInfos.Add(cii.Value);
var externalBase = ComInterfaceInfo.CreateInterfaceInfoForBaseInterfacesInOtherCompilations(symbol);
Expand All @@ -98,13 +91,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var inner = new List<ComMethodInfo>();
foreach (var m in cmi)
{
if (m.HasDiagnostic)
{
foreach (var diag in m.Diagnostics)
{
diags.Add(diag);
}
}
if (m.HasValue)
{
inner.Add(m.Value.ComMethod);
Expand All @@ -118,14 +104,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
for (int i = 0; i < interfaceInfos.Count; i++)
{
var cic = comInterfaceContexts[i];
var cii = interfaceInfos[i];
if (cic.HasDiagnostic)
{
foreach (var diag in cic.Diagnostics)
{
diags.Add(diag);
}
}
if (cic.HasValue)
{
ifaceCtxs.Add((cic.Value, methods[i].ToSequenceEqualImmutableArray()));
Expand All @@ -151,14 +129,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

return
(
Diagnostics: diags.ToSequenceEqualImmutableArray(),
InterfaceContexts: ifaceCtxs.Select(x => x.Item1).Where(x => !x.IsExternallyDefined).ToSequenceEqualImmutableArray(),
MethodContexts: methodContexts.ToSequenceEqualImmutableArray()
);
});

context.RegisterDiagnostics(attributedInterfaces.SelectMany(static (data, ct) => data.Diagnostics));

// Create list of methods (inherited and declared) and their owning interface
var interfaceContextsToGenerate = attributedInterfaces.SelectMany(static (a, ct) => a.InterfaceContexts);
var comMethodContexts = attributedInterfaces.Select(static (a, ct) => a.MethodContexts);
Expand All @@ -185,11 +160,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
GenerateIUnknownDerivedAttributeApplication(x.Interface.Info, ct).NormalizeWhitespace()
]));

// Report diagnostics for managed-to-unmanaged and unmanaged-to-managed stubs, deduplicating diagnostics that are reported for both.
context.RegisterDiagnostics(
interfaceAndMethodsContexts
.SelectMany(static (data, ct) => data.DeclaredMethods.SelectMany(m => m.ManagedToUnmanagedStub.Diagnostics).Union(data.DeclaredMethods.SelectMany(m => m.UnmanagedToManagedStub.Diagnostics))));

var filesToGenerate = syntaxes
.Select(static (methodSyntaxes, ct) =>
{
Expand Down Expand Up @@ -443,7 +413,7 @@ private static IncrementalMethodStubGenerationContext CalculateSharedStubInforma
ComInterfaceDispatchMarshallingInfo.Instance);
}

private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax? syntax, IMethodSymbol symbol, int index, StubEnvironment environment, ComInterfaceInfo owningInterface, CancellationToken ct)
internal static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax? syntax, IMethodSymbol symbol, int index, StubEnvironment environment, ComInterfaceInfo owningInterface, CancellationToken ct)
{
ISignatureDiagnosticLocations locations = syntax is null
? NoneSignatureDiagnosticLocations.Instance
Expand Down
Loading
Loading