Welcome to mirror list, hosted at ThFree Co, Russian Federation.

COMAnalyzer.cs « ILLink.RoslynAnalyzer « src - github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 05e0aa097c3b868826fe8a20454d49e0c381ae03 (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
118
119
120
121
122
// 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);
		private const string RequiresUnreferencedCodeAttribute = nameof (RequiresUnreferencedCodeAttribute);

		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;

					if (operationContext.ContainingSymbol is ISymbol containingSymbol && containingSymbol.HasAttribute(RequiresUnreferencedCodeAttribute)) {
							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;
			}
		}
	}
}