blob: 8e7dfe82c0b483a8ff1df1b5f71ca49bcd2190e8 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using ILLink.Shared;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace ILLink.RoslynAnalyzer
{
[DiagnosticAnalyzer (LanguageNames.CSharp)]
public sealed class COMAnalyzer : DiagnosticAnalyzer
{
private const string StructLayoutAttribute = nameof (StructLayoutAttribute);
private const string DllImportAttribute = nameof (DllImportAttribute);
private const string MarshalAsAttribute = nameof (MarshalAsAttribute);
static readonly DiagnosticDescriptor s_correctnessOfCOMCannotBeGuaranteed = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed,
helpLinkUri: "https://docs.microsoft.com/en-us/dotnet/core/deploying/trim-warnings/il2050");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create (s_correctnessOfCOMCannotBeGuaranteed);
public override void Initialize (AnalysisContext context)
{
context.EnableConcurrentExecution ();
context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterCompilationStartAction (context => {
var compilation = context.Compilation;
if (!context.Options.IsMSBuildPropertyValueTrue (MSBuildPropertyOptionNames.EnableTrimAnalyzer, compilation))
return;
context.RegisterOperationAction (operationContext => {
var invocationOperation = (IInvocationOperation) operationContext.Operation;
var targetMethod = invocationOperation.TargetMethod;
if (!targetMethod.HasAttribute (DllImportAttribute))
return;
bool comDangerousMethod = IsComInterop (targetMethod.ReturnType);
foreach (var parameter in targetMethod.Parameters) {
comDangerousMethod |= IsComInterop (parameter);
}
if (comDangerousMethod) {
operationContext.ReportDiagnostic (Diagnostic.Create (s_correctnessOfCOMCannotBeGuaranteed,
operationContext.Operation.Syntax.GetLocation (), targetMethod.GetDisplayName ()));
}
}, OperationKind.Invocation);
});
static bool IsComInterop (ISymbol symbol)
{
if (symbol.TryGetAttribute (MarshalAsAttribute, out var marshalAsAttribute) &&
marshalAsAttribute.ConstructorArguments.Length >= 1 && marshalAsAttribute.ConstructorArguments[0] is TypedConstant typedConstant &&
typedConstant.Type != null && typedConstant.Type.IsUnmanagedType) {
var unmanagedType = typedConstant.Value;
switch (unmanagedType) {
case (int) UnmanagedType.IUnknown:
case (int) UnmanagedType.IDispatch:
case (int) UnmanagedType.Interface:
return true;
default:
if (Enum.IsDefined (typeof (UnmanagedType), unmanagedType))
return false;
break;
}
}
if (symbol.IsInterface ())
return true;
ITypeSymbol? typeSymbol = symbol is ITypeSymbol ? symbol as ITypeSymbol : null;
if (symbol is IParameterSymbol parameterSymbol)
typeSymbol = parameterSymbol.Type;
if (typeSymbol == null)
return false;
if (typeSymbol.ContainingNamespace.Name == "System" && typeSymbol.Name == "Array") {
// System.Array marshals as IUnknown by default
return true;
} else if (typeSymbol.ContainingNamespace.Name == "System" && typeSymbol.Name == "String" ||
typeSymbol.ContainingNamespace.Name == "System.Text" && typeSymbol.Name == "StringBuilder") {
// String and StringBuilder are special cased by interop
return false;
}
if (typeSymbol.IsValueType) {
// Value types don't marshal as COM
return false;
} else if (typeSymbol.IsInterface ()) {
// Interface types marshal as COM by default
return true;
} else if (typeSymbol.ContainingNamespace.Name == "System" &&
typeSymbol.Name == "MulticastDelegate") {
// Delegates are special cased by interop
return false;
} else if (typeSymbol.IsSubclassOf ("System.Runtime.InteropServices", "CriticalHandle") ||
typeSymbol.IsSubclassOf ("System.Runtime.InteropServices", "SafeHandle")) {
// Subclasses of CriticalHandle and SafeHandle are special cased by interop
return false;
} else if (typeSymbol.TryGetAttribute (StructLayoutAttribute, out var structLayoutAttribute) &&
(LayoutKind) structLayoutAttribute.ConstructorArguments[0].Value! == LayoutKind.Auto) {
// Rest of classes that don't have layout marshal as COM
return true;
}
return false;
}
}
}
}
|