-
-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathReflectionTypeComparisonAnalyzer.cs
More file actions
136 lines (115 loc) · 4.88 KB
/
Copy pathReflectionTypeComparisonAnalyzer.cs
File metadata and controls
136 lines (115 loc) · 4.88 KB
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// <copyright file="ReflectionTypeComparisonAnalyzer.cs" company="GSharp">
// Copyright (C) GSharp Authors. All rights reserved.
// </copyright>
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace GSharp.InternalAnalyzers;
/// <summary>
/// Flags reference equality comparisons between reflection <see cref="System.Type"/> values.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ReflectionTypeComparisonAnalyzer : DiagnosticAnalyzer
{
private const string ClrTypeUtilitiesName = "ClrTypeUtilities";
private const string TypeIdentityComparerName = "TypeIdentityComparer";
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
= ImmutableArray.Create(DiagnosticDescriptors.ReflectionTypeReferenceComparison);
/// <inheritdoc />
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterOperationAction(AnalyzeBinary, OperationKind.BinaryOperator);
context.RegisterOperationAction(AnalyzeInvocation, OperationKind.Invocation);
}
private static void AnalyzeBinary(OperationAnalysisContext context)
{
if (IsInsideExemptType(context.ContainingSymbol) || !IsCompilerMetadataArea(context.ContainingSymbol?.ContainingNamespace))
{
return;
}
var operation = (IBinaryOperation)context.Operation;
var left = UnwrapConversion(operation.LeftOperand);
var right = UnwrapConversion(operation.RightOperand);
if ((operation.OperatorKind == BinaryOperatorKind.Equals || operation.OperatorKind == BinaryOperatorKind.NotEquals)
&& IsTypeofComparedToReflectionType(left, right))
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ReflectionTypeReferenceComparison, operation.Syntax.GetLocation()));
}
}
private static void AnalyzeInvocation(OperationAnalysisContext context)
{
if (IsInsideExemptType(context.ContainingSymbol) || !IsCompilerMetadataArea(context.ContainingSymbol?.ContainingNamespace))
{
return;
}
var operation = (IInvocationOperation)context.Operation;
if (operation.TargetMethod.Name != nameof(object.ReferenceEquals)
|| operation.Arguments.Length != 2
|| operation.TargetMethod.ContainingType.SpecialType != SpecialType.System_Object)
{
return;
}
var left = UnwrapConversion(operation.Arguments[0].Value);
var right = UnwrapConversion(operation.Arguments[1].Value);
if (IsTypeofComparedToReflectionType(left, right))
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ReflectionTypeReferenceComparison, operation.Syntax.GetLocation()));
}
}
private static bool IsTypeofComparedToReflectionType(IOperation left, IOperation right)
{
if (IsNullLiteral(left) || IsNullLiteral(right))
{
return false;
}
return (IsTypeof(left) && IsReflectionType(right.Type))
|| (IsTypeof(right) && IsReflectionType(left.Type));
}
private static bool IsTypeof(IOperation operation)
{
return UnwrapConversion(operation).Kind == OperationKind.TypeOf;
}
private static bool IsNullLiteral(IOperation operation)
{
return operation.ConstantValue.HasValue && operation.ConstantValue.Value == null;
}
private static bool IsCompilerMetadataArea(INamespaceSymbol namespaceSymbol)
{
var namespaceName = namespaceSymbol?.ToDisplayString();
return namespaceName == "GSharp.Core.CodeAnalysis.Emit"
|| namespaceName == "GSharp.Core.CodeAnalysis.Symbols"
|| namespaceName == "GSharp.Core.CodeAnalysis.Binding";
}
private static IOperation UnwrapConversion(IOperation operation)
{
while (operation is IConversionOperation conversion)
{
operation = conversion.Operand;
}
return operation;
}
private static bool IsInsideExemptType(ISymbol symbol)
{
for (var type = symbol?.ContainingType; type != null; type = type.ContainingType)
{
if (type.Name == ClrTypeUtilitiesName || type.Name == TypeIdentityComparerName)
{
return true;
}
}
return false;
}
private static bool IsReflectionType(ITypeSymbol type)
{
if (type == null)
{
return false;
}
return type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::System.Type"
|| (type.Name == "TypeInfo" && type.ContainingNamespace?.ToDisplayString() == "System.Reflection");
}
}