8
8
using System . Collections . Immutable ;
9
9
using System . Diagnostics ;
10
10
using System . Linq ;
11
+ using System . Security . Cryptography . X509Certificates ;
11
12
using System . Text ;
12
13
using System . Threading ;
13
14
using System . Threading . Tasks ;
15
+ using static Microsoft . CodeAnalysis . CSharp . SyntaxFactory ;
14
16
15
17
namespace EntityFrameworkCore . Projectables . Generator
16
18
{
@@ -19,6 +21,22 @@ public class ProjectionExpressionGenerator : IIncrementalGenerator
19
21
{
20
22
private const string ProjectablesAttributeName = "EntityFrameworkCore.Projectables.ProjectableAttribute" ;
21
23
24
+ static readonly AttributeSyntax _editorBrowsableAttribute =
25
+ Attribute (
26
+ ParseName ( "global::System.ComponentModel.EditorBrowsable" ) ,
27
+ AttributeArgumentList (
28
+ SingletonSeparatedList (
29
+ AttributeArgument (
30
+ MemberAccessExpression (
31
+ SyntaxKind . SimpleMemberAccessExpression ,
32
+ IdentifierName ( "global::System.ComponentModel.EditorBrowsableState" ) ,
33
+ IdentifierName ( "Never" )
34
+ )
35
+ )
36
+ )
37
+ )
38
+ ) ;
39
+
22
40
public void Initialize ( IncrementalGeneratorInitializationContext context )
23
41
{
24
42
// Do a simple filter for members
@@ -91,76 +109,84 @@ static void Execute(Compilation compilation, ImmutableArray<MemberDeclarationSyn
91
109
throw new InvalidOperationException ( "Expected a memberName here" ) ;
92
110
}
93
111
94
- resultBuilder . Clear ( ) ;
95
-
96
- resultBuilder . AppendLine ( "// <auto-generated/>" ) ;
97
-
98
- if ( projectable . UsingDirectives is not null )
99
- {
100
- foreach ( var usingDirective in projectable . UsingDirectives . Distinct ( ) )
101
- {
102
- resultBuilder . AppendLine ( usingDirective ) ;
103
- }
104
- }
105
-
106
- if ( projectable . TargetClassNamespace is not null )
107
- {
108
- var targetClassUsingDirective = $ "using { projectable . TargetClassNamespace } ;";
109
-
110
- if ( ! projectable . UsingDirectives . Contains ( targetClassUsingDirective ) )
111
- {
112
- resultBuilder . AppendLine ( targetClassUsingDirective ) ;
113
- }
114
- }
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";
114
+
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
+ )
133
+ )
134
+ )
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
+ )
149
+ )
150
+ )
151
+ )
152
+ ) ;
115
153
116
- if ( projectable . ClassNamespace is not null && projectable . ClassNamespace != projectable . TargetClassNamespace )
117
- {
118
- var classUsingDirective = $ "using { projectable . ClassNamespace } ;";
154
+ #nullable disable
119
155
120
- if ( ! projectable . UsingDirectives . Contains ( classUsingDirective ) )
121
- {
122
- resultBuilder . AppendLine ( classUsingDirective ) ;
123
- }
124
- }
156
+ var compilationUnit = CompilationUnit ( )
157
+ . AddMembers (
158
+ NamespaceDeclaration (
159
+ ParseName ( "EntityFrameworkCore.Projectables.Generated" )
160
+ ) . AddMembers ( classSyntax )
161
+ )
162
+ . WithLeadingTrivia (
163
+ TriviaList (
164
+ Comment ( "// <auto-generated/>" ) ,
165
+ Trivia ( NullableDirectiveTrivia ( Token ( SyntaxKind . DisableKeyword ) , true ) )
166
+ )
167
+ ) ;
125
168
126
- var generatedClassName = ProjectionExpressionClassNameGenerator . GenerateName ( projectable . ClassNamespace , projectable . NestedInClassNames , projectable . MemberName ) ;
127
169
128
- var lambdaTypeArguments = SyntaxFactory . TypeArgumentList (
129
- SyntaxFactory . SeparatedList (
130
- projectable . ParametersList ? . Parameters . Where ( p => p . Type is not null ) . Select ( p => p . Type ! )
131
- )
132
- ) ;
170
+ context . AddSource ( generatedFileName , SourceText . From ( compilationUnit . NormalizeWhitespace ( ) . ToFullString ( ) , Encoding . UTF8 ) ) ;
133
171
134
- resultBuilder . Append ( $@ "
135
- namespace EntityFrameworkCore.Projectables.Generated
136
- #nullable disable
137
- {{
138
- [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
139
- public static class { generatedClassName }
140
- {{
141
- public static System.Linq.Expressions.Expression<System.Func<{ ( lambdaTypeArguments . Arguments . Any ( ) ? $ "{ lambdaTypeArguments . Arguments } , " : "" ) } { projectable . ReturnTypeName } >> Expression{ ( projectable . TypeParameterList ? . Parameters . Any ( ) == true ? projectable . TypeParameterList . ToString ( ) : string . Empty ) } ()" ) ;
142
172
143
- if ( projectable . ConstraintClauses is not null )
173
+ static TypeArgumentListSyntax GetLambdaTypeArgumentListSyntax ( ProjectableDescriptor projectable )
144
174
{
145
- foreach ( var constraintClause in projectable . ConstraintClauses )
175
+ var lambdaTypeArguments = TypeArgumentList (
176
+ SeparatedList (
177
+ // TODO: Document where clause
178
+ projectable . ParametersList ? . Parameters . Where ( p => p . Type is not null ) . Select ( p => p . Type ! )
179
+ )
180
+ ) ;
181
+
182
+ if ( projectable . ReturnTypeName is not null )
146
183
{
147
- resultBuilder . Append ( $@ "
148
- { constraintClause } " ) ;
184
+ lambdaTypeArguments = lambdaTypeArguments . AddArguments ( ParseTypeName ( projectable . ReturnTypeName ) ) ;
149
185
}
150
- }
151
-
152
- resultBuilder . Append ( $@ "
153
- {{
154
- return { projectable . ParametersList } =>
155
- { projectable . Body } ;
156
- }}
157
- }}
158
- }}" ) ;
159
186
160
-
161
- context . AddSource ( $ " { generatedClassName } .g.cs" , SourceText . From ( resultBuilder . ToString ( ) , Encoding . UTF8 ) ) ;
187
+ return lambdaTypeArguments ;
188
+ }
162
189
}
163
190
}
164
-
165
191
}
166
192
}
0 commit comments