From 9b268d00e36816fc26c1ce0ca144fd03048ca579 Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Carle <29762210+jscarle@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:58:01 -0500 Subject: [PATCH] Tighten diagnostic metadata --- .../AnalyzerReleases.Shipped.md | 11 ++++ .../AnalyzerReleases.Unshipped.md | 12 ++++ .../GeneratedIdentifierSourceGenerator.cs | 61 ++++++++++++++----- .../LightObjects.Generated.csproj | 5 ++ 4 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 src/LightObjects.Generated/AnalyzerReleases.Shipped.md create mode 100644 src/LightObjects.Generated/AnalyzerReleases.Unshipped.md diff --git a/src/LightObjects.Generated/AnalyzerReleases.Shipped.md b/src/LightObjects.Generated/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000..0ee0ad4 --- /dev/null +++ b/src/LightObjects.Generated/AnalyzerReleases.Shipped.md @@ -0,0 +1,11 @@ +## Release 10.0 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- + +### Removed Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- diff --git a/src/LightObjects.Generated/AnalyzerReleases.Unshipped.md b/src/LightObjects.Generated/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000..496221e --- /dev/null +++ b/src/LightObjects.Generated/AnalyzerReleases.Unshipped.md @@ -0,0 +1,12 @@ +; Please do not edit this file manually, it should only be updated through code fix application. + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +LO0001 | LightObjects.Generated | Error | Unsupported identifier type + +### Removed Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- diff --git a/src/LightObjects.Generated/GeneratedIdentifierSourceGenerator.cs b/src/LightObjects.Generated/GeneratedIdentifierSourceGenerator.cs index 580938c..ab9520b 100644 --- a/src/LightObjects.Generated/GeneratedIdentifierSourceGenerator.cs +++ b/src/LightObjects.Generated/GeneratedIdentifierSourceGenerator.cs @@ -10,11 +10,22 @@ namespace LightObjects.Generated; [Generator] public sealed class GeneratedIdentifierSourceGenerator : IIncrementalGenerator { + private sealed record IdentifierTransformResult(Identifier? Identifier, Diagnostic? Diagnostic); + private const string AttributesNamespace = "LightObjects.Generated"; private const string GeneratedIdentifierAttributeName = "GeneratedIdentifierAttribute"; private const string GeneratedIdentifierAttributeFullyQualifiedName = $"{AttributesNamespace}.{GeneratedIdentifierAttributeName}`1"; private const string GeneratedIdentifierAttributeHint = $"{GeneratedIdentifierAttributeFullyQualifiedName}.g.cs"; + private static readonly DiagnosticDescriptor UnsupportedIdentifierTypeDiagnostic = new( + id: "LO0001", + title: "Unsupported identifier type", + messageFormat: "'{0}' is not a supported identifier type. Only short, int, long, string, or System.Guid are allowed.", + category: "LightObjects.Generated", + DiagnosticSeverity.Error, + isEnabledByDefault: true + ); + private static readonly string FileHeader = $""" //----------------------------------------------------------------------------- // @@ -34,7 +45,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var generatedIdentifiers = context.SyntaxProvider .ForAttributeWithMetadataName(GeneratedIdentifierAttributeFullyQualifiedName, Filter, Transform) - .WhereNotNull() .Collect(); context.RegisterSourceOutput(generatedIdentifiers, GenerateIdentifier); @@ -69,12 +79,12 @@ private static bool Filter(SyntaxNode syntaxNode, CancellationToken cancellation return syntaxNode.IsKind(SyntaxKind.StructDeclaration) || syntaxNode.IsKind(SyntaxKind.ClassDeclaration); } - private static Identifier? Transform(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) + private static IdentifierTransformResult Transform(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (context.TargetSymbol is not INamedTypeSymbol namedTypeSymbol) - return null; + return new IdentifierTransformResult(null, null); var containingDeclarations = namedTypeSymbol.GetContainingDeclarations(cancellationToken); var symbolName = namedTypeSymbol.Name; @@ -83,7 +93,7 @@ private static bool Filter(SyntaxNode syntaxNode, CancellationToken cancellation var attribute = context.Attributes[0].AttributeClass!; if (attribute.TypeArguments.Length != 1) - return null; + return new IdentifierTransformResult(null, null); var typeArgument = attribute.TypeArguments[0]; @@ -113,7 +123,17 @@ private static bool Filter(SyntaxNode syntaxNode, CancellationToken cancellation break; default: if (typeArgument is not { Name: "Guid", ContainingNamespace: { Name: "System", ContainingNamespace.IsGlobalNamespace: true } }) - return null; + { + var attributeSyntax = context.Attributes[0].ApplicationSyntaxReference?.GetSyntax(cancellationToken); + var location = attributeSyntax?.GetLocation() ?? context.TargetNode.GetLocation(); + var diagnostic = Diagnostic.Create( + UnsupportedIdentifierTypeDiagnostic, + location, + typeArgument.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat) + ); + return new IdentifierTransformResult(null, diagnostic); + } + declaredValueType = "Guid"; fullValueType = "Guid"; break; @@ -129,21 +149,30 @@ private static bool Filter(SyntaxNode syntaxNode, CancellationToken cancellation supportsProviderBasedTryParse ); - return symbol; + return new IdentifierTransformResult(symbol, null); } - private static void GenerateIdentifier(SourceProductionContext context, ImmutableArray generatedIdentifiers) + private static void GenerateIdentifier(SourceProductionContext context, ImmutableArray generatedIdentifiers) { - foreach (var symbol in generatedIdentifiers) + foreach (var result in generatedIdentifiers) { - var symbolNamespace = symbol.ContainingDeclarations.ToNamespace(); - var symbolName = symbol.Name; - var isStruct = symbol.IsStruct; - var isPublic = symbol.IsPublic; - var declaredValueType = symbol.DeclaredValueType; - var fullValueType = symbol.FullValueType; + if (result.Diagnostic is { } diagnostic) + context.ReportDiagnostic(diagnostic); + + var symbol = result.Identifier; + if (!symbol.HasValue) + continue; + + var identifier = symbol.Value; + + var symbolNamespace = identifier.ContainingDeclarations.ToNamespace(); + var symbolName = identifier.Name; + var isStruct = identifier.IsStruct; + var isPublic = identifier.IsPublic; + var declaredValueType = identifier.DeclaredValueType; + var fullValueType = identifier.FullValueType; - var supportsProviderBasedTryParse = symbol.SupportsProviderBasedTryParse; + var supportsProviderBasedTryParse = identifier.SupportsProviderBasedTryParse; var source = new StringBuilder(); @@ -754,7 +783,7 @@ public override void Write(Utf8JsonWriter writer, {{symbolName}} identifier, Jso """ ); - var hint = $"{symbol.ContainingDeclarations.ToFullyQualifiedName()}.{symbol.Name}.g.cs"; + var hint = $"{identifier.ContainingDeclarations.ToFullyQualifiedName()}.{identifier.Name}.g.cs"; context.AddSource(hint, source.ToString()); } } diff --git a/src/LightObjects.Generated/LightObjects.Generated.csproj b/src/LightObjects.Generated/LightObjects.Generated.csproj index 74d12ef..549daee 100644 --- a/src/LightObjects.Generated/LightObjects.Generated.csproj +++ b/src/LightObjects.Generated/LightObjects.Generated.csproj @@ -73,6 +73,11 @@ + + + + + true