diff --git a/Directory.Packages.props b/Directory.Packages.props
index 53919131..4d7fe5bf 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -39,8 +39,8 @@
-
-
+
+
diff --git a/Source/Analyzers/CodeFixes/AttributeIdentityCodeFixProvider.cs b/Source/Analyzers/CodeFixes/AttributeIdentityCodeFixProvider.cs
index 924e3b60..3b60316d 100644
--- a/Source/Analyzers/CodeFixes/AttributeIdentityCodeFixProvider.cs
+++ b/Source/Analyzers/CodeFixes/AttributeIdentityCodeFixProvider.cs
@@ -1,8 +1,10 @@
// Copyright (c) Dolittle. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using System.Collections.Immutable;
using System.Composition;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
@@ -21,14 +23,25 @@ namespace Dolittle.SDK.Analyzers.CodeFixes;
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AttributeIdentityCodeFixProvider)), Shared]
public class AttributeIdentityCodeFixProvider : CodeFixProvider
{
+ const string NoArgumentCorrespondsToRequiredParameter = "CS7036";
+
///
- public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIds.AttributeInvalidIdentityRuleId, DiagnosticIds.RedactionEventIncorrectPrefix);
+ public override ImmutableArray FixableDiagnosticIds { get; } = [
+ NoArgumentCorrespondsToRequiredParameter,
+ DiagnosticIds.AttributeInvalidIdentityRuleId,
+ DiagnosticIds.RedactionEventIncorrectPrefix];
///
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var document = context.Document;
var diagnostic = context.Diagnostics[0];
+ if(diagnostic.Id.Equals(NoArgumentCorrespondsToRequiredParameter, StringComparison.Ordinal))
+ {
+ RegisterCs7036IfApplicable(context, diagnostic, document);
+ return Task.CompletedTask;
+ }
+
if (!diagnostic.Properties.TryGetValue("identityParameter", out var identityParameterName))
{
return Task.CompletedTask;
@@ -54,6 +67,43 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
return Task.CompletedTask;
}
+ void RegisterCs7036IfApplicable(CodeFixContext context, Diagnostic diagnostic, Document document)
+ {
+ try
+ {
+ var (identityParameterName, type) = Extract7036Arguments(diagnostic);
+
+ switch (type, identityParameterName)
+ {
+ case ("EventTypeAttribute", "eventTypeId"):
+ case ("EventHandlerAttribute", "eventHandlerId"):
+ case ("ProjectionAttribute", "projectionId"):
+ case ("AggregateRootAttribute", "id"):
+ break;
+ default:
+ return;
+ }
+
+ context.RegisterCodeFix(
+ CodeAction.Create(
+ "Add identity",
+ ct => AddIdentity(context, document, identityParameterName, IdentityGenerator.Generate, ct),
+ nameof(AttributeIdentityCodeFixProvider) + ".AddIdentity"),
+ diagnostic);
+
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ (string parameterName, string typeName) Extract7036Arguments(Diagnostic diagnostic)
+ {
+ var message = diagnostic.GetMessage();
+ var parts = message.Split('\'');
+ return (parts[1], parts[3].Split('.').FirstOrDefault());
+ }
static async Task UpdateIdentity(CodeFixContext context, Document document, string identityParameterName,
CreateIdentity createIdentity,
@@ -65,6 +115,17 @@ static async Task UpdateIdentity(CodeFixContext context, Document docu
var updatedRoot = root.ReplaceNode(attribute, GenerateIdentityAttribute(attribute, identityParameterName, createIdentity));
return document.WithSyntaxRoot(updatedRoot);
}
+
+ static async Task AddIdentity(CodeFixContext context, Document document, string identityParameterName,
+ CreateIdentity createIdentity,
+ CancellationToken cancellationToken)
+ {
+ var root = await context.Document.GetSyntaxRootAsync(cancellationToken);
+ if (root is null) return document;
+ if (!TryGetTargetNode(context, root, out AttributeSyntax attribute)) return document; // Target not found
+ var updatedRoot = root.ReplaceNode(attribute, GenerateIdentityAttribute(attribute, identityParameterName, createIdentity));
+ return document.WithSyntaxRoot(updatedRoot);
+ }
static AttributeSyntax GenerateIdentityAttribute(AttributeSyntax existing, string identityParameterName, CreateIdentity generate)
{
diff --git a/Tests/Analyzers/Analyzers.csproj b/Tests/Analyzers/Analyzers.csproj
index c09759b5..9542daf0 100644
--- a/Tests/Analyzers/Analyzers.csproj
+++ b/Tests/Analyzers/Analyzers.csproj
@@ -8,6 +8,7 @@
+
diff --git a/Tests/Analyzers/CodeFixes/AnnotationIdentityCodeFixTests.cs b/Tests/Analyzers/CodeFixes/AnnotationIdentityCodeFixTests.cs
index ffaaf5e1..98fdca95 100644
--- a/Tests/Analyzers/CodeFixes/AnnotationIdentityCodeFixTests.cs
+++ b/Tests/Analyzers/CodeFixes/AnnotationIdentityCodeFixTests.cs
@@ -34,6 +34,112 @@ class SomeEvent
await VerifyCodeFixAsync(test, expected, diagnosticResult);
}
+ [Fact]
+ public async Task FixEventTypeAttributeWithNoIdentity()
+ {
+ var test = @"
+using Dolittle.SDK.Events;
+
+[EventType]
+class SomeEvent
+{
+ public string Name {get; set;}
+}";
+
+ var expected = @"
+using Dolittle.SDK.Events;
+
+[EventType(""61359cf4-3ae7-4a26-8a81-6816d3877f81"")]
+class SomeEvent
+{
+ public string Name {get; set;}
+}";
+ IdentityGenerator.Override = "61359cf4-3ae7-4a26-8a81-6816d3877f81";
+ var diagnosticResult = DiagnosticResult.CompilerError("CS7036")
+ .WithSpan(4, 2, 4, 11)
+ .WithArguments("eventTypeId", "Dolittle.SDK.Events.EventTypeAttribute.EventTypeAttribute(string, uint, string?)");
+ await VerifyCodeFixAsync(test, expected, diagnosticResult);
+ }
+
+ [Fact]
+ public async Task FixEventHandlerAttributeWithNoIdentity()
+ {
+ var test = @"
+using Dolittle.SDK.Events.Handling;
+
+[EventHandler]
+class SomeHandler
+{
+ public string Name {get; set;}
+}";
+
+ var expected = @"
+using Dolittle.SDK.Events.Handling;
+
+[EventHandler(""61359cf4-3ae7-4a26-8a81-6816d3877f81"")]
+class SomeHandler
+{
+ public string Name {get; set;}
+}";
+ IdentityGenerator.Override = "61359cf4-3ae7-4a26-8a81-6816d3877f81";
+ var diagnosticResult = DiagnosticResult.CompilerError("CS7036")
+ .WithSpan(4, 2, 4, 14)
+ .WithArguments("eventHandlerId", "Dolittle.SDK.Events.Handling.EventHandlerAttribute.EventHandlerAttribute(string, bool, string?, string?, int, Dolittle.SDK.Events.Handling.ProcessFrom, string?, string?)");
+ await VerifyCodeFixAsync(test, expected, diagnosticResult);
+ }
+
+ [Fact]
+ public async Task FixProjectionAttributeWithNoIdentity()
+ {
+ var test = @"
+using Dolittle.SDK.Projections;
+
+[Projection]
+class SomeProjection: ReadModel
+{
+ public string Name {get; set;}
+}";
+
+ var expected = @"
+using Dolittle.SDK.Projections;
+
+[Projection(""61359cf4-3ae7-4a26-8a81-6816d3877f81"")]
+class SomeProjection: ReadModel
+{
+ public string Name {get; set;}
+}";
+ IdentityGenerator.Override = "61359cf4-3ae7-4a26-8a81-6816d3877f81";
+ var diagnosticResult = DiagnosticResult.CompilerError("CS7036")
+ .WithSpan(4, 2, 4, 12)
+ .WithArguments("projectionId", "Dolittle.SDK.Projections.ProjectionAttribute.ProjectionAttribute(string, string?, string?, string?, bool)");
+ await VerifyCodeFixAsync(test, expected, diagnosticResult);
+ }
+
+ [Fact]
+ public async Task FixAggregateRootAttributeWithNoIdentity()
+ {
+ var test = @"
+using Dolittle.SDK.Aggregates;
+
+[AggregateRoot]
+class SomeAggregate: AggregateRoot
+{
+}";
+
+ var expected = @"
+using Dolittle.SDK.Aggregates;
+
+[AggregateRoot(""61359cf4-3ae7-4a26-8a81-6816d3877f81"")]
+class SomeAggregate: AggregateRoot
+{
+}";
+ IdentityGenerator.Override = "61359cf4-3ae7-4a26-8a81-6816d3877f81";
+
+ var diagnosticResult = DiagnosticResult.CompilerError("CS7036")
+ .WithSpan(4, 2, 4, 15)
+ .WithArguments("id", "Dolittle.SDK.Aggregates.AggregateRootAttribute.AggregateRootAttribute(string, string?)");
+ await VerifyCodeFixAsync(test, expected, diagnosticResult);
+ }
[Fact]
public async Task FixesAttributeWithInvalidIdentityWithNamedArguments()
{