Skip to content

Commit b30c757

Browse files
authored
Merge pull request #62 from EFNext/fix/extension-members
Fix C# 14 extension members in [Expressive] generation
2 parents dc01522 + 1487f67 commit b30c757

14 files changed

Lines changed: 244 additions & 62 deletions

src/ExpressiveSharp.Generator/Interpretation/ExpressiveInterpreter.BodyProcessors.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ private static EmitResult EmitExpressionTreeForProperty(
272272
{
273273
var thisParam = descriptor.ParametersList.Parameters[0];
274274
var thisTypeFqn = thisParam.Type?.ToString() ?? "object";
275-
emitterParams.Add(new EmitterParameter("@this", thisTypeFqn, isThis: true));
275+
emitterParams.Add(new EmitterParameter(
276+
"@this", thisTypeFqn, symbol: descriptor.ExtensionParameterSymbol, isThis: true));
276277
}
277278

278279
var allTypeArgs = emitterParams.Select(p => p.TypeFqn).ToList();
@@ -295,7 +296,8 @@ private static List<EmitterParameter> BuildEmitterParameters(
295296
{
296297
var thisParam = descriptor.ParametersList.Parameters[0];
297298
var thisTypeFqn = thisParam.Type?.ToString() ?? "object";
298-
result.Add(new EmitterParameter("@this", thisTypeFqn, isThis: true));
299+
result.Add(new EmitterParameter(
300+
"@this", thisTypeFqn, symbol: descriptor.ExtensionParameterSymbol, isThis: true));
299301
}
300302

301303
foreach (var param in methodSymbol.Parameters)

src/ExpressiveSharp.Generator/Interpretation/ExpressiveInterpreter.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ static internal partial class ExpressiveInterpreter
3434
var descriptor = BuildBaseDescriptor(
3535
member, memberSymbol, methodSymbol,
3636
isExtensionMember, extensionParameter, extensionReceiverType);
37+
descriptor.ExtensionParameterSymbol = extensionParameter;
3738

3839
foreach (var typeName in expressiveAttribute.TransformerTypeNames)
3940
descriptor.DeclaredTransformerTypeNames.Add(typeName);
@@ -94,6 +95,12 @@ private static ExpressiveDescriptor BuildBaseDescriptor(
9495
ParametersList = SyntaxFactory.ParameterList()
9596
};
9697

98+
// A `static` member inside an `extension(T) { }` block has no implicit receiver —
99+
// treat it like an ordinary static method on the containing static class.
100+
var isInstanceExtensionMember = isExtensionMember
101+
&& extensionReceiverType is not null
102+
&& !member.Modifiers.Any(SyntaxKind.StaticKeyword);
103+
97104
if (methodSymbol is not null)
98105
{
99106
var parameterTypeNames = methodSymbol.Parameters
@@ -102,10 +109,10 @@ private static ExpressiveDescriptor BuildBaseDescriptor(
102109

103110
// Prepend the extension receiver type to match how the runtime sees the method
104111
// (receiver is the first implicit parameter).
105-
if (isExtensionMember && extensionReceiverType is not null)
112+
if (isInstanceExtensionMember)
106113
{
107114
parameterTypeNames.Insert(0,
108-
extensionReceiverType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
115+
extensionReceiverType!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
109116
}
110117

111118
descriptor.ParameterTypeNames = parameterTypeNames;
@@ -116,12 +123,12 @@ private static ExpressiveDescriptor BuildBaseDescriptor(
116123
SetupGenericTypeParameters(descriptor, classForNaming);
117124
}
118125

119-
if (isExtensionMember && extensionReceiverType is not null)
126+
if (isInstanceExtensionMember)
120127
{
121128
descriptor.ParametersList = descriptor.ParametersList.AddParameters(
122129
SyntaxFactory.Parameter(SyntaxFactory.Identifier("@this"))
123130
.WithType(SyntaxFactory.ParseTypeName(
124-
extensionReceiverType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))));
131+
extensionReceiverType!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))));
125132
}
126133
else if (!member.Modifiers.Any(SyntaxKind.StaticKeyword) && member is not ConstructorDeclarationSyntax)
127134
{
@@ -132,9 +139,9 @@ private static ExpressiveDescriptor BuildBaseDescriptor(
132139
}
133140

134141
// Target class info is used by the registry to associate the projection.
135-
if (isExtensionMember && extensionReceiverType is not null)
142+
if (isInstanceExtensionMember)
136143
{
137-
descriptor.TargetClassNamespace = extensionReceiverType.ContainingNamespace.IsGlobalNamespace
144+
descriptor.TargetClassNamespace = extensionReceiverType!.ContainingNamespace.IsGlobalNamespace
138145
? null
139146
: extensionReceiverType.ContainingNamespace.ToDisplayString();
140147
descriptor.TargetNestedInClassNames = GetNestedInClassPath(extensionReceiverType);

src/ExpressiveSharp.Generator/Models/ExpressiveDescriptor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ internal class ExpressiveDescriptor
2929
public ParameterListSyntax? ParametersList { get; set; }
3030

3131
public IEnumerable<string>? ParameterTypeNames { get; set; }
32+
33+
public IParameterSymbol? ExtensionParameterSymbol { get; set; }
3234

3335
public TypeParameterListSyntax? TypeParameterList { get; set; }
3436

tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/ExtensionMemberTests.ExtensionMemberMethod.verified.txt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ namespace ExpressiveSharp.Generated
1212
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Entity, int>> TripleId_P0_Foo_Entity_Expression()
1313
{
1414
var p__this = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "@this");
15-
var expr_2 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "e"); // e
16-
var expr_1 = global::System.Linq.Expressions.Expression.Property(expr_2, typeof(global::Foo.Entity).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
17-
var expr_3 = global::System.Linq.Expressions.Expression.Constant(3, typeof(int)); // 3
18-
var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_1, expr_3);
15+
var expr_1 = global::System.Linq.Expressions.Expression.Property(p__this, typeof(global::Foo.Entity).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Id
16+
var expr_2 = global::System.Linq.Expressions.Expression.Constant(3, typeof(int)); // 3
17+
var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_1, expr_2);
1918
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::Foo.Entity, int>>(expr_0, p__this);
2019
}
2120
}

tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/ExtensionMemberTests.ExtensionMemberMethodWithParameters.verified.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ namespace ExpressiveSharp.Generated
1313
{
1414
var p__this = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "@this");
1515
var p_factor = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "factor");
16-
var expr_2 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "e"); // e
17-
var expr_1 = global::System.Linq.Expressions.Expression.Property(expr_2, typeof(global::Foo.Entity).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
16+
var expr_1 = global::System.Linq.Expressions.Expression.Property(p__this, typeof(global::Foo.Entity).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Id
1817
var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_1, p_factor);
1918
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::Foo.Entity, int, int>>(expr_0, p__this, p_factor);
2019
}

tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/ExtensionMemberTests.ExtensionMemberOnInterface.verified.txt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ namespace ExpressiveSharp.Generated
1212
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.IEntity, string>> Label_Expression()
1313
{
1414
var p__this = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.IEntity), "@this");
15-
var expr_4 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.IEntity), "e"); // e
16-
var expr_3 = global::System.Linq.Expressions.Expression.Property(expr_4, typeof(global::Foo.IEntity).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
15+
var expr_3 = global::System.Linq.Expressions.Expression.Property(p__this, typeof(global::Foo.IEntity).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Id
1716
var expr_2 = global::System.Linq.Expressions.Expression.Convert(expr_3, typeof(object));
18-
var expr_5 = global::System.Linq.Expressions.Expression.Constant(": ", typeof(string)); // ": "
19-
var expr_1 = global::System.Linq.Expressions.Expression.Call(typeof(string).GetMethod("Concat", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(object), typeof(object) }, null), expr_2, expr_5);
20-
var expr_6 = global::System.Linq.Expressions.Expression.Property(expr_4, typeof(global::Foo.IEntity).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Name
21-
var expr_0 = global::System.Linq.Expressions.Expression.Call(typeof(string).GetMethod("Concat", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(string), typeof(string) }, null), expr_1, expr_6);
17+
var expr_4 = global::System.Linq.Expressions.Expression.Constant(": ", typeof(string)); // ": "
18+
var expr_1 = global::System.Linq.Expressions.Expression.Call(typeof(string).GetMethod("Concat", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(object), typeof(object) }, null), expr_2, expr_4);
19+
var expr_5 = global::System.Linq.Expressions.Expression.Property(p__this, typeof(global::Foo.IEntity).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Name
20+
var expr_0 = global::System.Linq.Expressions.Expression.Call(typeof(string).GetMethod("Concat", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(string), typeof(string) }, null), expr_1, expr_5);
2221
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::Foo.IEntity, string>>(expr_0, p__this);
2322
}
2423
}

tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/ExtensionMemberTests.ExtensionMemberOnPrimitive.verified.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ namespace ExpressiveSharp.Generated
1111
static global::System.Linq.Expressions.Expression<global::System.Func<int, int>> Squared_Expression()
1212
{
1313
var p__this = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "@this");
14-
var expr_1 = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "i"); // i
15-
var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_1, expr_1);
14+
var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, p__this, p__this); // i * i
1615
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<int, int>>(expr_0, p__this);
1716
}
1817
}

tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/ExtensionMemberTests.ExtensionMemberProperty.verified.txt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ namespace ExpressiveSharp.Generated
1212
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Entity, int>> DoubleId_Expression()
1313
{
1414
var p__this = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "@this");
15-
var expr_2 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "e"); // e
16-
var expr_1 = global::System.Linq.Expressions.Expression.Property(expr_2, typeof(global::Foo.Entity).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
17-
var expr_3 = global::System.Linq.Expressions.Expression.Constant(2, typeof(int)); // 2
18-
var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_1, expr_3);
15+
var expr_1 = global::System.Linq.Expressions.Expression.Property(p__this, typeof(global::Foo.Entity).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Id
16+
var expr_2 = global::System.Linq.Expressions.Expression.Constant(2, typeof(int)); // 2
17+
var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_1, expr_2);
1918
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::Foo.Entity, int>>(expr_0, p__this);
2019
}
2120
}

tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/ExtensionMemberTests.ExtensionMemberWithBlockBody.verified.txt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@ namespace ExpressiveSharp.Generated
2020
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Entity, string>> GetStatus_P0_Foo_Entity_Expression()
2121
{
2222
var p__this = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "@this");
23-
var expr_3 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "e"); // e
24-
var expr_2 = global::System.Linq.Expressions.Expression.Property(expr_3, typeof(global::Foo.Entity).GetProperty("IsActive", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
25-
var expr_5 = global::System.Linq.Expressions.Expression.Property(expr_3, typeof(global::Foo.Entity).GetProperty("Value", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Value
26-
var expr_6 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
27-
var expr_4 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_5, expr_6);
28-
var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.AndAlso, expr_2, expr_4);
29-
var expr_7 = global::System.Linq.Expressions.Expression.Constant("Active", typeof(string)); // "Active"
30-
var expr_8 = global::System.Linq.Expressions.Expression.Constant("Inactive", typeof(string)); // "Inactive"
31-
var expr_0 = global::System.Linq.Expressions.Expression.Condition(expr_1, expr_7, expr_8, typeof(string));
23+
var expr_2 = global::System.Linq.Expressions.Expression.Property(p__this, typeof(global::Foo.Entity).GetProperty("IsActive", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.IsActive
24+
var expr_4 = global::System.Linq.Expressions.Expression.Property(p__this, typeof(global::Foo.Entity).GetProperty("Value", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Value
25+
var expr_5 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
26+
var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_4, expr_5);
27+
var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.AndAlso, expr_2, expr_3);
28+
var expr_6 = global::System.Linq.Expressions.Expression.Constant("Active", typeof(string)); // "Active"
29+
var expr_7 = global::System.Linq.Expressions.Expression.Constant("Inactive", typeof(string)); // "Inactive"
30+
var expr_0 = global::System.Linq.Expressions.Expression.Condition(expr_1, expr_6, expr_7, typeof(string));
3231
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::Foo.Entity, string>>(expr_0, p__this);
3332
}
3433
}

tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/ExtensionMemberTests.ExtensionMemberWithIsPatternExpression.verified.txt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ namespace ExpressiveSharp.Generated
1212
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Entity, bool>> IsHighValue_Expression()
1313
{
1414
var p__this = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "@this");
15-
var expr_1 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::Foo.Entity), "e"); // e
16-
var expr_0 = global::System.Linq.Expressions.Expression.Property(expr_1, typeof(global::Foo.Entity).GetProperty("Value", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
17-
var expr_3 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
18-
var expr_2 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_0, expr_3);
19-
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::Foo.Entity, bool>>(expr_2, p__this);
15+
var expr_0 = global::System.Linq.Expressions.Expression.Property(p__this, typeof(global::Foo.Entity).GetProperty("Value", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // e.Value
16+
var expr_2 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
17+
var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_0, expr_2);
18+
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::Foo.Entity, bool>>(expr_1, p__this);
2019
}
2120
}
2221
}

0 commit comments

Comments
 (0)