|
3 | 3 | using Microsoft.CodeAnalysis.CSharp; |
4 | 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; |
5 | 5 | using Microsoft.CodeAnalysis.Text; |
6 | | -using System; |
7 | | -using System.Collections.Generic; |
8 | | -using System.Collections.Immutable; |
9 | | -using System.Diagnostics; |
10 | | -using System.Linq; |
11 | | -using System.Security.Cryptography.X509Certificates; |
12 | 6 | using System.Text; |
13 | | -using System.Threading; |
14 | | -using System.Threading.Tasks; |
15 | 7 | using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; |
16 | 8 |
|
17 | 9 | namespace EntityFrameworkCore.Projectables.Generator |
@@ -41,167 +33,127 @@ public void Initialize(IncrementalGeneratorInitializationContext context) |
41 | 33 | { |
42 | 34 | // Do a simple filter for members |
43 | 35 | IncrementalValuesProvider<MemberDeclarationSyntax> memberDeclarations = context.SyntaxProvider |
44 | | - .CreateSyntaxProvider( |
45 | | - predicate: static (s, _) => s is MemberDeclarationSyntax m && m.AttributeLists.Count > 0, |
46 | | - transform: static (c, _) => GetSemanticTargetForGeneration(c)) |
47 | | - .Where(static m => m is not null)!; // filter out attributed enums that we don't care about |
| 36 | + .ForAttributeWithMetadataName( |
| 37 | + ProjectablesAttributeName, |
| 38 | + predicate: static (s, _) => s is MemberDeclarationSyntax, |
| 39 | + transform: static (c, _) => (MemberDeclarationSyntax)c.TargetNode) |
| 40 | + .WithComparer(new MemberDeclarationSyntaxEqualityComparer()); |
48 | 41 |
|
49 | 42 | // Combine the selected enums with the `Compilation` |
50 | | - IncrementalValueProvider<(Compilation, ImmutableArray<MemberDeclarationSyntax>)> compilationAndEnums |
51 | | - = context.CompilationProvider.Combine(memberDeclarations.Collect()); |
| 43 | + IncrementalValuesProvider<(MemberDeclarationSyntax, Compilation)> compilationAndMemberPairs = memberDeclarations |
| 44 | + .Combine(context.CompilationProvider) |
| 45 | + .WithComparer(new MemberDeclarationSyntaxAndCompilationEqualityComparer()); |
52 | 46 |
|
53 | 47 | // Generate the source using the compilation and enums |
54 | | - context.RegisterImplementationSourceOutput(compilationAndEnums, |
| 48 | + context.RegisterImplementationSourceOutput(compilationAndMemberPairs, |
55 | 49 | static (spc, source) => Execute(source.Item1, source.Item2, spc)); |
56 | 50 | } |
57 | 51 |
|
58 | | - static MemberDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context) |
| 52 | + static void Execute(MemberDeclarationSyntax member, Compilation compilation, SourceProductionContext context) |
59 | 53 | { |
60 | | - // we know the node is a MemberDeclarationSyntax |
61 | | - var memberDeclarationSyntax = (MemberDeclarationSyntax)context.Node; |
| 54 | + var projectable = ProjectableInterpreter.GetDescriptor(compilation, member, context); |
62 | 55 |
|
63 | | - // loop through all the attributes on the method |
64 | | - foreach (var attributeListSyntax in memberDeclarationSyntax.AttributeLists) |
| 56 | + if (projectable is null) |
65 | 57 | { |
66 | | - foreach (var attributeSyntax in attributeListSyntax.Attributes) |
67 | | - { |
68 | | - if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) |
69 | | - { |
70 | | - // weird, we couldn't get the symbol, ignore it |
71 | | - continue; |
72 | | - } |
73 | | - |
74 | | - var attributeContainingTypeSymbol = attributeSymbol.ContainingType; |
75 | | - var fullName = attributeContainingTypeSymbol.ToDisplayString(); |
76 | | - |
77 | | - // Is the attribute the [Projcetable] attribute? |
78 | | - if (fullName == ProjectablesAttributeName) |
79 | | - { |
80 | | - // return the enum |
81 | | - return memberDeclarationSyntax; |
82 | | - } |
83 | | - } |
84 | | - } |
85 | | - |
86 | | - // we didn't find the attribute we were looking for |
87 | | - return null; |
88 | | - } |
89 | | - |
90 | | - static void Execute(Compilation compilation, ImmutableArray<MemberDeclarationSyntax> members, SourceProductionContext context) |
91 | | - { |
92 | | - if (members.IsDefaultOrEmpty) |
93 | | - { |
94 | | - // nothing to do yet |
95 | 58 | return; |
96 | 59 | } |
97 | 60 |
|
98 | | - var projectables = members |
99 | | - .Select(x => ProjectableInterpreter.GetDescriptor(compilation, x, context)) |
100 | | - .Where(x => x is not null) |
101 | | - .Select(x => x!); |
102 | | - |
103 | | - var resultBuilder = new StringBuilder(); |
104 | | - |
105 | | - foreach (var projectable in projectables) |
| 61 | + if (projectable.MemberName is null) |
106 | 62 | { |
107 | | - if (projectable.MemberName is null) |
108 | | - { |
109 | | - throw new InvalidOperationException("Expected a memberName here"); |
110 | | - } |
| 63 | + throw new InvalidOperationException("Expected a memberName here"); |
| 64 | + } |
111 | 65 |
|
112 | | - var generatedClassName = ProjectionExpressionClassNameGenerator.GenerateName(projectable.ClassNamespace, projectable.NestedInClassNames, projectable.MemberName); |
113 | | - var generatedFileName = projectable.ClassTypeParameterList is not null ? $"{generatedClassName}-{projectable.ClassTypeParameterList.ChildNodes().Count()}.g.cs" : $"{generatedClassName}.g.cs"; |
| 66 | + var generatedClassName = ProjectionExpressionClassNameGenerator.GenerateName(projectable.ClassNamespace, projectable.NestedInClassNames, projectable.MemberName); |
| 67 | + var generatedFileName = projectable.ClassTypeParameterList is not null ? $"{generatedClassName}-{projectable.ClassTypeParameterList.ChildNodes().Count()}.g.cs" : $"{generatedClassName}.g.cs"; |
114 | 68 |
|
115 | | - var classSyntax = ClassDeclaration(generatedClassName) |
116 | | - .WithModifiers(TokenList(Token(SyntaxKind.StaticKeyword))) |
117 | | - .WithTypeParameterList(projectable.ClassTypeParameterList) |
118 | | - .WithConstraintClauses(projectable.ClassConstraintClauses ?? List<TypeParameterConstraintClauseSyntax>()) |
119 | | - .AddAttributeLists( |
120 | | - AttributeList() |
121 | | - .AddAttributes(_editorBrowsableAttribute) |
122 | | - ) |
123 | | - .AddMembers( |
124 | | - MethodDeclaration( |
125 | | - GenericName( |
126 | | - Identifier("global::System.Linq.Expressions.Expression"), |
127 | | - TypeArgumentList( |
128 | | - SingletonSeparatedList( |
129 | | - (TypeSyntax)GenericName( |
130 | | - Identifier("global::System.Func"), |
131 | | - GetLambdaTypeArgumentListSyntax(projectable) |
132 | | - ) |
| 69 | + var classSyntax = ClassDeclaration(generatedClassName) |
| 70 | + .WithModifiers(TokenList(Token(SyntaxKind.StaticKeyword))) |
| 71 | + .WithTypeParameterList(projectable.ClassTypeParameterList) |
| 72 | + .WithConstraintClauses(projectable.ClassConstraintClauses ?? List<TypeParameterConstraintClauseSyntax>()) |
| 73 | + .AddAttributeLists( |
| 74 | + AttributeList() |
| 75 | + .AddAttributes(_editorBrowsableAttribute) |
| 76 | + ) |
| 77 | + .AddMembers( |
| 78 | + MethodDeclaration( |
| 79 | + GenericName( |
| 80 | + Identifier("global::System.Linq.Expressions.Expression"), |
| 81 | + TypeArgumentList( |
| 82 | + SingletonSeparatedList( |
| 83 | + (TypeSyntax)GenericName( |
| 84 | + Identifier("global::System.Func"), |
| 85 | + GetLambdaTypeArgumentListSyntax(projectable) |
133 | 86 | ) |
134 | 87 | ) |
135 | | - ), |
136 | | - "Expression" |
137 | | - ) |
138 | | - .WithModifiers(TokenList(Token(SyntaxKind.StaticKeyword))) |
139 | | - .WithTypeParameterList(projectable.TypeParameterList) |
140 | | - .WithConstraintClauses(projectable.ConstraintClauses ?? List<TypeParameterConstraintClauseSyntax>()) |
141 | | - .WithBody( |
142 | | - Block( |
143 | | - ReturnStatement( |
144 | | - ParenthesizedLambdaExpression( |
145 | | - projectable.ParametersList ?? ParameterList(), |
146 | | - null, |
147 | | - projectable.ExpressionBody |
148 | | - ) |
| 88 | + ) |
| 89 | + ), |
| 90 | + "Expression" |
| 91 | + ) |
| 92 | + .WithModifiers(TokenList(Token(SyntaxKind.StaticKeyword))) |
| 93 | + .WithTypeParameterList(projectable.TypeParameterList) |
| 94 | + .WithConstraintClauses(projectable.ConstraintClauses ?? List<TypeParameterConstraintClauseSyntax>()) |
| 95 | + .WithBody( |
| 96 | + Block( |
| 97 | + ReturnStatement( |
| 98 | + ParenthesizedLambdaExpression( |
| 99 | + projectable.ParametersList ?? ParameterList(), |
| 100 | + null, |
| 101 | + projectable.ExpressionBody |
149 | 102 | ) |
150 | 103 | ) |
151 | | - ) |
152 | | - ); |
| 104 | + ) |
| 105 | + ) |
| 106 | + ); |
153 | 107 |
|
154 | 108 | #nullable disable |
155 | 109 |
|
156 | | - var compilationUnit = CompilationUnit(); |
| 110 | + var compilationUnit = CompilationUnit(); |
157 | 111 |
|
158 | | - foreach (var usingDirective in projectable.UsingDirectives) |
159 | | - { |
160 | | - compilationUnit = compilationUnit.AddUsings(usingDirective); |
161 | | - } |
| 112 | + foreach (var usingDirective in projectable.UsingDirectives) |
| 113 | + { |
| 114 | + compilationUnit = compilationUnit.AddUsings(usingDirective); |
| 115 | + } |
162 | 116 |
|
163 | | - if (projectable.ClassNamespace is not null) |
164 | | - { |
165 | | - compilationUnit = compilationUnit.AddUsings( |
166 | | - UsingDirective( |
167 | | - ParseName(projectable.ClassNamespace) |
168 | | - ) |
169 | | - ); |
170 | | - } |
| 117 | + if (projectable.ClassNamespace is not null) |
| 118 | + { |
| 119 | + compilationUnit = compilationUnit.AddUsings( |
| 120 | + UsingDirective( |
| 121 | + ParseName(projectable.ClassNamespace) |
| 122 | + ) |
| 123 | + ); |
| 124 | + } |
171 | 125 |
|
172 | | - compilationUnit = compilationUnit |
173 | | - .AddMembers( |
174 | | - NamespaceDeclaration( |
175 | | - ParseName("EntityFrameworkCore.Projectables.Generated") |
176 | | - ).AddMembers(classSyntax) |
| 126 | + compilationUnit = compilationUnit |
| 127 | + .AddMembers( |
| 128 | + NamespaceDeclaration( |
| 129 | + ParseName("EntityFrameworkCore.Projectables.Generated") |
| 130 | + ).AddMembers(classSyntax) |
| 131 | + ) |
| 132 | + .WithLeadingTrivia( |
| 133 | + TriviaList( |
| 134 | + Comment("// <auto-generated/>"), |
| 135 | + Trivia(NullableDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)) |
177 | 136 | ) |
178 | | - .WithLeadingTrivia( |
179 | | - TriviaList( |
180 | | - Comment("// <auto-generated/>"), |
181 | | - Trivia(NullableDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)) |
182 | | - ) |
183 | | - ); |
| 137 | + ); |
184 | 138 |
|
185 | 139 |
|
186 | | - context.AddSource(generatedFileName, SourceText.From(compilationUnit.NormalizeWhitespace().ToFullString(), Encoding.UTF8)); |
| 140 | + context.AddSource(generatedFileName, SourceText.From(compilationUnit.NormalizeWhitespace().ToFullString(), Encoding.UTF8)); |
187 | 141 |
|
| 142 | + static TypeArgumentListSyntax GetLambdaTypeArgumentListSyntax(ProjectableDescriptor projectable) |
| 143 | + { |
| 144 | + var lambdaTypeArguments = TypeArgumentList( |
| 145 | + SeparatedList( |
| 146 | + // TODO: Document where clause |
| 147 | + projectable.ParametersList?.Parameters.Where(p => p.Type is not null).Select(p => p.Type!) |
| 148 | + ) |
| 149 | + ); |
188 | 150 |
|
189 | | - static TypeArgumentListSyntax GetLambdaTypeArgumentListSyntax(ProjectableDescriptor projectable) |
| 151 | + if (projectable.ReturnTypeName is not null) |
190 | 152 | { |
191 | | - var lambdaTypeArguments = TypeArgumentList( |
192 | | - SeparatedList( |
193 | | - // TODO: Document where clause |
194 | | - projectable.ParametersList?.Parameters.Where(p => p.Type is not null).Select(p => p.Type!) |
195 | | - ) |
196 | | - ); |
197 | | - |
198 | | - if (projectable.ReturnTypeName is not null) |
199 | | - { |
200 | | - lambdaTypeArguments = lambdaTypeArguments.AddArguments(ParseTypeName(projectable.ReturnTypeName)); |
201 | | - } |
202 | | - |
203 | | - return lambdaTypeArguments; |
| 153 | + lambdaTypeArguments = lambdaTypeArguments.AddArguments(ParseTypeName(projectable.ReturnTypeName)); |
204 | 154 | } |
| 155 | + |
| 156 | + return lambdaTypeArguments; |
205 | 157 | } |
206 | 158 | } |
207 | 159 | } |
|
0 commit comments