From 0d67e74a31b0890b57a4bb896737d45f22f12b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E9=9B=85=E8=99=8E?= Date: Sat, 4 Nov 2023 16:17:22 +0800 Subject: [PATCH] Report Diagnostic GEN031 #1 --- Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs | 88 +++++++++++++------ .../SourceGenCodeFixProvider.cs | 78 +++++++++++++++- Biwen.AutoClassGen.TestConsole/Classes.cs | 5 +- README-zh.md | 3 + README.md | 1 + 5 files changed, 142 insertions(+), 33 deletions(-) diff --git a/Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs b/Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs index 1e42fa6..f6aa4a6 100644 --- a/Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs +++ b/Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs @@ -19,6 +19,7 @@ public class SourceGenAnalyzer : DiagnosticAnalyzer public const string GEN001 = "GEN001"; public const string GEN011 = "GEN011"; public const string GEN021 = "GEN021"; + public const string GEN031 = "GEN031";//推荐生成 /// @@ -67,12 +68,30 @@ public class SourceGenAnalyzer : DiagnosticAnalyzer helpLinkUri: helplink, isEnabledByDefault: true); + + /// + /// 推荐使用自动生成 + /// +#pragma warning disable RS2008 // 启用分析器发布跟踪 + private static readonly DiagnosticDescriptor SuggestAutoGen = new(id: GEN031, +#pragma warning restore RS2008 // 启用分析器发布跟踪 + title: "使用[AutoGen]自动生成", +#pragma warning disable RS1032 // 正确定义诊断消息 + messageFormat: "使用[AutoGen]自动生成.", +#pragma warning restore RS1032 // 正确定义诊断消息 + category: typeof(SourceGenerator).Assembly.GetName().Name, + DiagnosticSeverity.Info, + helpLinkUri: helplink, + isEnabledByDefault: true); + + #endregion public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create( InvalidDeclareError, InvalidDeclareNameError, - SuggestDeclareNameWarning + SuggestDeclareNameWarning, + SuggestAutoGen ); const string AttributeValueMetadataName = "AutoGen"; @@ -86,40 +105,53 @@ public override void Initialize(AnalysisContext context) { // Find implicitly typed interface declarations. var declaration = (InterfaceDeclarationSyntax)ctx.Node; - if (declaration == null) return; - if (declaration.AttributeLists.Count == 0) return; - foreach (var attr in declaration.AttributeLists.AsEnumerable()) + if (declaration.AttributeLists.Count > 0) { - if (attr.Attributes.Any(x => x.Name.ToString() == AttributeValueMetadataName)) + foreach (var attr in declaration.AttributeLists.AsEnumerable()) { - if (declaration.BaseList == null || !declaration.BaseList.Types.Any()) - { - // issue error - ctx.ReportDiagnostic(Diagnostic.Create(InvalidDeclareError, attr.GetLocation())); - } - - var arg0 = attr.Attributes.First(x => x.Name.ToString() == AttributeValueMetadataName) - .ArgumentList!.Arguments[0]; - - var arg1 = attr.Attributes.First(x => x.Name.ToString() == AttributeValueMetadataName) - .ArgumentList!.Arguments[1]; - - if (declaration.Identifier.Text == arg0.GetText().ToString().Replace("\"", "")) + if (attr.Attributes.Any(x => x.Name.ToString() == AttributeValueMetadataName)) { - var location = arg0?.GetLocation(); - // issue error - ctx.ReportDiagnostic(Diagnostic.Create(InvalidDeclareNameError, location)); + if (declaration.BaseList == null || !declaration.BaseList.Types.Any()) + { + // issue error + ctx.ReportDiagnostic(Diagnostic.Create(InvalidDeclareError, attr.GetLocation())); + } + + var arg0 = attr.Attributes.First(x => x.Name.ToString() == AttributeValueMetadataName) + .ArgumentList!.Arguments[0]; + + var arg1 = attr.Attributes.First(x => x.Name.ToString() == AttributeValueMetadataName) + .ArgumentList!.Arguments[1]; + + if (declaration.Identifier.Text == arg0.GetText().ToString().Replace("\"", "")) + { + var location = arg0?.GetLocation(); + // issue error + ctx.ReportDiagnostic(Diagnostic.Create(InvalidDeclareNameError, location)); + } + + if (declaration.Parent is NamespaceDeclarationSyntax @namespace && + @namespace?.Name.ToString() != arg1.GetText().ToString().Replace("\"", "")) + { + var location = arg1?.GetLocation(); + // issue warning + ctx.ReportDiagnostic(Diagnostic.Create(SuggestDeclareNameWarning, location)); + } } + } + } - if (declaration.Parent is NamespaceDeclarationSyntax @namespace && - @namespace?.Name.ToString() != arg1.GetText().ToString().Replace("\"", "")) - { - var location = arg1?.GetLocation(); - // issue warning - ctx.ReportDiagnostic(Diagnostic.Create(SuggestDeclareNameWarning, location)); - } + //suggest + if (declaration.BaseList != null && declaration.BaseList.Types.Any(x => x.IsKind(SyntaxKind.SimpleBaseType))) + { + var haveAttr = declaration.AttributeLists.Any(x => x.Attributes.Any(x => x.Name.ToString() == AttributeValueMetadataName)); + if (!haveAttr) + { + var location = declaration.GetLocation(); + // issue suggest + ctx.ReportDiagnostic(Diagnostic.Create(SuggestAutoGen, location)); } } }, SyntaxKind.InterfaceDeclaration); diff --git a/Biwen.AutoClassGen.Gen/SourceGenCodeFixProvider.cs b/Biwen.AutoClassGen.Gen/SourceGenCodeFixProvider.cs index c17771b..74dc448 100644 --- a/Biwen.AutoClassGen.Gen/SourceGenCodeFixProvider.cs +++ b/Biwen.AutoClassGen.Gen/SourceGenCodeFixProvider.cs @@ -6,7 +6,9 @@ using Microsoft.CodeAnalysis.Editing; using System.Collections.Immutable; using System.Composition; +using System.Data; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -24,7 +26,8 @@ public sealed class SourceGenCodeFixProvider : CodeFixProvider public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create( SourceGenAnalyzer.GEN001, SourceGenAnalyzer.GEN011, - SourceGenAnalyzer.GEN021 + SourceGenAnalyzer.GEN021, + SourceGenAnalyzer.GEN031 ); public override async Task RegisterCodeFixesAsync(CodeFixContext context) @@ -89,8 +92,26 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) equivalenceKey: nameof(SourceGenCodeFixProvider)); context.RegisterCodeFix(action, diagnostic); } + else if (diagnostic.Id == SourceGenAnalyzer.GEN031) + { + CodeAction action = CodeAction.Create( + "GEN:添加自动生成特性[AutoGen]", + c => AddAttributeAsync(context.Document, root?.FindNode(context.Span)!, "AutoGen", c), + equivalenceKey: nameof(SourceGenCodeFixProvider)); + context.RegisterCodeFix(action, diagnostic); + } } } + + + /// + /// 替换字符串 + /// + /// + /// + /// + /// + /// private static async Task ReplaceWithNameOfAsync(Document document, SyntaxNode nodeToReplace, string stringText, CancellationToken cancellationToken) { @@ -117,5 +138,60 @@ private static async Task ReplaceWithNameOfAsync(Document document, Sy var newRoot = root?.ReplaceNode(nodeToReplace, textExpression); return document.WithSyntaxRoot(newRoot!); } + + /// + /// 给接口添加特性 + /// + /// + /// + /// + /// + /// + private static async Task AddAttributeAsync(Document document, SyntaxNode nodeToAddAttribute, + string attributeName, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + if (nodeToAddAttribute is not InterfaceDeclarationSyntax) + { + return document.WithSyntaxRoot(root!); + } + + var rootCompUnit = (CompilationUnitSyntax)root!; + //命名空间 + var @namespace = ((rootCompUnit.Members.Where( + m => m.IsKind(SyntaxKind.NamespaceDeclaration)).Single()) as NamespaceDeclarationSyntax)?.Name.ToString(); + //类名 + var @class = "YourClassName"; + + var trailingTrivia = nodeToAddAttribute.GetTrailingTrivia(); + var leadingTrivia = nodeToAddAttribute.GetLeadingTrivia(); + + var argumentLis = SyntaxFactory.AttributeArgumentList( + SyntaxFactory.SeparatedList( + new AttributeArgumentSyntax[] + { + SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression( + SyntaxKind.StringLiteralExpression,SyntaxFactory.Literal(@class))), + SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression( + SyntaxKind.StringLiteralExpression,SyntaxFactory.Literal(@namespace!))), + })); + + var attributes = (nodeToAddAttribute as InterfaceDeclarationSyntax)!.AttributeLists.Add( + SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Attribute(SyntaxFactory.IdentifierName(attributeName)) + .WithArgumentList(argumentLis) + )) + .WithTrailingTrivia(trailingTrivia) + .WithLeadingTrivia(leadingTrivia)); + +#pragma warning disable CS8604 // 引用类型参数可能为 null。 + + return document.WithSyntaxRoot( + root?.ReplaceNode( + oldNode: nodeToAddAttribute, + newNode: ((InterfaceDeclarationSyntax)nodeToAddAttribute)?.WithAttributeLists(attributes))); + +#pragma warning restore CS8604 // 引用类型参数可能为 null。 + + } } } \ No newline at end of file diff --git a/Biwen.AutoClassGen.TestConsole/Classes.cs b/Biwen.AutoClassGen.TestConsole/Classes.cs index 8ba9a47..22164dc 100644 --- a/Biwen.AutoClassGen.TestConsole/Classes.cs +++ b/Biwen.AutoClassGen.TestConsole/Classes.cs @@ -11,10 +11,7 @@ public interface IQueryRequest : IPager, IQuery { } - /// - /// 多租户请求 - /// - [AutoGen("TenantRealRequest", "Biwen.AutoClassGen.Models")] + public interface ITenantRealRequest : ITenantRequest { diff --git a/README-zh.md b/README-zh.md index d440035..21e9753 100644 --- a/README-zh.md +++ b/README-zh.md @@ -153,3 +153,6 @@ namespace Biwen.AutoClassGen.Models ### 错误码 - GEN001: 标注特性[AutoGen]的接口必须继承至少一个接口 +- GEN011: 非法的类命名 +- GEN021: 警告没有使用相同的命名空间 +- GEN031: 建议标注特性[AutoGen]以便生成类 diff --git a/README.md b/README.md index 04d827f..7e85baf 100644 --- a/README.md +++ b/README.md @@ -158,3 +158,4 @@ namespace Biwen.AutoClassGen.Models - GEN001: The interface marked [AutoGen] should be inherent one or more interface - GEN011: Illegal naming - GEN021: It is recommended to use the same namespace name +- GEN031: Suggestions to use [AutoGen] attribute to mark the interface