Skip to content

Commit 68a4599

Browse files
authored
Merge pull request #208 from EFNext/copilot/fix-source-generator-error
Fix source generator crash when parameter type uses verbatim @ keyword prefix Fixes #207
2 parents a150e7f + 6479be2 commit 68a4599

4 files changed

Lines changed: 78 additions & 0 deletions

File tree

src/EntityFrameworkCore.Projectables/Services/ProjectionExpressionClassNameGenerator.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ private static void AppendSanitizedTypeName(StringBuilder sb, string typeName)
140140
continue;
141141
}
142142

143+
// Skip the verbatim identifier prefix '@' — it is a C# syntactic escape for
144+
// reserved keywords (e.g. '@event') and has no meaning at the CLR level.
145+
// The CLR type name is just 'event', so stripping '@' keeps the generated name
146+
// consistent with the runtime resolver's output.
147+
if (typeName[i] == '@')
148+
{
149+
i++;
150+
continue;
151+
}
152+
143153
var c = typeName[i];
144154
sb.Append(IsInvalidIdentifierChar(c) ? '_' : c);
145155
i++;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// <auto-generated/>
2+
#nullable disable
3+
using System;
4+
using EntityFrameworkCore.Projectables;
5+
using Foo;
6+
7+
namespace EntityFrameworkCore.Projectables.Generated
8+
{
9+
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
10+
static class Foo_EventExtensions_GetId_P0_Foo_event
11+
{
12+
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.@event, int>> Expression()
13+
{
14+
return (global::Foo.@event e) => e.Id;
15+
}
16+
}
17+
}

tests/EntityFrameworkCore.Projectables.Generator.Tests/ExtensionMethodTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,32 @@ static class C {
105105
return Verifier.Verify(result.GeneratedTrees[0].ToString());
106106
}
107107

108+
[Fact]
109+
public Task ProjectableExtensionMethod_WithVerbatimKeywordParameterType()
110+
{
111+
var compilation = CreateCompilation(@"
112+
using System;
113+
using EntityFrameworkCore.Projectables;
114+
namespace Foo {
115+
class @event {
116+
public int Id { get; set; }
117+
}
118+
119+
static class EventExtensions {
120+
[Projectable]
121+
public static int GetId(this @event e) => e.Id;
122+
}
123+
}
124+
");
125+
126+
var result = RunGenerator(compilation);
127+
128+
Assert.Empty(result.Diagnostics);
129+
Assert.Single(result.GeneratedTrees);
130+
131+
return Verifier.Verify(result.GeneratedTrees[0].ToString());
132+
}
133+
108134
[Fact]
109135
public Task ProjectableExtensionMethod_WithExpressionPropertyBody()
110136
{

tests/EntityFrameworkCore.Projectables.Tests/Services/ProjectionExpressionClassNameGeneratorTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,31 @@ public void GenerateName_WithGlobalPrefixInGenericArgs_StripsAllGlobalPrefixes(
5050
Assert.Equal(expected, result);
5151
}
5252

53+
/// <summary>
54+
/// Verifies that the verbatim identifier prefix <c>@</c> is stripped from parameter type
55+
/// names. Roslyn's <c>FullyQualifiedFormat</c> includes it for types whose names are
56+
/// reserved C# keywords (e.g. <c>@event</c>), but the CLR runtime name never includes
57+
/// <c>@</c> — so both sides must agree on the sanitised name.
58+
/// </summary>
59+
[Theory]
60+
[InlineData(
61+
"global::Foo.Storage.@event",
62+
"ns_a_m_P0_Foo_Storage_event")]
63+
[InlineData(
64+
"@event",
65+
"ns_a_m_P0_event")]
66+
[InlineData(
67+
"global::Foo.@delegate",
68+
"ns_a_m_P0_Foo_delegate")]
69+
public void GenerateName_WithVerbatimAtPrefixInParamType_StripsAtSign(
70+
string paramTypeName, string expected)
71+
{
72+
var result = ProjectionExpressionClassNameGenerator.GenerateName(
73+
"ns", new[] { "a" }, "m", new[] { paramTypeName });
74+
75+
Assert.Equal(expected, result);
76+
}
77+
5378
[Fact]
5479
public void GeneratedFullName()
5580
{

0 commit comments

Comments
 (0)