返回提交历史
Modified
XFEExtension.NetCore.AutoImplement.Analyzer/Generator/ImplementAutoGenerator.cs
+126
-63
Modified
XFEExtension.NetCore.AutoImplement.Test/MyTestClass.cs
+1
-1
Modified
XFEExtension.NetCore.AutoImplement.Test/Program.cs
+5
-1
Modified
XFEExtension.NetCore.AutoImplement/XFEExtension.NetCore.AutoImplement.csproj
+5
-3
XFEstudio/XFEExtension.NetCore.AutoImplement
现在,可以通过在CreateImpl特性的传参中设置属性来实现自定义实现类的类名、命名空间及可访问性修饰符 且现在的实现类默认使用`internal`和`partial`修饰符,可以更加方便的修改实现类中的内容
f81581c
代码差异
4 个文件
+137
-68
@@ -1,11 +1,8 @@
1
1
using Microsoft.CodeAnalysis;
2
2
using Microsoft.CodeAnalysis.CSharp;
3
3
using Microsoft.CodeAnalysis.CSharp.Syntax;
4
using System;
5
using System.Diagnostics;
4
using System.Collections.Generic;
6
5
using System.Linq;
7
using System.Threading;
8
using XFEExtension.NetCore.AutoImplement.Analyzer.Model;
9
6
10
7
namespace XFEExtension.NetCore.AutoImplement.Analyzer.Generator;
11
8
@@ -17,69 +14,135 @@ public class ImplementAutoGenerator : IIncrementalGenerator
17
14
//var implementationProvider = context.SyntaxProvider.ForAttributeWithMetadataName("CreateImpl",
18
15
//static (x, _) => x is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Any(IsCreateImplAttribute),
19
16
//GetCreateImpl);
20
var implementationProvider = context.SyntaxProvider.ForAttributeWithMetadataName("CreateImpl",
21
static (x, _) => x is ClassDeclarationSyntax classDeclaration,
22
GetCreateImpl);
23
context.RegisterSourceOutput(implementationProvider.Combine(context.CompilationProvider), Generate);
17
//context.RegisterSourceOutput(implementationProvider.Combine(context.CompilationProvider), Generate);
18
context.RegisterSourceOutput(context.CompilationProvider, Generate);
24
19
}
25
20
26
public void Generate(SourceProductionContext context, (ImplementationInfo? ImplementationInfo, Compilation Compilation) args)
21
//public void Generate(SourceProductionContext context, (ImplementationInfo? ImplementationInfo, Compilation Compilation) args)
22
//{
23
// if (args.ImplementationInfo is null)
24
// return;
25
// var root = args.ImplementationInfo.ClassDeclaration.SyntaxTree.GetRoot();
26
// var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
27
// FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
28
// var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
29
// if (namespaceResults != null && namespaceResults.Count() > 0)
30
// fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
31
// var implementationSyntaxTree = GenerateImplementationSyntaxTree(args.ImplementationInfo.ClassDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, args.ImplementationInfo.TargetClassName, args.ImplementationInfo.Modifiers, args.ImplementationInfo.NameSpace);
32
// context.AddSource($"{args.ImplementationInfo.TargetClassName}.g.cs", implementationSyntaxTree.ToString());
33
//}
34
35
public void Generate(SourceProductionContext context, Compilation compilation)
27
36
{
28
if (args.ImplementationInfo is null)
29
return;
30
var root = args.ImplementationInfo.ClassDeclaration.SyntaxTree.GetRoot();
31
var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
32
FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
33
var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
34
if (namespaceResults != null && namespaceResults.Count() > 0)
35
fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
36
var implementationSyntaxTree = GenerateImplementationSyntaxTree(args.ImplementationInfo.ClassDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, args.ImplementationInfo.TargetClassName, args.ImplementationInfo.Modifiers, args.ImplementationInfo.NameSpace);
37
context.AddSource($"{args.ImplementationInfo.TargetClassName}.g.cs", implementationSyntaxTree.ToString());
37
var syntaxTrees = compilation.SyntaxTrees;
38
foreach (var syntaxTree in syntaxTrees)
39
{
40
var root = syntaxTree.GetRoot();
41
var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>()
42
.Where(classDeclaration => classDeclaration.AttributeLists.Any(IsCreateImplAttribute));
43
var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
44
FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
45
var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
46
if (namespaceResults != null && namespaceResults.Count() > 0)
47
fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
48
foreach (var classDeclaration in classDeclarations)
49
{
50
foreach (var attribute in GetCreateImplAttributeList(classDeclaration))
51
{
52
var className = $"{classDeclaration.Identifier.ValueText}Impl";
53
var modifiers = new string[] { "internal", "partial" };
54
var nameSpace = string.Empty;
55
if (attribute.ArgumentList is not null && attribute.ArgumentList.Arguments.Count > 0)
56
{
57
foreach (var argument in attribute.ArgumentList.Arguments)
58
{
59
if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax && literalExpressionSyntax.IsKind(SyntaxKind.StringLiteralExpression))
60
{
61
className = literalExpressionSyntax.Token.ValueText;
62
}
63
else if (argument.NameEquals is NameEqualsSyntax nameEqualsSyntax)
64
{
65
var argumentName = nameEqualsSyntax.Name.Identifier.ValueText;
66
if (argumentName == "ClassName" && argument.Expression is LiteralExpressionSyntax classNameLiteral && classNameLiteral.IsKind(SyntaxKind.StringLiteralExpression))
67
{
68
className = classNameLiteral.Token.ValueText;
69
}
70
else if (argumentName == "Modifiers")
71
{
72
if (argument.Expression is ArrayCreationExpressionSyntax arrayCreationExpressionSyntax)
73
{
74
if (arrayCreationExpressionSyntax.Initializer is InitializerExpressionSyntax initializerExpressionSyntax)
75
{
76
modifiers = [.. initializerExpressionSyntax.Expressions.OfType<LiteralExpressionSyntax>()
77
.Where(literal => literal.IsKind(SyntaxKind.StringLiteralExpression))
78
.Select(literal => literal.Token.ValueText)
79
.Where(s => !string.IsNullOrEmpty(s))];
80
}
81
}
82
else if (argument.Expression is CollectionExpressionSyntax collectionExpressionSyntax)
83
{
84
modifiers = [.. collectionExpressionSyntax.ChildNodes().OfType<ExpressionElementSyntax>()
85
.Select(element => element.Expression.ToString().Replace("\"",""))
86
.Where(s => !string.IsNullOrEmpty(s))];
87
}
88
}
89
else if (argumentName == "NameSpace" && argument.Expression is LiteralExpressionSyntax nameSpaceLiteral && nameSpaceLiteral.IsKind(SyntaxKind.StringLiteralExpression))
90
{
91
nameSpace = nameSpaceLiteral.Token.ValueText;
92
}
93
}
94
}
95
}
96
var implementationSyntaxTree = GenerateImplementationSyntaxTree(classDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, className, modifiers, nameSpace);
97
context.AddSource($"{className}.g.cs", implementationSyntaxTree.ToString());
98
}
99
}
100
}
38
101
}
39
102
40
103
private static bool IsCreateImplAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "CreateImpl");
41
104
42
//public static List<AttributeSyntax> GetCreateImplAttributeList(ClassDeclarationSyntax classDeclaration) => [.. classDeclaration.AttributeLists.Where(IsCreateImplAttribute).SelectMany(attributeList => attributeList.Attributes)];
105
public static List<AttributeSyntax> GetCreateImplAttributeList(ClassDeclarationSyntax classDeclaration) => [.. classDeclaration.AttributeLists.Where(IsCreateImplAttribute).SelectMany(attributeList => attributeList.Attributes)];
43
106
44
public static ImplementationInfo? GetCreateImpl(GeneratorAttributeSyntaxContext context, CancellationToken token)
45
{
46
if (context is { TargetSymbol: INamedTypeSymbol } && context.Attributes.First().ConstructorArguments.Length > 0)
47
Debugger.Launch();
48
token.ThrowIfCancellationRequested();
49
var classDeclaration = (ClassDeclarationSyntax)context.TargetNode;
50
var attributeData = context.Attributes.FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "CreateImpl");
51
if (attributeData is null)
52
return null;
53
var className = classDeclaration.Identifier.ValueText;
54
var targetClassName = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "ClassName").Value.Value as string ?? $"{className}Impl";
55
//var targetClassName = $"{className}Impl";
56
var nameSpace = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "NameSpace").Value.Value as string ?? string.Empty;
57
//var nameSpace = string.Empty;
58
//var value = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value;
59
//var modifiers = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value.Values.Select(v => v.Value?.ToString() ?? string.Empty).Where(s => !string.IsNullOrEmpty(s)).ToArray() ?? [];
60
var modifiers = Array.Empty<string>();
61
//foreach (var attributeList in classDeclaration.AttributeLists)
62
//{
63
// foreach (var attribute in attributeList.Attributes)
64
// {
65
// if (attribute is null || attribute.Name.ToString() != "CreateImpl")
66
// continue;
67
// foreach (var argument in attribute.ArgumentList.Arguments)
68
// {
107
//public static ImplementationInfo? GetCreateImpl(GeneratorAttributeSyntaxContext context, CancellationToken token)
108
//{
109
// if (context is { TargetSymbol: INamedTypeSymbol })
110
// Debugger.Launch();
111
// token.ThrowIfCancellationRequested();
112
// var classDeclaration = (ClassDeclarationSyntax)context.TargetNode;
113
// var attributeData = context.Attributes.FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "CreateImpl");
114
// if (attributeData is null)
115
// return null;
116
// var className = classDeclaration.Identifier.ValueText;
117
// var targetClassName = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "ClassName").Value.Value as string ?? $"{className}Impl";
118
// //var targetClassName = $"{className}Impl";
119
// var nameSpace = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "NameSpace").Value.Value as string ?? string.Empty;
120
// //var nameSpace = string.Empty;
121
// //var value = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value;
122
// //var modifiers = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value.Values.Select(v => v.Value?.ToString() ?? string.Empty).Where(s => !string.IsNullOrEmpty(s)).ToArray() ?? [];
123
// var modifiers = Array.Empty<string>();
124
// //foreach (var attributeList in classDeclaration.AttributeLists)
125
// //{
126
// // foreach (var attribute in attributeList.Attributes)
127
// // {
128
// // if (attribute is null || attribute.Name.ToString() != "CreateImpl")
129
// // continue;
130
// // foreach (var argument in attribute.ArgumentList.Arguments)
131
// // {
69
132
70
// }
71
// }
72
//}
73
if (modifiers.Length == 0)
74
modifiers = ["internal", "partial"];
75
return new ImplementationInfo
76
{
77
ClassDeclaration = classDeclaration,
78
TargetClassName = targetClassName,
79
Modifiers = modifiers,
80
NameSpace = nameSpace
81
};
82
}
133
// // }
134
// // }
135
// //}
136
// if (modifiers.Length == 0)
137
// modifiers = ["internal", "partial"];
138
// return new ImplementationInfo
139
// {
140
// ClassDeclaration = classDeclaration,
141
// TargetClassName = targetClassName,
142
// Modifiers = modifiers,
143
// NameSpace = nameSpace
144
// };
145
//}
83
146
84
147
private static SyntaxTree GenerateImplementationSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax, string className, string[] modifierParameters, string nameSpace)
85
148
{
@@ -106,15 +169,15 @@ public class ImplementAutoGenerator : IIncrementalGenerator
106
169
}
107
170
else
108
171
{
109
implementationClass = SyntaxFactory.ClassDeclaration($"{className}Impl")
172
implementationClass = SyntaxFactory.ClassDeclaration(className)
110
173
.AddModifiers(modifiers)
111
.AddMembers(SyntaxFactory.ConstructorDeclaration($"{className}Impl")
174
.AddMembers(SyntaxFactory.ConstructorDeclaration(className)
112
175
.AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
113
176
.WithBody(SyntaxFactory.Block())
114
177
.WithParameterList(classDeclaration.ParameterList)
115
178
.WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(classDeclaration.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier))))))))
116
179
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
117
/// <seealso cref=""{className}Impl""/> 是根据 <seealso cref=""{className}""/> 自动生成的实现类
180
/// <seealso cref=""{className}""/> 是根据 <seealso cref=""{fatherClassName}""/> 自动生成的实现类
118
181
/// </summary>
119
182
"))
120
183
.NormalizeWhitespace();
@@ -122,12 +185,12 @@ public class ImplementAutoGenerator : IIncrementalGenerator
122
185
if (classDeclaration.TypeParameterList is TypeParameterListSyntax typeParameterListSyntax && typeParameterListSyntax.Parameters.Count > 0)
123
186
{
124
187
implementationClass = implementationClass.AddTypeParameterListParameters([.. typeParameterListSyntax.Parameters])
125
.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"{className}{typeParameterListSyntax}")))
188
.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"{fatherClassName}{typeParameterListSyntax}")))
126
189
.AddConstraintClauses([.. classDeclaration.ConstraintClauses]);
127
190
}
128
191
else
129
192
{
130
implementationClass = implementationClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(className)));
193
implementationClass = implementationClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(fatherClassName)));
131
194
}
132
195
MemberDeclarationSyntax memberDeclaration;
133
196
if (nameSpace != string.Empty)
@@ -2,7 +2,7 @@
2
2
3
3
[CreateImpl("MyTestClassA")]
4
4
[CreateImpl("MyTestClassB", Modifiers = ["public", "partial"])]
5
[CreateImpl("MyTestClassC", Modifiers = ["public", "partial"])]
5
[CreateImpl("MyTestClassC", Modifiers = new string[] { "public", "partial" })]
6
6
public abstract class MyTestClass<T, F>(T obj, F obj2) where T : class where F : new()
7
7
{
8
8
public T TProperty { get; set; } = obj;
@@ -1,2 +1,6 @@
1
1
// See https://aka.ms/new-console-template for more information
2
Console.WriteLine("Hello, World!");
2
using XFEExtension.NetCore.AutoImplement.Test;
3
4
Console.WriteLine("Hello, World!");
5
6
var myClass = new MyTestClassC();
@@ -5,7 +5,7 @@
5
5
<ImplicitUsings>enable</ImplicitUsings>
6
6
<Nullable>enable</Nullable>
7
7
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
8
<Version>1.0.1</Version>
8
<Version>1.1.0</Version>
9
9
<Title>AutoImplement</Title>
10
10
<Authors>XFEstudio</Authors>
11
11
<Company>寰宇朽力网络科技有限公司</Company>
@@ -16,8 +16,10 @@
16
16
<PackageReadmeFile>README.md</PackageReadmeFile>
17
17
<RepositoryUrl>https://github.com/XFEstudio/XFEExtension.NetCore.AutoImplement.git</RepositoryUrl>
18
18
<PackageTags>XFE;impl;implement;auto</PackageTags>
19
<PackageReleaseNotes>修复了不能实现泛型类的bug
20
修复了泛型类无法继承泛型约束条件的bug</PackageReleaseNotes>
19
<PackageReleaseNotes>
20
现在,可以通过在CreateImpl特性的传参中设置属性来实现自定义实现类的类名、命名空间及可访问性修饰符
21
且现在的实现类默认使用`internal`和`partial`修饰符,可以更加方便的修改实现类中的内容
22
</PackageReleaseNotes>
21
23
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
22
24
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
23
25
</PropertyGroup>