-
Notifications
You must be signed in to change notification settings - Fork 57
Expand file tree
/
Copy pathHasFlagAnalyzer.cs
More file actions
103 lines (88 loc) · 3.51 KB
/
Copy pathHasFlagAnalyzer.cs
File metadata and controls
103 lines (88 loc) · 3.51 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
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace NetEscapades.EnumGenerators.Diagnostics;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class HasFlagAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "NEEG005";
public static readonly DiagnosticDescriptor Rule = new(
#pragma warning disable RS2008 // Enable Analyzer Release Tracking
id: DiagnosticId,
#pragma warning restore RS2008
title: "Use HasFlagFast() instead of HasFlag()",
messageFormat: "Use HasFlagFast() instead of HasFlag() for better performance on enum '{0}'",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(ctx =>
{
var (enumExtensionsAttr, externalEnumTypes) = AnalyzerHelpers.GetEnumExtensionAttributes(ctx);
if (enumExtensionsAttr is null || externalEnumTypes is null)
{
return;
}
ctx.RegisterSyntaxNodeAction(
c => AnalyzeInvocation(c, enumExtensionsAttr, externalEnumTypes),
SyntaxKind.InvocationExpression);
});
}
private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedTypeSymbol enumExtensionsAttr, HashSet<INamedTypeSymbol> externalEnumTypes)
{
var invocation = (InvocationExpressionSyntax)context.Node;
// Check if this is a member access expression (e.g., value.HasFlag())
if (invocation.Expression is not MemberAccessExpressionSyntax memberAccess)
{
return;
}
// Check if the method name is "HasFlag"
if (memberAccess.Name.Identifier.Text != "HasFlag")
{
return;
}
// Check if there is exactly one argument
if (invocation.ArgumentList.Arguments.Count != 1)
{
return;
}
// Get the symbol information for the invocation
var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation);
if (symbolInfo.Symbol is not IMethodSymbol methodSymbol)
{
return;
}
// Verify this is the HasFlag() method from System.Enum
if (methodSymbol.Name != "HasFlag" ||
methodSymbol.Parameters.Length != 1 ||
methodSymbol.ContainingType.SpecialType != SpecialType.System_Enum)
{
return;
}
// Get the type of the receiver (the thing before .HasFlag())
var receiverType = context.SemanticModel.GetTypeInfo(memberAccess.Expression).Type;
if (receiverType is null || receiverType.TypeKind != TypeKind.Enum)
{
return;
}
if (!AnalyzerHelpers.IsEnumWithExtensions(receiverType, enumExtensionsAttr, externalEnumTypes))
{
return;
}
// Report the diagnostic
var diagnostic = Diagnostic.Create(
Rule,
memberAccess.Name.GetLocation(),
receiverType.Name);
context.ReportDiagnostic(diagnostic);
}
}