From a382920c77b39a4f2445f543650a80e7f2da36ec Mon Sep 17 00:00:00 2001 From: Ledjon Behluli Date: Tue, 9 Jan 2024 21:36:15 +0100 Subject: [PATCH 1/2] wip --- Orleans.sln | 7 +++ .../IdClashAttributeAnalyzer.cs | 11 +++- .../IdClashAttributeCodeFix.cs | 56 +++++++++++++++++++ test/ClassLibrary1/Class1.cs | 17 ++++++ test/ClassLibrary1/ClassLibrary1.csproj | 14 +++++ 5 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 src/Orleans.Analyzers/IdClashAttributeCodeFix.cs create mode 100644 test/ClassLibrary1/Class1.cs create mode 100644 test/ClassLibrary1/ClassLibrary1.csproj diff --git a/Orleans.sln b/Orleans.sln index 2b92cc4246..b6946e95b6 100644 --- a/Orleans.sln +++ b/Orleans.sln @@ -219,6 +219,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tester.Redis", "test\Extens EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orleans.Serialization.Protobuf", "src\Serializers\Orleans.Serialization.Protobuf\Orleans.Serialization.Protobuf.csproj", "{A073C0EE-8732-42F9-A22E-D47034E25076}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "test\ClassLibrary1\ClassLibrary1.csproj", "{D3B9EC50-D970-473D-91A5-E4E69A629D29}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -581,6 +583,10 @@ Global {A073C0EE-8732-42F9-A22E-D47034E25076}.Debug|Any CPU.Build.0 = Debug|Any CPU {A073C0EE-8732-42F9-A22E-D47034E25076}.Release|Any CPU.ActiveCfg = Release|Any CPU {A073C0EE-8732-42F9-A22E-D47034E25076}.Release|Any CPU.Build.0 = Release|Any CPU + {D3B9EC50-D970-473D-91A5-E4E69A629D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3B9EC50-D970-473D-91A5-E4E69A629D29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3B9EC50-D970-473D-91A5-E4E69A629D29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3B9EC50-D970-473D-91A5-E4E69A629D29}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -688,6 +694,7 @@ Global {8FC6457C-6273-4338-AD2A-ECAA8FE2C5D7} = {082D25DB-70CA-48F4-93E0-EC3455F494B8} {F13247A0-70C9-4200-9CB1-2002CB8105E0} = {082D25DB-70CA-48F4-93E0-EC3455F494B8} {A073C0EE-8732-42F9-A22E-D47034E25076} = {4CD3AA9E-D937-48CA-BB6C-158E12257D23} + {D3B9EC50-D970-473D-91A5-E4E69A629D29} = {A6573187-FD0D-4DF7-91D1-03E07E470C0A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7BFB3429-B5BB-4DB1-95B4-67D77A864952} diff --git a/src/Orleans.Analyzers/IdClashAttributeAnalyzer.cs b/src/Orleans.Analyzers/IdClashAttributeAnalyzer.cs index 96a3df2bb5..d7e425f785 100644 --- a/src/Orleans.Analyzers/IdClashAttributeAnalyzer.cs +++ b/src/Orleans.Analyzers/IdClashAttributeAnalyzer.cs @@ -45,7 +45,7 @@ private void CheckSyntaxNode(SyntaxNodeAnalysisContext context) return; } - List> bags = new(); + List> bags = []; foreach (var memberDeclaration in typeDeclaration.Members.OfType()) { var attributes = memberDeclaration.AttributeLists.GetAttributeSyntaxes(Constants.IdAttributeName); @@ -78,11 +78,16 @@ private void CheckSyntaxNode(SyntaxNodeAnalysisContext context) { foreach (var bag in filteredBags) { + var builder = ImmutableDictionary.CreateBuilder(); + + builder.Add("IdValue", bag.Value.ToString()); + context.ReportDiagnostic(Diagnostic.Create( descriptor: Rule, - location: bag.Location)); + location: bag.Location, + properties: builder.ToImmutable())); } } } } -} \ No newline at end of file +} diff --git a/src/Orleans.Analyzers/IdClashAttributeCodeFix.cs b/src/Orleans.Analyzers/IdClashAttributeCodeFix.cs new file mode 100644 index 0000000000..021e27b9d8 --- /dev/null +++ b/src/Orleans.Analyzers/IdClashAttributeCodeFix.cs @@ -0,0 +1,56 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading.Tasks; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Orleans.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IdClashAttributeCodeFix)), Shared] +public class IdClashAttributeCodeFix : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(IdClashAttributeAnalyzer.RuleId); + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var diagnostic = context.Diagnostics.First(); + if (root.FindNode(diagnostic.Location.SourceSpan) is not AttributeSyntax attribute) + { + return; + } + + var idValue = diagnostic.Properties["IdValue"]; + + context.RegisterCodeFix( + CodeAction.Create( + Resources.IdClashDetectedTitle, + createChangedDocument: _ => + { + var newIdValue = root + .DescendantNodes() + .OfType() + .Where(a => a.IsAttribute(Constants.IdAttributeName)) + .Select(a => int.Parse(a.ArgumentList.Arguments.Single().ToString())) + .ToList() + .Max() + 1; + + var newAttribute = attribute.ReplaceNode( + attribute.ArgumentList.Arguments[0].Expression, + LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(newIdValue))); + + var newRoot = root.ReplaceNode(attribute, newAttribute); + var newDocument = context.Document.WithSyntaxRoot(newRoot); + + return Task.FromResult(newDocument); + }, + equivalenceKey: IdClashAttributeAnalyzer.RuleId), + diagnostic); + } +} \ No newline at end of file diff --git a/test/ClassLibrary1/Class1.cs b/test/ClassLibrary1/Class1.cs new file mode 100644 index 0000000000..7316f2ddee --- /dev/null +++ b/test/ClassLibrary1/Class1.cs @@ -0,0 +1,17 @@ +using Orleans; + +namespace ClassLibrary1; + +[GenerateSerializer] +[Alias("Class")] +public class Class +{ + [Id(0)] public string A { get; set; } + + [Id(1)] public string B { get; set; } + [Id(1)] public string C { get; set; } + + [Id(2)] public string B1 { get; set; } + [Id(2)] public string C1 { get; set; } + [Id(2)] public string D1 { get; set; } +} \ No newline at end of file diff --git a/test/ClassLibrary1/ClassLibrary1.csproj b/test/ClassLibrary1/ClassLibrary1.csproj new file mode 100644 index 0000000000..475c729948 --- /dev/null +++ b/test/ClassLibrary1/ClassLibrary1.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + Latest + + + + + + + + From f1330d45cd15eec502d0971b422850179bf9406b Mon Sep 17 00:00:00 2001 From: Ledjon Behluli Date: Tue, 9 Jan 2024 22:07:16 +0100 Subject: [PATCH 2/2] done --- Orleans.sln | 7 ------- src/Orleans.Analyzers/Resources.Designer.cs | 4 ++-- src/Orleans.Analyzers/Resources.resx | 4 ++-- test/ClassLibrary1/Class1.cs | 17 ----------------- test/ClassLibrary1/ClassLibrary1.csproj | 14 -------------- 5 files changed, 4 insertions(+), 42 deletions(-) delete mode 100644 test/ClassLibrary1/Class1.cs delete mode 100644 test/ClassLibrary1/ClassLibrary1.csproj diff --git a/Orleans.sln b/Orleans.sln index b6946e95b6..2b92cc4246 100644 --- a/Orleans.sln +++ b/Orleans.sln @@ -219,8 +219,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tester.Redis", "test\Extens EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orleans.Serialization.Protobuf", "src\Serializers\Orleans.Serialization.Protobuf\Orleans.Serialization.Protobuf.csproj", "{A073C0EE-8732-42F9-A22E-D47034E25076}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "test\ClassLibrary1\ClassLibrary1.csproj", "{D3B9EC50-D970-473D-91A5-E4E69A629D29}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -583,10 +581,6 @@ Global {A073C0EE-8732-42F9-A22E-D47034E25076}.Debug|Any CPU.Build.0 = Debug|Any CPU {A073C0EE-8732-42F9-A22E-D47034E25076}.Release|Any CPU.ActiveCfg = Release|Any CPU {A073C0EE-8732-42F9-A22E-D47034E25076}.Release|Any CPU.Build.0 = Release|Any CPU - {D3B9EC50-D970-473D-91A5-E4E69A629D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D3B9EC50-D970-473D-91A5-E4E69A629D29}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D3B9EC50-D970-473D-91A5-E4E69A629D29}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D3B9EC50-D970-473D-91A5-E4E69A629D29}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -694,7 +688,6 @@ Global {8FC6457C-6273-4338-AD2A-ECAA8FE2C5D7} = {082D25DB-70CA-48F4-93E0-EC3455F494B8} {F13247A0-70C9-4200-9CB1-2002CB8105E0} = {082D25DB-70CA-48F4-93E0-EC3455F494B8} {A073C0EE-8732-42F9-A22E-D47034E25076} = {4CD3AA9E-D937-48CA-BB6C-158E12257D23} - {D3B9EC50-D970-473D-91A5-E4E69A629D29} = {A6573187-FD0D-4DF7-91D1-03E07E470C0A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7BFB3429-B5BB-4DB1-95B4-67D77A864952} diff --git a/src/Orleans.Analyzers/Resources.Designer.cs b/src/Orleans.Analyzers/Resources.Designer.cs index 4393a35353..08fdb8cb68 100644 --- a/src/Orleans.Analyzers/Resources.Designer.cs +++ b/src/Orleans.Analyzers/Resources.Designer.cs @@ -214,7 +214,7 @@ internal static string IdClashDetectedDescription { } /// - /// Looks up a localized string similar to Substitute duplicated [Id] value with the correct unique identity of this member. + /// Looks up a localized string similar to Change duplicated [Id]. /// internal static string IdClashDetectedMessageFormat { get { @@ -223,7 +223,7 @@ internal static string IdClashDetectedMessageFormat { } /// - /// Looks up a localized string similar to Substitute duplicated [Id] value with the correct unique identity of this member. + /// Looks up a localized string similar to Change duplicated [Id]. /// internal static string IdClashDetectedTitle { get { diff --git a/src/Orleans.Analyzers/Resources.resx b/src/Orleans.Analyzers/Resources.resx index 90d80ddfcc..09e12ca19e 100644 --- a/src/Orleans.Analyzers/Resources.resx +++ b/src/Orleans.Analyzers/Resources.resx @@ -169,10 +169,10 @@ The [Id] attribute must be unique to each members of the declaring type. - Substitute duplicated [Id] value with the correct unique identity of this member + Change duplicated [Id] - Substitute duplicated [Id] value with the correct unique identity of this member + Change duplicated [Id] Remove attribute [{0}] diff --git a/test/ClassLibrary1/Class1.cs b/test/ClassLibrary1/Class1.cs deleted file mode 100644 index 7316f2ddee..0000000000 --- a/test/ClassLibrary1/Class1.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Orleans; - -namespace ClassLibrary1; - -[GenerateSerializer] -[Alias("Class")] -public class Class -{ - [Id(0)] public string A { get; set; } - - [Id(1)] public string B { get; set; } - [Id(1)] public string C { get; set; } - - [Id(2)] public string B1 { get; set; } - [Id(2)] public string C1 { get; set; } - [Id(2)] public string D1 { get; set; } -} \ No newline at end of file diff --git a/test/ClassLibrary1/ClassLibrary1.csproj b/test/ClassLibrary1/ClassLibrary1.csproj deleted file mode 100644 index 475c729948..0000000000 --- a/test/ClassLibrary1/ClassLibrary1.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net8.0 - enable - Latest - - - - - - - -