Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/LightObjects.Generated/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Release 10.0

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------

### Removed Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
12 changes: 12 additions & 0 deletions src/LightObjects.Generated/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
@@ -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
--------|----------|----------|-------
61 changes: 45 additions & 16 deletions src/LightObjects.Generated/GeneratedIdentifierSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = $"""
//-----------------------------------------------------------------------------
// <auto-generated>
Expand All @@ -34,7 +45,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

var generatedIdentifiers = context.SyntaxProvider
.ForAttributeWithMetadataName(GeneratedIdentifierAttributeFullyQualifiedName, Filter, Transform)
.WhereNotNull()
.Collect();

context.RegisterSourceOutput(generatedIdentifiers, GenerateIdentifier);
Expand Down Expand Up @@ -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;
Expand All @@ -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];

Expand Down Expand Up @@ -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;
Expand All @@ -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<Identifier> generatedIdentifiers)
private static void GenerateIdentifier(SourceProductionContext context, ImmutableArray<IdentifierTransformResult> 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();

Expand Down Expand Up @@ -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());
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/LightObjects.Generated/LightObjects.Generated.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
<None Include="$(PKGMicrosoft_Bcl_HashCode)\lib\netstandard2.0\Microsoft.Bcl.HashCode.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false"/>
</ItemGroup>

<ItemGroup>
<AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md" />
</ItemGroup>

<!-- Development -->
<PropertyGroup>
<IsRoslynComponent>true</IsRoslynComponent>
Expand Down