Skip to content

Commit a52326c

Browse files
author
Rhodon
committed
Merge from master
2 parents aab4834 + d6b1cfb commit a52326c

File tree

74 files changed

+705
-464
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+705
-464
lines changed

src/EntityFrameworkCore.Projectables.Generator/DeclarationSyntaxRewriter.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public DeclarationSyntaxRewriter(SemanticModel semanticModel)
2828
visitedNode = visitedParameterSyntax.WithModifiers(node.Modifiers.RemoveAt(thisKeywordIndex));
2929
}
3030

31-
// Strip the parans keyword of any parameter
31+
// Strip the params keyword of any parameter
3232
var paramsKeywordIndex = ((ParameterSyntax)visitedNode).Modifiers.IndexOf(SyntaxKind.ParamsKeyword);
3333
if (paramsKeywordIndex != -1)
3434
{
@@ -73,6 +73,19 @@ public DeclarationSyntaxRewriter(SemanticModel semanticModel)
7373
return base.VisitIdentifierName(node);
7474
}
7575

76+
public override SyntaxNode? VisitGenericName(GenericNameSyntax node)
77+
{
78+
var typeInfo = _semanticModel.GetTypeInfo(node);
79+
if (typeInfo.Type is not null)
80+
{
81+
return SyntaxFactory.ParseTypeName(
82+
typeInfo.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
83+
).WithTriviaFrom(node);
84+
}
85+
86+
return base.VisitGenericName(node);
87+
}
88+
7689
public override SyntaxNode? VisitQualifiedName(QualifiedNameSyntax node)
7790
{
7891
var typeInfo = _semanticModel.GetTypeInfo(node);

src/EntityFrameworkCore.Projectables.Generator/EntityFrameworkCore.Projectables.Generator.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netstandard2.0</TargetFramework>
4+
<TargetFrameworks>netstandard2.0</TargetFrameworks>
55
<NoWarn>$(NoWarn);NU5128</NoWarn>
66
<IsPackable>false</IsPackable>
77
</PropertyGroup>

src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,9 @@ public ExpressionSyntaxRewriter(INamedTypeSymbol targetTypeSymbol, NullCondition
214214
// if this node refers to a named type which is not yet fully qualified, we want to fully qualify it
215215
if (symbol.Kind is SymbolKind.NamedType && node.Parent?.Kind() is not SyntaxKind.QualifiedName)
216216
{
217-
var typeInfo = _semanticModel.GetTypeInfo(node);
218-
219-
if (typeInfo.Type is not null)
220-
{
221-
return SyntaxFactory.ParseTypeName(
222-
typeInfo.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
223-
).WithLeadingTrivia(node.GetLeadingTrivia());
224-
}
217+
return SyntaxFactory.ParseTypeName(
218+
symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
219+
).WithLeadingTrivia(node.GetLeadingTrivia());
225220
}
226221
}
227222

src/EntityFrameworkCore.Projectables.Generator/ProjectableDescriptor.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace EntityFrameworkCore.Projectables.Generator
1111
{
1212
public class ProjectableDescriptor
1313
{
14-
public IEnumerable<string>? UsingDirectives { get; set; }
14+
public IEnumerable<UsingDirectiveSyntax>? UsingDirectives { get; set; }
1515

1616
public string? ClassNamespace { get; set; }
1717

@@ -23,6 +23,10 @@ public class ProjectableDescriptor
2323

2424
public string? ClassName { get; set; }
2525

26+
public TypeParameterListSyntax? ClassTypeParameterList { get; set; }
27+
28+
public SyntaxList<TypeParameterConstraintClauseSyntax>? ClassConstraintClauses { get; set; }
29+
2630
public string? MemberName { get; set; }
2731

2832
public string? ReturnTypeName { get; set; }
@@ -31,8 +35,8 @@ public class ProjectableDescriptor
3135

3236
public TypeParameterListSyntax? TypeParameterList { get; set; }
3337

34-
public IEnumerable<TypeParameterConstraintClauseSyntax>? ConstraintClauses { get; set; }
38+
public SyntaxList<TypeParameterConstraintClauseSyntax>? ConstraintClauses { get; set; }
3539

36-
public SyntaxNode? Body { get; set; }
40+
public ExpressionSyntax? ExpressionBody { get; set; }
3741
}
3842
}

src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -119,27 +119,58 @@ x is IPropertySymbol xProperty &&
119119
var declarationSyntaxRewriter = new DeclarationSyntaxRewriter(semanticModel);
120120

121121
var descriptor = new ProjectableDescriptor {
122+
123+
UsingDirectives = member.SyntaxTree.GetRoot().DescendantNodes().OfType<UsingDirectiveSyntax>(),
122124
ClassName = memberSymbol.ContainingType.Name,
123125
ClassNamespace = memberSymbol.ContainingType.ContainingNamespace.IsGlobalNamespace ? null : memberSymbol.ContainingType.ContainingNamespace.ToDisplayString(),
124126
MemberName = memberSymbol.Name,
125127
NestedInClassNames = GetNestedInClassPath(memberSymbol.ContainingType),
126-
ParametersList = SyntaxFactory.ParameterList(),
127-
TypeParameterList = SyntaxFactory.TypeParameterList()
128+
ParametersList = SyntaxFactory.ParameterList()
128129
};
129130

131+
if (memberSymbol.ContainingType is INamedTypeSymbol { IsGenericType: true } containingNamedType)
132+
{
133+
descriptor.ClassTypeParameterList = SyntaxFactory.TypeParameterList();
134+
135+
foreach (var additionalClassTypeParameter in containingNamedType.TypeParameters)
136+
{
137+
descriptor.ClassTypeParameterList = descriptor.ClassTypeParameterList.AddParameters(
138+
SyntaxFactory.TypeParameter(additionalClassTypeParameter.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
139+
);
140+
141+
if (!additionalClassTypeParameter.ConstraintTypes.IsDefaultOrEmpty)
142+
{
143+
descriptor.ClassConstraintClauses ??= SyntaxFactory.List<TypeParameterConstraintClauseSyntax>();
144+
145+
descriptor.ClassConstraintClauses = descriptor.ClassConstraintClauses.Value.Add(
146+
SyntaxFactory.TypeParameterConstraintClause(
147+
SyntaxFactory.IdentifierName(additionalClassTypeParameter.Name),
148+
SyntaxFactory.SeparatedList<TypeParameterConstraintSyntax>(
149+
additionalClassTypeParameter
150+
.ConstraintTypes
151+
.Select(c => SyntaxFactory.TypeConstraint(
152+
SyntaxFactory.IdentifierName(c.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
153+
))
154+
)
155+
)
156+
);
157+
}
158+
159+
// todo: add additional type constraints
160+
}
161+
}
162+
130163
if (!member.Modifiers.Any(SyntaxKind.StaticKeyword))
131164
{
132165
descriptor.ParametersList = descriptor.ParametersList.AddParameters(
133166
SyntaxFactory.Parameter(
134-
SyntaxFactory.Identifier("@this")
135-
).WithType(
136-
SyntaxFactory.ParseTypeName(
137-
memberSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
138-
)
139-
.WithTrailingTrivia(
140-
SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")
141-
)
167+
SyntaxFactory.Identifier("@this")
168+
)
169+
.WithType(
170+
SyntaxFactory.ParseTypeName(
171+
memberSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
142172
)
173+
)
143174
);
144175
}
145176

@@ -169,14 +200,15 @@ x is IPropertySymbol xProperty &&
169200
var returnType = declarationSyntaxRewriter.Visit(methodDeclarationSyntax.ReturnType);
170201

171202
descriptor.ReturnTypeName = returnType.ToString();
172-
descriptor.Body = expressionSyntaxRewriter.Visit(methodDeclarationSyntax.ExpressionBody.Expression);
203+
descriptor.ExpressionBody = (ExpressionSyntax)expressionSyntaxRewriter.Visit(methodDeclarationSyntax.ExpressionBody.Expression);
173204
foreach (var additionalParameter in ((ParameterListSyntax)declarationSyntaxRewriter.Visit(methodDeclarationSyntax.ParameterList)).Parameters)
174205
{
175206
descriptor.ParametersList = descriptor.ParametersList.AddParameters(additionalParameter);
176207
}
177208

178209
if (methodDeclarationSyntax.TypeParameterList is not null)
179210
{
211+
descriptor.TypeParameterList = SyntaxFactory.TypeParameterList();
180212
foreach (var additionalTypeParameter in ((TypeParameterListSyntax)declarationSyntaxRewriter.Visit(methodDeclarationSyntax.TypeParameterList)).Parameters)
181213
{
182214
descriptor.TypeParameterList = descriptor.TypeParameterList.AddParameters(additionalTypeParameter);
@@ -185,8 +217,11 @@ x is IPropertySymbol xProperty &&
185217

186218
if (methodDeclarationSyntax.ConstraintClauses.Any())
187219
{
188-
descriptor.ConstraintClauses = methodDeclarationSyntax.ConstraintClauses
189-
.Select(x => (TypeParameterConstraintClauseSyntax)declarationSyntaxRewriter.Visit(x));
220+
descriptor.ConstraintClauses = SyntaxFactory.List(
221+
methodDeclarationSyntax
222+
.ConstraintClauses
223+
.Select(x => (TypeParameterConstraintClauseSyntax)declarationSyntaxRewriter.Visit(x))
224+
);
190225
}
191226
}
192227
else if (memberBody is PropertyDeclarationSyntax propertyDeclarationSyntax)
@@ -201,20 +236,13 @@ x is IPropertySymbol xProperty &&
201236
var returnType = declarationSyntaxRewriter.Visit(propertyDeclarationSyntax.Type);
202237

203238
descriptor.ReturnTypeName = returnType.ToString();
204-
descriptor.Body = expressionSyntaxRewriter.Visit(propertyDeclarationSyntax.ExpressionBody.Expression);
239+
descriptor.ExpressionBody = (ExpressionSyntax)expressionSyntaxRewriter.Visit(propertyDeclarationSyntax.ExpressionBody.Expression);
205240
}
206241
else
207242
{
208243
return null;
209244
}
210245

211-
descriptor.UsingDirectives =
212-
member.SyntaxTree
213-
.GetRoot()
214-
.DescendantNodes()
215-
.OfType<UsingDirectiveSyntax>()
216-
.Select(x => x.ToString());
217-
218246
return descriptor;
219247
}
220248
}

src/EntityFrameworkCore.Projectables.Generator/ProjectionExpressionGenerator.cs

Lines changed: 95 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
using System.Collections.Immutable;
99
using System.Diagnostics;
1010
using System.Linq;
11+
using System.Security.Cryptography.X509Certificates;
1112
using System.Text;
1213
using System.Threading;
1314
using System.Threading.Tasks;
15+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
1416

1517
namespace EntityFrameworkCore.Projectables.Generator
1618
{
@@ -19,6 +21,22 @@ public class ProjectionExpressionGenerator : IIncrementalGenerator
1921
{
2022
private const string ProjectablesAttributeName = "EntityFrameworkCore.Projectables.ProjectableAttribute";
2123

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+
2240
public void Initialize(IncrementalGeneratorInitializationContext context)
2341
{
2442
// Do a simple filter for members
@@ -91,76 +109,100 @@ static void Execute(Compilation compilation, ImmutableArray<MemberDeclarationSyn
91109
throw new InvalidOperationException("Expected a memberName here");
92110
}
93111

94-
resultBuilder.Clear();
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+
);
95153

96-
resultBuilder.AppendLine("// <auto-generated/>");
154+
#nullable disable
97155

98-
if (projectable.UsingDirectives is not null)
99-
{
100-
foreach (var usingDirective in projectable.UsingDirectives.Distinct())
101-
{
102-
resultBuilder.AppendLine(usingDirective);
103-
}
104-
}
156+
var compilationUnit = CompilationUnit();
105157

106-
if (projectable.TargetClassNamespace is not null)
158+
foreach (var usingDirective in projectable.UsingDirectives)
107159
{
108-
var targetClassUsingDirective = $"using {projectable.TargetClassNamespace};";
109-
110-
if (!projectable.UsingDirectives.Contains(targetClassUsingDirective))
111-
{
112-
resultBuilder.AppendLine(targetClassUsingDirective);
113-
}
160+
compilationUnit = compilationUnit.AddUsings(usingDirective);
114161
}
115162

116-
if (projectable.ClassNamespace is not null && projectable.ClassNamespace != projectable.TargetClassNamespace)
163+
if (projectable.ClassNamespace is not null)
117164
{
118-
var classUsingDirective = $"using {projectable.ClassNamespace};";
119-
120-
if (!projectable.UsingDirectives.Contains(classUsingDirective))
121-
{
122-
resultBuilder.AppendLine(classUsingDirective);
123-
}
165+
compilationUnit = compilationUnit.AddUsings(
166+
UsingDirective(
167+
ParseName(projectable.ClassNamespace)
168+
)
169+
);
124170
}
125171

126-
var generatedClassName = ProjectionExpressionClassNameGenerator.GenerateName(projectable.ClassNamespace, projectable.NestedInClassNames, projectable.MemberName);
127-
128-
var lambdaTypeArguments = SyntaxFactory.TypeArgumentList(
129-
SyntaxFactory.SeparatedList(
130-
projectable.ParametersList?.Parameters.Where(p => p.Type is not null).Select(p => p.Type!)
172+
compilationUnit = compilationUnit
173+
.AddMembers(
174+
NamespaceDeclaration(
175+
ParseName("EntityFrameworkCore.Projectables.Generated")
176+
).AddMembers(classSyntax)
131177
)
132-
);
178+
.WithLeadingTrivia(
179+
TriviaList(
180+
Comment("// <auto-generated/>"),
181+
Trivia(NullableDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true))
182+
)
183+
);
133184

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)}()");
142185

143-
if (projectable.ConstraintClauses is not null)
186+
context.AddSource(generatedFileName, SourceText.From(compilationUnit.NormalizeWhitespace().ToFullString(), Encoding.UTF8));
187+
188+
189+
static TypeArgumentListSyntax GetLambdaTypeArgumentListSyntax(ProjectableDescriptor projectable)
144190
{
145-
foreach (var constraintClause in projectable.ConstraintClauses)
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)
146199
{
147-
resultBuilder.Append($@"
148-
{constraintClause}");
200+
lambdaTypeArguments = lambdaTypeArguments.AddArguments(ParseTypeName(projectable.ReturnTypeName));
149201
}
150-
}
151-
152-
resultBuilder.Append($@"
153-
{{
154-
return {projectable.ParametersList} =>
155-
{projectable.Body};
156-
}}
157-
}}
158-
}}");
159202

160-
161-
context.AddSource($"{generatedClassName}.g.cs", SourceText.From(resultBuilder.ToString(), Encoding.UTF8));
203+
return lambdaTypeArguments;
204+
}
162205
}
163206
}
164-
165207
}
166208
}

0 commit comments

Comments
 (0)