Skip to content

Commit bc69ef7

Browse files
committed
Extract some common code
1 parent 8d9fc92 commit bc69ef7

6 files changed

Lines changed: 80 additions & 109 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.Diagnostics;
3+
4+
namespace NetEscapades.EnumGenerators.Diagnostics;
5+
6+
public class AnalyzerHelpers
7+
{
8+
public static (INamedTypeSymbol? enumExtensionsAttr, HashSet<INamedTypeSymbol>? externalEnumTypes) GetEnumExtensionAttributes(
9+
CompilationStartAnalysisContext ctx)
10+
{
11+
var enumExtensionsAttr =
12+
ctx.Compilation.GetTypeByMetadataName(Attributes.EnumExtensionsAttribute);
13+
var externalEnumExtensionsAttr =
14+
ctx.Compilation.GetTypeByMetadataName(Attributes.ExternalEnumExtensionsAttribute);
15+
16+
if (enumExtensionsAttr is null)
17+
{
18+
return (enumExtensionsAttr, null);
19+
}
20+
21+
// Collect all enum types that have EnumExtensions<T> attributes
22+
var externalEnumTypes = new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default);
23+
if (externalEnumExtensionsAttr is not null)
24+
{
25+
foreach (var attribute in ctx.Compilation.Assembly.GetAttributes())
26+
{
27+
if (attribute.AttributeClass is { IsGenericType: true } attrClass &&
28+
SymbolEqualityComparer.Default.Equals(attrClass.ConstructedFrom, externalEnumExtensionsAttr) &&
29+
attrClass.TypeArguments is [INamedTypeSymbol { TypeKind: TypeKind.Enum } enumType])
30+
{
31+
externalEnumTypes.Add(enumType);
32+
}
33+
}
34+
}
35+
36+
return (enumExtensionsAttr, externalEnumTypes);
37+
}
38+
39+
public static bool IsEnumWithExtensions(
40+
ITypeSymbol receiverType,
41+
INamedTypeSymbol enumExtensionsAttr,
42+
HashSet<INamedTypeSymbol> externalEnumTypes)
43+
{
44+
// Check if the enum has the [EnumExtensions] attribute or is referenced in EnumExtensions<T>
45+
// First check if the enum itself has the attribute
46+
foreach (var attributeData in receiverType.GetAttributes())
47+
{
48+
if (SymbolEqualityComparer.Default.Equals(
49+
attributeData.AttributeClass,
50+
enumExtensionsAttr))
51+
{
52+
return true;
53+
}
54+
}
55+
56+
// If not, check if it's in the external enum types (EnumExtensions<T>)
57+
return receiverType is INamedTypeSymbol namedType
58+
&& externalEnumTypes.Contains(namedType);
59+
}
60+
}

src/NetEscapades.EnumGenerators/Diagnostics/HasFlagAnalyzer.cs

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,12 @@ public override void Initialize(AnalysisContext context)
3131
context.EnableConcurrentExecution();
3232
context.RegisterCompilationStartAction(ctx =>
3333
{
34-
var enumExtensionsAttr =
35-
ctx.Compilation.GetTypeByMetadataName(Attributes.EnumExtensionsAttribute);
36-
var externalEnumExtensionsAttr =
37-
ctx.Compilation.GetTypeByMetadataName(Attributes.ExternalEnumExtensionsAttribute);
38-
39-
if (enumExtensionsAttr is null)
34+
var (enumExtensionsAttr, externalEnumTypes) = AnalyzerHelpers.GetEnumExtensionAttributes(ctx);
35+
if (enumExtensionsAttr is null || externalEnumTypes is null)
4036
{
4137
return;
4238
}
4339

44-
// Collect all enum types that have EnumExtensions<T> attributes
45-
var externalEnumTypes = new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default);
46-
if (externalEnumExtensionsAttr is not null)
47-
{
48-
foreach (var attribute in ctx.Compilation.Assembly.GetAttributes())
49-
{
50-
if (attribute.AttributeClass is { IsGenericType: true } attrClass &&
51-
SymbolEqualityComparer.Default.Equals(attrClass.ConstructedFrom, externalEnumExtensionsAttr) &&
52-
attrClass.TypeArguments is [INamedTypeSymbol { TypeKind: TypeKind.Enum } enumType])
53-
{
54-
externalEnumTypes.Add(enumType);
55-
}
56-
}
57-
}
58-
5940
ctx.RegisterSyntaxNodeAction(
6041
c => AnalyzeInvocation(c, enumExtensionsAttr, externalEnumTypes),
6142
SyntaxKind.InvocationExpression);
@@ -106,7 +87,7 @@ private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedT
10687
return;
10788
}
10889

109-
if (!IsEnumWithExtensions(receiverType, enumExtensionsAttr, externalEnumTypes))
90+
if (!AnalyzerHelpers.IsEnumWithExtensions(receiverType, enumExtensionsAttr, externalEnumTypes))
11091
{
11192
return;
11293
}
@@ -119,26 +100,4 @@ private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedT
119100

120101
context.ReportDiagnostic(diagnostic);
121102
}
122-
123-
private static bool IsEnumWithExtensions(
124-
ITypeSymbol receiverType,
125-
INamedTypeSymbol enumExtensionsAttr,
126-
HashSet<INamedTypeSymbol> externalEnumTypes)
127-
{
128-
// Check if the enum has the [EnumExtensions] attribute or is referenced in EnumExtensions<T>
129-
// First check if the enum itself has the attribute
130-
foreach (var attributeData in receiverType.GetAttributes())
131-
{
132-
if (SymbolEqualityComparer.Default.Equals(
133-
attributeData.AttributeClass,
134-
enumExtensionsAttr))
135-
{
136-
return true;
137-
}
138-
}
139-
140-
// If not, check if it's in the external enum types (EnumExtensions<T>)
141-
return receiverType is INamedTypeSymbol namedType
142-
&& externalEnumTypes.Contains(namedType);
143-
}
144103
}

src/NetEscapades.EnumGenerators/Diagnostics/ToStringAnalyzer.cs

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,12 @@ public override void Initialize(AnalysisContext context)
3131
context.EnableConcurrentExecution();
3232
context.RegisterCompilationStartAction(ctx =>
3333
{
34-
var enumExtensionsAttr =
35-
ctx.Compilation.GetTypeByMetadataName(Attributes.EnumExtensionsAttribute);
36-
var externalEnumExtensionsAttr =
37-
ctx.Compilation.GetTypeByMetadataName(Attributes.ExternalEnumExtensionsAttribute);
38-
39-
if (enumExtensionsAttr is null)
34+
var (enumExtensionsAttr, externalEnumTypes) = AnalyzerHelpers.GetEnumExtensionAttributes(ctx);
35+
if (enumExtensionsAttr is null || externalEnumTypes is null)
4036
{
4137
return;
4238
}
4339

44-
// Collect all enum types that have EnumExtensions<T> attributes
45-
var externalEnumTypes = new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default);
46-
if (externalEnumExtensionsAttr is not null)
47-
{
48-
foreach (var attribute in ctx.Compilation.Assembly.GetAttributes())
49-
{
50-
if (attribute.AttributeClass is { IsGenericType: true } attrClass &&
51-
SymbolEqualityComparer.Default.Equals(attrClass.ConstructedFrom, externalEnumExtensionsAttr) &&
52-
attrClass.TypeArguments is [INamedTypeSymbol { TypeKind: TypeKind.Enum } enumType])
53-
{
54-
externalEnumTypes.Add(enumType);
55-
}
56-
}
57-
}
58-
5940
ctx.RegisterSyntaxNodeAction(
6041
c => AnalyzeInvocation(c, enumExtensionsAttr, externalEnumTypes),
6142
SyntaxKind.InvocationExpression);
@@ -136,7 +117,7 @@ private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedT
136117
return;
137118
}
138119

139-
if (!IsEnumWithExtensions(receiverType, enumExtensionsAttr, externalEnumTypes))
120+
if (!AnalyzerHelpers.IsEnumWithExtensions(receiverType, enumExtensionsAttr, externalEnumTypes))
140121
{
141122
return;
142123
}
@@ -190,7 +171,7 @@ private static void AnalyzeInterpolation(SyntaxNodeAnalysisContext context, INam
190171
}
191172
}
192173

193-
if (!IsEnumWithExtensions(expressionType, enumExtensionsAttr, externalEnumTypes))
174+
if (!AnalyzerHelpers.IsEnumWithExtensions(expressionType, enumExtensionsAttr, externalEnumTypes))
194175
{
195176
return;
196177
}
@@ -203,27 +184,4 @@ private static void AnalyzeInterpolation(SyntaxNodeAnalysisContext context, INam
203184

204185
context.ReportDiagnostic(diagnostic);
205186
}
206-
207-
private static bool IsEnumWithExtensions(
208-
ITypeSymbol receiverType,
209-
INamedTypeSymbol enumExtensionsAttr,
210-
HashSet<INamedTypeSymbol> externalEnumTypes)
211-
{
212-
// Check if the enum has the [EnumExtensions] attribute or is referenced in EnumExtensions<T>
213-
// First check if the enum itself has the attribute
214-
foreach (var attributeData in receiverType.GetAttributes())
215-
{
216-
if (SymbolEqualityComparer.Default.Equals(
217-
attributeData.AttributeClass,
218-
enumExtensionsAttr))
219-
{
220-
return true;
221-
}
222-
}
223-
224-
// If not, check if it's in the external enum types (EnumExtensions<T>)
225-
return receiverType is INamedTypeSymbol namedType
226-
&& externalEnumTypes.Contains(namedType);
227-
}
228-
229187
}

tests/NetEscapades.EnumGenerators.IntegrationTests/.editorconfig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
dotnet_diagnostic.NEEG001.severity = error
55
dotnet_diagnostic.NEEG002.severity = error
66
dotnet_diagnostic.NEEG003.severity = error
7-
dotnet_diagnostic.NEEG004.severity = error
7+
dotnet_diagnostic.NEEG004.severity = error
8+
dotnet_diagnostic.NEEG005.severity = error

tests/NetEscapades.EnumGenerators.IntegrationTests/AnalyzerTests.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using NetEscapades.EnumGenerators.IntegrationTests;
23
using Xunit;
34

45
namespace NetEscapades.EnumGenerators.Benchmarks;
@@ -24,4 +25,14 @@ public void Neeg004Testing()
2425
_ = $"Some value: {EnumInSystem.First:G} <-";
2526
#pragma warning restore NEEG004
2627
}
28+
29+
[Fact]
30+
public void Neeg005Testing()
31+
{
32+
#pragma warning disable NEEG005
33+
var test = FlagsEnum.First;
34+
_ = test.HasFlag(FlagsEnum.Second);
35+
_ = $"Some value: {test.HasFlag(FlagsEnum.Second)} <-";
36+
#pragma warning restore NEEG005
37+
}
2738
}

tests/NetEscapades.EnumGenerators.Tests/HasFlagAnalyzerTests.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,6 @@ public void TestMethod()
3636
await Verifier.VerifyAnalyzerAsync(test);
3737
}
3838

39-
[Fact]
40-
public async Task NonEnumTypeShouldNotHaveDiagnostics()
41-
{
42-
var test = GetTestCode(
43-
/* lang=c# */
44-
"""
45-
public class TestClass
46-
{
47-
public void TestMethod()
48-
{
49-
var value = 42;
50-
// This won't compile but analyzer shouldn't care
51-
}
52-
}
53-
""");
54-
await Verifier.VerifyAnalyzerAsync(test);
55-
}
56-
5739
[Fact]
5840
public async Task HasFlagOnEnumWithAttributeShouldHaveDiagnostic()
5941
{

0 commit comments

Comments
 (0)