Skip to content

Commit

Permalink
Report Diagnostic GEN031 #1
Browse files Browse the repository at this point in the history
  • Loading branch information
vipwan committed Nov 4, 2023
1 parent 445aabd commit 0d67e74
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 33 deletions.
88 changes: 60 additions & 28 deletions Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";//推荐生成


/// <summary>
Expand Down Expand Up @@ -67,12 +68,30 @@ public class SourceGenAnalyzer : DiagnosticAnalyzer
helpLinkUri: helplink,
isEnabledByDefault: true);


/// <summary>
/// 推荐使用自动生成
/// </summary>
#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<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
InvalidDeclareError,
InvalidDeclareNameError,
SuggestDeclareNameWarning
SuggestDeclareNameWarning,
SuggestAutoGen
);

const string AttributeValueMetadataName = "AutoGen";
Expand All @@ -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);
Expand Down
78 changes: 77 additions & 1 deletion Biwen.AutoClassGen.Gen/SourceGenCodeFixProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -24,7 +26,8 @@ public sealed class SourceGenCodeFixProvider : CodeFixProvider
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
SourceGenAnalyzer.GEN001,
SourceGenAnalyzer.GEN011,
SourceGenAnalyzer.GEN021
SourceGenAnalyzer.GEN021,
SourceGenAnalyzer.GEN031
);

public override async Task RegisterCodeFixesAsync(CodeFixContext context)
Expand Down Expand Up @@ -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);
}
}
}


/// <summary>
/// 替换字符串
/// </summary>
/// <param name="document"></param>
/// <param name="nodeToReplace"></param>
/// <param name="stringText"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task<Document> ReplaceWithNameOfAsync(Document document, SyntaxNode nodeToReplace,
string stringText, CancellationToken cancellationToken)
{
Expand All @@ -117,5 +138,60 @@ private static async Task<Document> ReplaceWithNameOfAsync(Document document, Sy
var newRoot = root?.ReplaceNode(nodeToReplace, textExpression);
return document.WithSyntaxRoot(newRoot!);
}

/// <summary>
/// 给接口添加特性
/// </summary>
/// <param name="document"></param>
/// <param name="nodeToAddAttribute"></param>
/// <param name="attributeName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task<Document> 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。

}
}
}
5 changes: 1 addition & 4 deletions Biwen.AutoClassGen.TestConsole/Classes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ public interface IQueryRequest : IPager, IQuery
{
}

/// <summary>
/// 多租户请求
/// </summary>
[AutoGen("TenantRealRequest", "Biwen.AutoClassGen.Models")]

public interface ITenantRealRequest : ITenantRequest
{

Expand Down
3 changes: 3 additions & 0 deletions README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,6 @@ namespace Biwen.AutoClassGen.Models
### 错误码

- GEN001: 标注特性[AutoGen]的接口必须继承至少一个接口
- GEN011: 非法的类命名
- GEN021: 警告没有使用相同的命名空间
- GEN031: 建议标注特性[AutoGen]以便生成类
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 0d67e74

Please sign in to comment.