返回提交历史
Modified
XFEExtension.NetCore.AutoImplement.Analyzer/Generator/ImplementAutoGenerator.cs
+116
-88
Added
XFEExtension.NetCore.AutoImplement.Analyzer/Model/ImplementationInfo.cs
+11
-0
Modified
XFEExtension.NetCore.AutoImplement.Analyzer/XFEExtension.NetCore.AutoImplement.Analyzer.csproj
+2
-0
Modified
XFEExtension.NetCore.AutoImplement.Test/Program.cs
+1
-4
Modified
XFEExtension.NetCore.AutoImplement/CreateImpl.cs
+2
-11
XFEstudio/XFEExtension.NetCore.AutoImplement
完善部分特性识别
1b7dcce
代码差异
5 个文件
+132
-103
@@ -1,110 +1,138 @@
1
1
using Microsoft.CodeAnalysis;
2
2
using Microsoft.CodeAnalysis.CSharp;
3
3
using Microsoft.CodeAnalysis.CSharp.Syntax;
4
using System.Diagnostics;
4
5
using System.Linq;
6
using System.Threading;
7
using XFEExtension.NetCore.AutoImplement.Analyzer.Model;
5
8
6
namespace XFEExtension.NetCore.AutoImplement.Analyzer.Generator
9
namespace XFEExtension.NetCore.AutoImplement.Analyzer.Generator;
10
11
[Generator]
12
public class ImplementAutoGenerator : IIncrementalGenerator
7
13
{
8
[Generator]
9
public class ImplementAutoGenerator : IIncrementalGenerator
14
public void Initialize(IncrementalGeneratorInitializationContext context)
10
15
{
11
public void Initialize(IncrementalGeneratorInitializationContext context)
12
{
13
context.RegisterSourceOutput(context.CompilationProvider, Generate);
14
}
16
var implementationProvider = context.SyntaxProvider.ForAttributeWithMetadataName("CreateImpl",
17
static (x, _) => x is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Any(IsCreateImplAttribute),
18
GetCreateImpl);
19
context.RegisterSourceOutput(implementationProvider.Combine(context.CompilationProvider), Generate);
20
}
15
21
16
public void Generate(SourceProductionContext context, Compilation compilation)
17
{
18
var syntaxTrees = compilation.SyntaxTrees;
19
foreach (var syntaxTree in syntaxTrees)
20
{
21
var root = syntaxTree.GetRoot();
22
var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>()
23
.Where(classDeclaration => classDeclaration.AttributeLists.Any(IsCreateImplAttribute));
24
var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
25
FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax = null;
26
var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
27
if (namespaceResults != null && namespaceResults.Count() > 0)
28
fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
29
foreach (var classDeclaration in classDeclarations)
30
{
31
var className = classDeclaration.Identifier.ValueText;
32
var implementationSyntaxTree = GenerateImplementationSyntaxTree(classDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, className);
33
context.AddSource($"{className}Impl.g.cs", implementationSyntaxTree.ToString());
34
}
35
}
36
}
22
public void Generate(SourceProductionContext context, (ImplementationInfo? ImplementationInfo, Compilation Compilation) args)
23
{
24
Debugger.Launch();
25
if (args.ImplementationInfo is null)
26
return;
27
var root = args.ImplementationInfo.ClassDeclaration.SyntaxTree.GetRoot();
28
var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
29
FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
30
var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
31
if (namespaceResults != null && namespaceResults.Count() > 0)
32
fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
33
var implementationSyntaxTree = GenerateImplementationSyntaxTree(args.ImplementationInfo.ClassDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, args.ImplementationInfo.TargetClassName, args.ImplementationInfo.Modifiers, args.ImplementationInfo.NameSpace);
34
context.AddSource($"{args.ImplementationInfo.TargetClassName}.g.cs", implementationSyntaxTree.ToString());
35
}
37
36
38
private static bool IsCreateImplAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "CreateImpl");
37
private static bool IsCreateImplAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "CreateImpl");
39
38
40
private static SyntaxTree GenerateImplementationSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax, string className, string[] modifierParameters, string nameSpace)
39
//public static List<AttributeSyntax> GetCreateImplAttributeList(ClassDeclarationSyntax classDeclaration) => [.. classDeclaration.AttributeLists.Where(IsCreateImplAttribute).SelectMany(attributeList => attributeList.Attributes)];
40
41
public static ImplementationInfo? GetCreateImpl(GeneratorAttributeSyntaxContext context, CancellationToken token)
42
{
43
Debugger.Launch();
44
token.ThrowIfCancellationRequested();
45
var classDeclaration = (ClassDeclarationSyntax)context.TargetNode;
46
var attributeData = context.Attributes.FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "CreateImpl");
47
if (attributeData is null)
48
return null;
49
var className = classDeclaration.Identifier.ValueText;
50
var targetClassName = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "ClassName").Value.Value as string ?? $"{className}Impl";
51
var nameSpace = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "NameSpace").Value.Value as string ?? string.Empty;
52
var modifiers = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value.Values.Select(v => v.Value?.ToString() ?? string.Empty).Where(s => !string.IsNullOrEmpty(s)).ToArray() ?? [];//此处出现问题
53
if (modifiers.Length == 0)
54
modifiers = ["internal", "partial"];
55
return new ImplementationInfo
56
{
57
ClassDeclaration = classDeclaration,
58
TargetClassName = targetClassName,
59
Modifiers = modifiers,
60
NameSpace = nameSpace
61
};
62
}
63
64
private static SyntaxTree GenerateImplementationSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax, string className, string[] modifierParameters, string nameSpace)
65
{
66
var fatherClassName = classDeclaration.Identifier.ValueText;
67
var modifiers = modifierParameters.Select(modifier => SyntaxFactory.ParseToken(modifier)).ToArray();
68
ClassDeclarationSyntax implementationClass;
69
if (classDeclaration.ParameterList is null)
41
70
{
42
var fatherClassName = classDeclaration.Identifier.ValueText;
43
var modifiers = modifierParameters.Select(modifier => SyntaxFactory.ParseToken(modifier)).ToArray();
44
ClassDeclarationSyntax implementationClass;
45
if (classDeclaration.ParameterList is null)
46
{
47
implementationClass = SyntaxFactory.ClassDeclaration(className)
48
.AddModifiers(modifiers)
49
.AddMembers(classDeclaration.Members.OfType<ConstructorDeclarationSyntax>().Select(constructor =>
50
{
51
return SyntaxFactory.ConstructorDeclaration(className)
52
.AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
53
.WithBody(SyntaxFactory.Block())
54
.WithParameterList(constructor.ParameterList)
55
.WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(constructor.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier)))))));
56
}).ToArray())
57
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
71
implementationClass = SyntaxFactory.ClassDeclaration(className)
72
.AddModifiers(modifiers)
73
.AddMembers([.. classDeclaration.Members.OfType<ConstructorDeclarationSyntax>().Select(constructor =>
74
{
75
return SyntaxFactory.ConstructorDeclaration(className)
76
.AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
77
.WithBody(SyntaxFactory.Block())
78
.WithParameterList(constructor.ParameterList)
79
.WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(constructor.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier)))))));
80
})])
81
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
58
82
/// <seealso cref=""{className}""/> 是根据 <seealso cref=""{fatherClassName}""/> 自动生成的实现类
59
83
/// </summary>
60
84
"))
61
.NormalizeWhitespace();
62
}
63
else
64
{
65
implementationClass = SyntaxFactory.ClassDeclaration($"{className}Impl")
66
.AddModifiers(modifiers)
67
.AddMembers(SyntaxFactory.ConstructorDeclaration($"{className}Impl")
68
.AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
69
.WithBody(SyntaxFactory.Block())
70
.WithParameterList(classDeclaration.ParameterList)
71
.WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(classDeclaration.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier))))))))
72
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
85
.NormalizeWhitespace();
86
}
87
else
88
{
89
implementationClass = SyntaxFactory.ClassDeclaration($"{className}Impl")
90
.AddModifiers(modifiers)
91
.AddMembers(SyntaxFactory.ConstructorDeclaration($"{className}Impl")
92
.AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
93
.WithBody(SyntaxFactory.Block())
94
.WithParameterList(classDeclaration.ParameterList)
95
.WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(classDeclaration.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier))))))))
96
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
73
97
/// <seealso cref=""{className}Impl""/> 是根据 <seealso cref=""{className}""/> 自动生成的实现类
74
98
/// </summary>
75
99
"))
76
.NormalizeWhitespace();
77
}
78
if (classDeclaration.TypeParameterList is TypeParameterListSyntax typeParameterListSyntax && typeParameterListSyntax.Parameters.Count > 0)
79
{
80
implementationClass = implementationClass.AddTypeParameterListParameters(typeParameterListSyntax.Parameters.ToArray())
81
.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"{className}{typeParameterListSyntax}")))
82
.AddConstraintClauses(classDeclaration.ConstraintClauses.ToArray());
83
}
84
else
85
{
86
implementationClass = implementationClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(className)));
87
}
88
MemberDeclarationSyntax memberDeclaration;
89
if (fileScopedNamespaceDeclarationSyntax is null)
90
{
91
var namespaceDeclaration = classDeclaration.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
92
if (namespaceDeclaration is null)
93
memberDeclaration = implementationClass;
94
else
95
memberDeclaration = SyntaxFactory.NamespaceDeclaration(namespaceDeclaration.Name)
96
.AddMembers(implementationClass);
97
}
100
.NormalizeWhitespace();
101
}
102
if (classDeclaration.TypeParameterList is TypeParameterListSyntax typeParameterListSyntax && typeParameterListSyntax.Parameters.Count > 0)
103
{
104
implementationClass = implementationClass.AddTypeParameterListParameters([.. typeParameterListSyntax.Parameters])
105
.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"{className}{typeParameterListSyntax}")))
106
.AddConstraintClauses([.. classDeclaration.ConstraintClauses]);
107
}
108
else
109
{
110
implementationClass = implementationClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(className)));
111
}
112
MemberDeclarationSyntax memberDeclaration;
113
if (nameSpace != string.Empty)
114
{
115
memberDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(nameSpace))
116
.AddMembers(implementationClass);
117
}
118
else if (fileScopedNamespaceDeclarationSyntax is null)
119
{
120
var namespaceDeclaration = classDeclaration.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
121
if (namespaceDeclaration is null)
122
memberDeclaration = implementationClass;
98
123
else
99
{
100
memberDeclaration = SyntaxFactory.FileScopedNamespaceDeclaration(fileScopedNamespaceDeclarationSyntax.Name)
124
memberDeclaration = SyntaxFactory.NamespaceDeclaration(namespaceDeclaration.Name)
101
125
.AddMembers(implementationClass);
102
}
103
var implementationCompilationUnit = SyntaxFactory.CompilationUnit()
104
.AddUsings(usingDirectiveSyntaxes)
105
.AddMembers(memberDeclaration)
106
.NormalizeWhitespace();
107
return SyntaxFactory.SyntaxTree(implementationCompilationUnit);
108
126
}
127
else
128
{
129
memberDeclaration = SyntaxFactory.FileScopedNamespaceDeclaration(fileScopedNamespaceDeclarationSyntax.Name)
130
.AddMembers(implementationClass);
131
}
132
var implementationCompilationUnit = SyntaxFactory.CompilationUnit()
133
.AddUsings(usingDirectiveSyntaxes)
134
.AddMembers(memberDeclaration)
135
.NormalizeWhitespace();
136
return SyntaxFactory.SyntaxTree(implementationCompilationUnit);
109
137
}
110
138
}
@@ -0,0 +1,11 @@
1
using Microsoft.CodeAnalysis.CSharp.Syntax;
2
3
namespace XFEExtension.NetCore.AutoImplement.Analyzer.Model;
4
5
public class ImplementationInfo
6
{
7
public ClassDeclarationSyntax ClassDeclaration { get; set; }
8
public string TargetClassName { get; set; } = string.Empty;
9
public string[] Modifiers { get; set; } = [];
10
public string NameSpace { get; set; } = string.Empty;
11
}
@@ -2,6 +2,8 @@
2
2
3
3
<PropertyGroup>
4
4
<TargetFramework>netstandard2.0</TargetFramework>
5
<LangVersion>13.0</LangVersion>
6
<Nullable>enable</Nullable>
5
7
</PropertyGroup>
6
8
7
9
<PropertyGroup>
@@ -1,5 +1,2 @@
1
1
// See https://aka.ms/new-console-template for more information
2
using XFEExtension.NetCore.AutoImplement.Test;
3
4
Console.WriteLine("Hello, World!");
5
var myClass = new MyTestClassImpl<string, Program>("123", new());
2
Console.WriteLine("Hello, World!");
@@ -8,15 +8,6 @@ public class CreateImpl : Attribute
8
8
{
9
9
public string? ClassName { get; set; }
10
10
public string? NameSpace { get; set; }
11
public string[]? Modifier { get; set; }
11
public string[]? Modifiers { get; set; }
12
12
public CreateImpl() { }
13
public CreateImpl(string className)
14
{
15
ClassName = className;
16
}
17
public CreateImpl(string className, params string[] modifier)
18
{
19
ClassName = className;
20
Modifier = modifier;
21
}
22
}
13
}