Skip to content

Commit d81f786

Browse files
committed
Minor refactoring of diagnostic descriptors
1 parent 96241d0 commit d81f786

23 files changed

Lines changed: 367 additions & 285 deletions
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
### New Rules
2+
3+
Rule ID | Category | Severity | Notes
4+
-----------|---------------------------------------|----------|------------------------
5+
TTRESG061 | ThinktectureRuntimeExtensionsAnalyzer | Error | DiagnosticsDescriptors
6+
TTRESG062 | ThinktectureRuntimeExtensionsAnalyzer | Error | DiagnosticsDescriptors
7+
TTRESG063 | ThinktectureRuntimeExtensionsAnalyzer | Error | DiagnosticsDescriptors
8+
TTRESG064 | ThinktectureRuntimeExtensionsAnalyzer | Error | DiagnosticsDescriptors
9+
TTRESG065 | ThinktectureRuntimeExtensionsAnalyzer | Error | DiagnosticsDescriptors

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionSourceGenState.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public AdHocUnionSourceGenState(
3939
Namespace = type.ContainingNamespace?.IsGlobalNamespace == true ? null : type.ContainingNamespace?.ToString();
4040
ContainingTypes = type.GetContainingTypes();
4141
TypeFullyQualified = type.ToFullyQualifiedDisplayString();
42-
TypeMinimallyQualified = type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
42+
TypeMinimallyQualified = type.ToMinimallyQualifiedDisplayString();
4343
IsReferenceType = type.IsReferenceType;
4444
IsValueType = type.IsValueType;
4545
IsRefStruct = type is { IsRefLikeType: true, IsReferenceType: false };

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionSourceGenerator.cs

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private void InitializeUnionSourceGen(
6161

6262
InitializeUnionTypeGeneration(context, validStates, options);
6363

64-
InitializeErrorReporting(context, unionTypeOrError);
64+
InitializeDiagnosticReporting(context, unionTypeOrError);
6565
InitializeExceptionReporting(context, unionTypeOrError);
6666
}
6767

@@ -116,7 +116,7 @@ private static bool IsCandidate(SyntaxNode syntaxNode, CancellationToken cancell
116116
cancellationToken);
117117
}
118118

119-
private SourceGenContext? GetSourceGenContextOrNull(
119+
private static SourceGenContext? GetSourceGenContextOrNull(
120120
GeneratorAttributeSyntaxContext context,
121121
Func<INamedTypeSymbol, ImmutableArray<TypedConstant>, ImmutableArray<ITypeSymbol>> getMemberTypes,
122122
CancellationToken cancellationToken)
@@ -128,39 +128,39 @@ private static bool IsCandidate(SyntaxNode syntaxNode, CancellationToken cancell
128128
var type = (INamedTypeSymbol)context.TargetSymbol;
129129

130130
if (type.TypeKind == TypeKind.Error)
131-
{
132131
return null;
133-
}
134132

135-
if (context.Attributes.IsDefaultOrEmpty || context.Attributes.Length != 1)
136-
{
137-
return new SourceGenContext(new SourceGenError("Ad-hoc union requires exactly one Union attribute", tds));
138-
}
133+
if (type.Arity > 0)
134+
return null; // Analyzer emits DiagnosticsDescriptors.SmartEnumsValueObjectsAndAdHocUnionsMustNotBeGeneric
135+
136+
if (context.Attributes.IsDefaultOrEmpty)
137+
return null;
138+
139+
if (context.Attributes.Length > 1)
140+
return new SourceGenDiagnostic(tds, DiagnosticsDescriptors.TypeMustNotHaveMoveThanOneDiscriminatedUnionAttribute, [type.ToMinimallyQualifiedDisplayString()]);
139141

140142
var attributeData = context.Attributes[0];
141143

142-
if (attributeData.AttributeClass is null
143-
|| attributeData.AttributeClass.TypeKind == TypeKind.Error)
144-
{
145-
return null;
146-
}
144+
if (attributeData.AttributeClass is null)
145+
return new SourceGenDiagnostic(tds, DiagnosticsDescriptors.ErrorDuringCodeAnalysis, [type.ToMinimallyQualifiedDisplayString(), "Could not resolve discriminated union attribute type"]);
146+
147+
if (attributeData.AttributeClass.TypeKind == TypeKind.Error)
148+
return new SourceGenDiagnostic(tds, DiagnosticsDescriptors.ErrorDuringCodeAnalysis, [type.ToMinimallyQualifiedDisplayString(), "Discriminated union attribute has TypeKind=Error"]);
147149

148150
var memberTypeSymbols = getMemberTypes(attributeData.AttributeClass, attributeData.ConstructorArguments);
149151

150152
if (memberTypeSymbols.Length < 2)
151-
{
152-
return new SourceGenContext(new SourceGenError("Ad-hoc union must define at least two member types", tds));
153-
}
153+
return new SourceGenDiagnostic(tds, DiagnosticsDescriptors.AdHocUnionMustHaveAtLeastTwoMemberTypes, [type.ToMinimallyQualifiedDisplayString()]);
154154

155-
var errorMessage = AttributeInfo.TryCreate(type, out var attributeInfo);
155+
var diagnostic = AttributeInfo.TryCreate(type, out var attributeInfo);
156156

157-
if (errorMessage is not null)
158-
return new SourceGenContext(new SourceGenError(errorMessage, tds));
157+
if (diagnostic is not null)
158+
return new SourceGenDiagnostic(tds, diagnostic.Value.Descriptor, diagnostic.Value.Args);
159159

160160
var factory = TypedMemberStateFactoryProvider.GetFactoryOrNull(context.SemanticModel.Compilation);
161161

162162
if (factory is null)
163-
return new SourceGenContext(new SourceGenError("Could not fetch type information for code generation of a discriminated union", tds));
163+
return new SourceGenDiagnostic(tds, DiagnosticsDescriptors.ErrorDuringCodeAnalysis, [type.ToMinimallyQualifiedDisplayString(), "Could not fetch type information for code generation of a discriminated union"]);
164164

165165
var settings = new AdHocUnionSettings(context.Attributes[0],
166166
memberTypeSymbols.Length);
@@ -171,7 +171,7 @@ private static bool IsCandidate(SyntaxNode syntaxNode, CancellationToken cancell
171171
var memberType = memberTypeSymbols[i];
172172

173173
if (memberType.TypeKind == TypeKind.Error)
174-
return new SourceGenContext(new SourceGenError("Type of the member must be a named type or array type", tds));
174+
return new SourceGenDiagnostic(tds, DiagnosticsDescriptors.ErrorDuringCodeAnalysis, [type.ToMinimallyQualifiedDisplayString(), $"The member type '{memberType.Name}' could not be resolved"]);
175175

176176
var memberTypeSettings = settings.MemberTypeSettings[i];
177177
memberType = memberType.IsReferenceType && memberTypeSettings.IsNullableReferenceType ? memberType.WithNullableAnnotation(NullableAnnotation.Annotated) : memberType;
@@ -199,15 +199,13 @@ private static bool IsCandidate(SyntaxNode syntaxNode, CancellationToken cancell
199199
}
200200

201201
if (!memberType.TryBuildMemberName(out var defaultName))
202-
{
203-
return new SourceGenContext(new SourceGenError("Type of the member must be a named type or array type", tds));
204-
}
202+
return new SourceGenDiagnostic(tds, DiagnosticsDescriptors.ErrorDuringGeneration, [type.ToMinimallyQualifiedDisplayString(), $"Could not build name for type '{memberType.Name}'. The type must be a named type, an array or a parameter but found '{memberType.GetType().FullName}'."]);
205203

206204
var name = memberTypeSettings.Name ??
207205
(typeDuplicateCounter == 0 ? defaultName : defaultName + typeDuplicateCounter);
208206

209207
if (String.IsNullOrWhiteSpace(name))
210-
return new SourceGenContext(new SourceGenError("Ad-hoc union member name cannot be null or whitespace", tds));
208+
return new SourceGenDiagnostic(tds, DiagnosticsDescriptors.ErrorDuringGeneration, [type.ToMinimallyQualifiedDisplayString(), $"The name for type '{memberType.Name}' must not be null nor empty."]);
211209

212210
memberTypeStates.Add(new AdHocUnionMemberTypeState(name,
213211
defaultName,
@@ -216,22 +214,18 @@ private static bool IsCandidate(SyntaxNode syntaxNode, CancellationToken cancell
216214
memberTypeSettings));
217215
}
218216

219-
var unionState = new AdHocUnionSourceGenState(type,
220-
memberTypeStates.DrainToImmutable(),
221-
settings,
222-
attributeInfo);
223-
224-
return new SourceGenContext(unionState);
217+
return new AdHocUnionSourceGenState(type,
218+
memberTypeStates.DrainToImmutable(),
219+
settings,
220+
attributeInfo);
225221
}
226222
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
227223
{
228224
throw;
229225
}
230226
catch (Exception ex)
231227
{
232-
Logger.LogError("Error during extraction of relevant information out of semantic model for generation of a discriminated union", tds, ex);
233-
234-
return new SourceGenContext(new SourceGenException(ex, tds));
228+
return new SourceGenException("Error during extraction of relevant information out of semantic model for generation of a discriminated union", ex, tds);
235229
}
236230
}
237231

@@ -251,16 +245,6 @@ private void InitializeUnionTypeGeneration(
251245
context.RegisterSourceOutput(unionTypes.Combine(options), (ctx, tuple) => GenerateCode(ctx, tuple.Left, tuple.Right, AdHocUnionCodeGeneratorFactory.Instance));
252246
}
253247

254-
private void InitializeErrorReporting(
255-
IncrementalGeneratorInitializationContext context,
256-
IncrementalValuesProvider<SourceGenContext> unionTypeOrException)
257-
{
258-
var exceptions = unionTypeOrException.SelectMany(static (state, _) => state.Error is not null
259-
? [state.Error.Value]
260-
: ImmutableArray<SourceGenError>.Empty);
261-
context.RegisterSourceOutput(exceptions, ReportError);
262-
}
263-
264248
private void InitializeExceptionReporting(
265249
IncrementalGeneratorInitializationContext context,
266250
IncrementalValuesProvider<SourceGenContext> unionTypeOrException)
@@ -271,21 +255,34 @@ private void InitializeExceptionReporting(
271255
context.RegisterSourceOutput(exceptions, ReportException);
272256
}
273257

274-
private readonly record struct SourceGenContext(AdHocUnionSourceGenState? ValidState, SourceGenException? Exception, SourceGenError? Error)
258+
private void InitializeDiagnosticReporting(
259+
IncrementalGeneratorInitializationContext context,
260+
IncrementalValuesProvider<SourceGenContext> unionTypeOrException)
261+
{
262+
var exceptions = unionTypeOrException.SelectMany(static (state, _) => state.Diagnostic is not null
263+
? [state.Diagnostic.Value]
264+
: ImmutableArray<SourceGenDiagnostic>.Empty);
265+
context.RegisterSourceOutput(exceptions, ReportDiagnostic);
266+
}
267+
268+
private readonly record struct SourceGenContext(
269+
AdHocUnionSourceGenState? ValidState,
270+
SourceGenException? Exception,
271+
SourceGenDiagnostic? Diagnostic)
275272
{
276-
public SourceGenContext(AdHocUnionSourceGenState validState)
277-
: this(validState, null, null)
273+
public static implicit operator SourceGenContext(AdHocUnionSourceGenState state)
278274
{
275+
return new SourceGenContext(state, null, null);
279276
}
280277

281-
public SourceGenContext(SourceGenException exception)
282-
: this(null, exception, null)
278+
public static implicit operator SourceGenContext(SourceGenException exception)
283279
{
280+
return new SourceGenContext(null, exception, null);
284281
}
285282

286-
public SourceGenContext(SourceGenError errorMessage)
287-
: this(null, null, errorMessage)
283+
public static implicit operator SourceGenContext(SourceGenDiagnostic diagnostic)
288284
{
285+
return new SourceGenContext(null, null, diagnostic);
289286
}
290287
}
291288
}

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AttributeInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ private AttributeInfo(
3131
KeyMemberEqualityComparerAccessor = keyMemberEqualityComparerAccessor;
3232
}
3333

34-
public static string? TryCreate(
34+
public static (DiagnosticDescriptor Descriptor, object[] Args)? TryCreate(
3535
INamedTypeSymbol type,
3636
out AttributeInfo info)
3737
{
3838
return TryCreate(type, out info, out _);
3939
}
4040

41-
public static string? TryCreate(
41+
public static (DiagnosticDescriptor Descriptor, object[] Args)? TryCreate(
4242
INamedTypeSymbol type,
4343
out AttributeInfo info,
4444
out AttributeData? thinktectureComponentAttribute)
@@ -113,7 +113,7 @@ private AttributeInfo(
113113
if (numberOfSourceGenAttributes > 1)
114114
{
115115
info = default;
116-
return "Multiple ValueObject/SmartEnum/Union-attributes found";
116+
return (DiagnosticsDescriptors.TypeMustNotHaveMoveThanOneAttribute, [type.ToMinimallyQualifiedDisplayString()]);
117117
}
118118
}
119119

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Diagnostics/ThinktectureRuntimeExtensionsAnalyzer.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ public sealed class ThinktectureRuntimeExtensionsAnalyzer : DiagnosticAnalyzer
5555
DiagnosticsDescriptors.MembersDisallowingDefaultValuesMustBeRequired,
5656
DiagnosticsDescriptors.ObjectFactoryMustHaveCorrespondingConstructor,
5757
DiagnosticsDescriptors.SmartEnumMustNotHaveObjectFactoryConstructor,
58+
DiagnosticsDescriptors.TypeMustNotHaveMoveThanOneAttribute,
59+
DiagnosticsDescriptors.TypeMustNotHaveMoveThanOneSmartEnumAttribute,
60+
DiagnosticsDescriptors.TypeMustNotHaveMoveThanOneValueObjectAttribute,
61+
DiagnosticsDescriptors.TypeMustNotHaveMoveThanOneDiscriminatedUnionAttribute,
62+
DiagnosticsDescriptors.AdHocUnionMustHaveAtLeastTwoMemberTypes,
5863
];
5964

6065
/// <inheritdoc />
@@ -868,9 +873,7 @@ private static void ValidateSmartEnum(
868873
}
869874

870875
ValidateEnumDerivedTypes(context, enumType);
871-
872876
EnumKeyMemberNameMustNotBeItem(context, attribute, tdsLocation);
873-
874877
ValidateKeyedSmartEnum(context, enumType, attribute, tdsLocation, factory);
875878
}
876879

@@ -1175,6 +1178,6 @@ private static void ReportDiagnostic(OperationAnalysisContext context, Diagnosti
11751178

11761179
private static string BuildTypeName(ITypeSymbol type)
11771180
{
1178-
return type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
1181+
return type.ToMinimallyQualifiedDisplayString();
11791182
}
11801183
}

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiagnosticsDescriptors.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ internal static class DiagnosticsDescriptors
3939
public static readonly DiagnosticDescriptor AllowDefaultStructsCannotBeTrueIfSomeMembersDisallowDefaultValues = new("TTRESG058", "'AllowDefaultStructs' must be 'false' if some members disallow default values", "'AllowDefaultStructs' of type '{0}' must be 'false' because following members disallow default values: {1}", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
4040
public static readonly DiagnosticDescriptor ObjectFactoryMustHaveCorrespondingConstructor = new("TTRESG059", "Type with ObjectFactoryAttribute<T> and 'HasCorrespondingConstructor = true' must have a constructor with type 'T'", "The type '{0}' with 'ObjectFactoryAttribute<{1}>' and 'HasCorrespondingConstructor = true' must have a constructor with a single argument of type '{1}'", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
4141
public static readonly DiagnosticDescriptor SmartEnumMustNotHaveObjectFactoryConstructor = new("TTRESG060", "Smart Enums with ObjectFactoryAttribute<T> must not have 'HasCorrespondingConstructor' set to 'true'", "Smart Enum '{0}' with 'ObjectFactoryAttribute<{1}>' must not have the property 'HasCorrespondingConstructor' set to 'true'", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
42+
public static readonly DiagnosticDescriptor TypeMustNotHaveMoveThanOneAttribute = new("TTRESG061", "Type must not have more than one ValueObject/SmartEnum/Union-attribute", "The type '{0}' must not have more than one ValueObject/SmartEnum/Union-attribute", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
43+
public static readonly DiagnosticDescriptor TypeMustNotHaveMoveThanOneSmartEnumAttribute = new("TTRESG062", $"Type must not have more than one {Constants.Attributes.SmartEnum.NAME}", $"The type '{{0}}' must not have more than one {Constants.Attributes.SmartEnum.NAME}", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
44+
public static readonly DiagnosticDescriptor TypeMustNotHaveMoveThanOneValueObjectAttribute = new("TTRESG063", $"Type must not have more than one {Constants.Attributes.ValueObject.KEYED_NAME}/{Constants.Attributes.ValueObject.COMPLEX_NAME}", $"The type '{{0}}' must not have more than one {Constants.Attributes.ValueObject.KEYED_NAME}/{Constants.Attributes.ValueObject.COMPLEX_NAME}", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
45+
public static readonly DiagnosticDescriptor TypeMustNotHaveMoveThanOneDiscriminatedUnionAttribute = new("TTRESG064", $"Type must not have more than one {Constants.Attributes.Union.NAME}/{Constants.Attributes.Union.NAME_AD_HOC}", $"The type '{{0}}' must not have more than one {Constants.Attributes.Union.NAME}/{Constants.Attributes.Union.NAME_AD_HOC}", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
46+
public static readonly DiagnosticDescriptor AdHocUnionMustHaveAtLeastTwoMemberTypes = new("TTRESG065", "Ad hoc union must define at least two member types", "Ad hoc union '{0}' must have at least two member types", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
4247

4348
public static readonly DiagnosticDescriptor ErrorDuringModulesAnalysis = new("TTRESG097", "Error during analysis of referenced modules", "Error during analysis of referenced modules: '{0}'", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true);
4449
public static readonly DiagnosticDescriptor ErrorDuringCodeAnalysis = new("TTRESG098", "Error during code analysis", "Error during code analysis of '{0}': '{1}'", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Warning, true);

0 commit comments

Comments
 (0)