Skip to content

Commit dbb702b

Browse files
author
Julien Couvreur
committed
Extensions: extension methods cannot be entry points
1 parent c3c7ad6 commit dbb702b

File tree

3 files changed

+96
-12
lines changed

3 files changed

+96
-12
lines changed

src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1940,7 +1940,14 @@ internal EntryPoint GetEntryPointAndDiagnostics(CancellationToken cancellationTo
19401940
return scriptClass.GetScriptEntryPoint();
19411941
}
19421942

1943-
var mainTypeOrNamespace = globalNamespace.GetNamespaceOrTypeByQualifiedName(mainTypeName.Split('.')).OfMinimalArity();
1943+
var nameParts = mainTypeName.Split('.');
1944+
if (nameParts.Any(n => string.IsNullOrWhiteSpace(n)))
1945+
{
1946+
diagnostics.Add(ErrorCode.ERR_BadCompilationOptionValue, NoLocation.Singleton, nameof(CSharpCompilationOptions.MainTypeName), mainTypeName);
1947+
return null;
1948+
}
1949+
1950+
var mainTypeOrNamespace = globalNamespace.GetNamespaceOrTypeByQualifiedName(nameParts).OfMinimalArity();
19441951
if (mainTypeOrNamespace is null)
19451952
{
19461953
diagnostics.Add(ErrorCode.ERR_MainClassNotFound, NoLocation.Singleton, mainTypeName);
@@ -2157,6 +2164,21 @@ private static void AddEntryPointCandidates(
21572164
ArrayBuilder<MethodSymbol> entryPointCandidates, IEnumerable<Symbol> members)
21582165
{
21592166
foreach (var member in members)
2167+
{
2168+
if (member.GetIsNewExtensionMember())
2169+
{
2170+
if (member is MethodSymbol method && method.TryGetCorrespondingExtensionImplementationMethod() is { } implementationMethod)
2171+
{
2172+
addIfCandidate(entryPointCandidates, implementationMethod);
2173+
}
2174+
}
2175+
else
2176+
{
2177+
addIfCandidate(entryPointCandidates, member);
2178+
}
2179+
}
2180+
2181+
static void addIfCandidate(ArrayBuilder<MethodSymbol> entryPointCandidates, Symbol member)
21602182
{
21612183
if (member is MethodSymbol method &&
21622184
method.IsEntryPointCandidate)

src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,8 @@ internal bool IsEntryPointCandidate
710710
{
711711
get
712712
{
713+
Debug.Assert(!this.GetIsNewExtensionMember());
714+
713715
if (this.IsPartialDefinition() &&
714716
this.PartialImplementationPart is null)
715717
{

src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35517,16 +35517,36 @@ static class E
3551735517
}
3551835518
""";
3551935519
var comp = CreateCompilation(source, options: TestOptions.DebugExe);
35520-
comp.VerifyEmitDiagnostics();
35521-
// Tracked by https://github.com/dotnet/roslyn/issues/76130
35522-
// FindEntryPoint/NameSymbolSearcher skip "E" since it has not declaration named "Main"
35523-
// CompileAndVerify(comp, expectedOutput: "ran");
35520+
CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics();
3552435521

3552535522
comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("E"));
35526-
comp.VerifyEmitDiagnostics();
3552735523
CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics();
3552835524
}
3552935525

35526+
[Fact]
35527+
public void Validation_EntryPoint_02()
35528+
{
35529+
string source = """
35530+
static class E
35531+
{
35532+
extension(int)
35533+
{
35534+
public static System.Action Main => throw null;
35535+
}
35536+
}
35537+
""";
35538+
var comp = CreateCompilation(source, options: TestOptions.DebugExe);
35539+
comp.VerifyEmitDiagnostics(
35540+
// error CS5001: Program does not contain a static 'Main' method suitable for an entry point
35541+
Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1));
35542+
35543+
comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("E"));
35544+
comp.VerifyDiagnostics(
35545+
// (1,14): error CS1558: 'E' does not have a suitable static 'Main' method
35546+
// static class E
35547+
Diagnostic(ErrorCode.ERR_NoMainInClass, "E").WithArguments("E").WithLocation(1, 14));
35548+
}
35549+
3553035550
[Fact]
3553135551
public void Validation_EntryPoint_03()
3553235552
{
@@ -35535,14 +35555,19 @@ static class E
3553535555
{
3553635556
extension(int i)
3553735557
{
35538-
public static void Main() { System.Console.Write("ran"); }
35558+
public static void Main() => throw null;
3553935559
}
3554035560
}
3554135561
""";
3554235562
var comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("E.<>E__0"));
3554335563
comp.VerifyEmitDiagnostics(
3554435564
// error CS1555: Could not find 'E.<>E__0' specified for Main method
3554535565
Diagnostic(ErrorCode.ERR_MainClassNotFound).WithArguments("E.<>E__0").WithLocation(1, 1));
35566+
35567+
Assert.Equal("<>E__0", comp.GetTypeByMetadataName("E").GetTypeMembers().Single().ExtensionName);
35568+
35569+
// Tracked by https://github.com/dotnet/roslyn/issues/76130 : we should find the unspeakable nested type
35570+
Assert.Null(comp.GetTypeByMetadataName("E+<>E__0"));
3554635571
}
3554735572

3554835573
[Fact]
@@ -35557,13 +35582,48 @@ public static void Main() { }
3555735582
}
3555835583
}
3555935584
""";
35560-
// Tracked by https://github.com/dotnet/roslyn/issues/76130
35561-
// FindEntryPoint should not attempt to find types with an empty name
3556235585
var comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("E."));
3556335586
comp.VerifyEmitDiagnostics(
35564-
// (3,5): error CS1556: 'E.extension(int)' specified for Main method must be a non-generic class, record, struct, or interface
35565-
// extension(int i)
35566-
Diagnostic(ErrorCode.ERR_MainClassNotClass, "extension").WithArguments("E.extension(int)").WithLocation(3, 5));
35587+
// error CS7088: Invalid 'MainTypeName' value: 'E.'.
35588+
Diagnostic(ErrorCode.ERR_BadCompilationOptionValue).WithArguments("MainTypeName", "E.").WithLocation(1, 1));
35589+
35590+
comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("E. "));
35591+
comp.VerifyEmitDiagnostics(
35592+
// error CS7088: Invalid 'MainTypeName' value: 'E. '.
35593+
Diagnostic(ErrorCode.ERR_BadCompilationOptionValue).WithArguments("MainTypeName", "E. ").WithLocation(1, 1));
35594+
35595+
comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName(""));
35596+
comp.VerifyEmitDiagnostics(
35597+
Diagnostic(ErrorCode.ERR_BadCompilationOptionValue).WithArguments("MainTypeName", "").WithLocation(1, 1));
35598+
35599+
comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName(" "));
35600+
comp.VerifyEmitDiagnostics(
35601+
// error CS7088: Invalid 'MainTypeName' value: ' '.
35602+
Diagnostic(ErrorCode.ERR_BadCompilationOptionValue).WithArguments("MainTypeName", " ").WithLocation(1, 1));
35603+
35604+
comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName(".A"));
35605+
comp.VerifyEmitDiagnostics(
35606+
// error CS7088: Invalid 'MainTypeName' value: '.A'.
35607+
Diagnostic(ErrorCode.ERR_BadCompilationOptionValue).WithArguments("MainTypeName", ".A").WithLocation(1, 1));
35608+
}
35609+
35610+
[Fact]
35611+
public void Validation_EntryPoint_05()
35612+
{
35613+
string source = """
35614+
static class E
35615+
{
35616+
extension(int i)
35617+
{
35618+
public static async System.Threading.Tasks.Task<int> Main() { System.Console.Write("ran"); await System.Threading.Tasks.Task.Yield(); return 0; }
35619+
}
35620+
}
35621+
""";
35622+
var comp = CreateCompilation(source, options: TestOptions.DebugExe);
35623+
CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics();
35624+
35625+
comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("E"));
35626+
CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics();
3556735627
}
3556835628

3556935629
[Fact]

0 commit comments

Comments
 (0)