diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index 6075637771e03..da3ecdd5166a7 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -291,3 +291,26 @@ unsafe record struct R( public bool Equals(R other) => true; } ``` + +## Emitting metadata-only executables requires an entrypoint + +***Introduced in Visual Studio 2022 version 17.14*** + +Previously, the entrypoint was [unintentionally unset](https://github.com/dotnet/roslyn/issues/76707) +when emitting executables in metadata-only mode (also known as ref assemblies). +That is now corrected but it also means that a missing entrypoint is a compilation error: + +```cs +// previously successful, now fails: +CSharpCompilation.Create("test").Emit(new MemoryStream(), + options: EmitOptions.Default.WithEmitMetadataOnly(true)) + +CSharpCompilation.Create("test", + // workaround - mark as DLL instead of EXE (the default): + options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) + .Emit(new MemoryStream(), + options: EmitOptions.Default.WithEmitMetadataOnly(true)) +``` + +Similarly this can be observed when using the command-line argument `/refonly` +or the `ProduceOnlyReferenceAssembly` MSBuild property. diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index d5fee9089cef0..e23c3b07ded64 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -3503,6 +3503,29 @@ internal override bool CompileMethods( } SynthesizedMetadataCompiler.ProcessSynthesizedMembers(this, moduleBeingBuilt, cancellationToken); + + if (moduleBeingBuilt.OutputKind.IsApplication()) + { + var entryPointDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); + var entryPoint = MethodCompiler.GetEntryPoint( + this, + moduleBeingBuilt, + hasDeclarationErrors: false, + emitMethodBodies: false, + entryPointDiagnostics, + cancellationToken); + diagnostics.AddRange(entryPointDiagnostics.DiagnosticBag!); + bool shouldSetEntryPoint = entryPoint != null && !entryPointDiagnostics.HasAnyErrors(); + entryPointDiagnostics.Free(); + if (shouldSetEntryPoint) + { + moduleBeingBuilt.SetPEEntryPoint(entryPoint, diagnostics); + } + else + { + return false; + } + } } else { diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 632a09ab48501..aa022352a842e 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -218,7 +218,7 @@ public static void CompileMethodBodies( // Returns the MethodSymbol for the assembly entrypoint. If the user has a Task returning main, // this function returns the synthesized Main MethodSymbol. - private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, bool emitMethodBodies, BindingDiagnosticBag diagnostics, CancellationToken cancellationToken) + internal static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, bool emitMethodBodies, BindingDiagnosticBag diagnostics, CancellationToken cancellationToken) { Debug.Assert(diagnostics.DiagnosticBag != null); @@ -252,6 +252,7 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul if (((object)synthesizedEntryPoint != null) && (moduleBeingBuilt != null) && !hasDeclarationErrors && + !moduleBeingBuilt.EmitOptions.EmitMetadataOnly && !diagnostics.HasAnyErrors()) { BoundStatement body = synthesizedEntryPoint.CreateBody(diagnostics); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 5058dc3912928..c4dca4e0d2899 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -574,8 +574,8 @@ internal static void Main() VerifyMethods(output, "C", new[] { "void C.Main()", "C..ctor()" }); VerifyMvid(output, hasMvidSection: false); - verifyEntryPoint(metadataOutput, expectZero: true); - VerifyMethods(metadataOutput, "C", new[] { "C..ctor()" }); + verifyEntryPoint(metadataOutput, expectZero: false); + VerifyMethods(metadataOutput, "C", new[] { "void C.Main()", "C..ctor()" }); VerifyMvid(metadataOutput, hasMvidSection: true); } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs index 234d9f222229f..126f4d6ed3bbd 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs @@ -3507,5 +3507,224 @@ public class InvalidOperationException(); // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1)); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe() + { + CompileAndVerify(""" + System.Console.WriteLine("a"); + """, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true), + symbolValidator: static (ModuleSymbol module) => + { + Assert.NotEqual(0, module.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + var main = module.GlobalNamespace.GetMember("Program.
$"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + }) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe_AsyncMain() + { + CompileAndVerify(""" + using System.Threading.Tasks; + static class Program + { + static async Task Main() + { + await Task.Yield(); + System.Console.WriteLine("a"); + } + } + """, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true), + symbolValidator: static (ModuleSymbol module) => + { + Assert.NotEqual(0, module.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + var main = module.GlobalNamespace.GetMember("Program.
"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + }) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe_NoMain() + { + var emitResult = CreateCompilation(""" + class Program; + """, + options: TestOptions.ReleaseExe) + .Emit(new MemoryStream(), options: EmitOptions.Default.WithEmitMetadataOnly(true)); + Assert.False(emitResult.Success); + emitResult.Diagnostics.Verify( + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe_PrivateMain_ExcludePrivateMembers() + { + CompileAndVerify(""" + static class Program + { + private static void Main() { } + } + """, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions: EmitOptions.Default + .WithEmitMetadataOnly(true) + .WithIncludePrivateMembers(false), + symbolValidator: static (ModuleSymbol module) => + { + Assert.NotEqual(0, module.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + var main = module.GlobalNamespace.GetMember("Program.Main"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + }) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe_PrivateMain_ExcludePrivateMembers_AsyncMain() + { + CompileAndVerify(""" + using System.Threading.Tasks; + static class Program + { + private static async Task Main() + { + await Task.Yield(); + } + } + """, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions: EmitOptions.Default + .WithEmitMetadataOnly(true) + .WithIncludePrivateMembers(false), + symbolValidator: static (ModuleSymbol module) => + { + Assert.NotEqual(0, module.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + var main = module.GlobalNamespace.GetMember("Program.
"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + }) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void ExcludePrivateMembers_PrivateMain() + { + using var peStream = new MemoryStream(); + using var metadataStream = new MemoryStream(); + var comp = CreateCompilation(""" + static class Program + { + private static void Main() { } + } + """, + options: TestOptions.ReleaseExe); + var emitResult = comp.Emit( + peStream: peStream, + metadataPEStream: metadataStream, + options: EmitOptions.Default.WithIncludePrivateMembers(false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + + verify(peStream); + verify(metadataStream); + + CompileAndVerify(comp).VerifyDiagnostics(); + + static void verify(Stream stream) + { + stream.Position = 0; + Assert.NotEqual(0, new PEHeaders(stream).CorHeader.EntryPointTokenOrRelativeVirtualAddress); + + stream.Position = 0; + var reference = AssemblyMetadata.CreateFromStream(stream).GetReference(); + var comp = CreateCompilation("", references: [reference], + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + var main = comp.GetMember("Program.Main"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void ExcludePrivateMembers_PrivateMain_AsyncMain() + { + using var peStream = new MemoryStream(); + using var metadataStream = new MemoryStream(); + var comp = CreateCompilation(""" + using System.Threading.Tasks; + static class Program + { + private static async Task Main() + { + await Task.Yield(); + } + } + """, + options: TestOptions.ReleaseExe); + var emitResult = comp.Emit( + peStream: peStream, + metadataPEStream: metadataStream, + options: EmitOptions.Default.WithIncludePrivateMembers(false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + + verify(peStream); + verify(metadataStream); + + CompileAndVerify(comp).VerifyDiagnostics(); + + static void verify(Stream stream) + { + stream.Position = 0; + Assert.NotEqual(0, new PEHeaders(stream).CorHeader.EntryPointTokenOrRelativeVirtualAddress); + + stream.Position = 0; + var reference = AssemblyMetadata.CreateFromStream(stream).GetReference(); + var comp = CreateCompilation("", references: [reference], + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + var main = comp.GetMember("Program.
"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void ExcludePrivateMembers_DebugEntryPoint() + { + using var peStream = new MemoryStream(); + using var metadataStream = new MemoryStream(); + + { + var comp = CreateCompilation(""" + static class Program + { + static void M1() { } + static void M2() { } + } + """).VerifyDiagnostics(); + var emitResult = comp.Emit( + peStream: peStream, + metadataPEStream: metadataStream, + debugEntryPoint: comp.GetMember("Program.M1").GetPublicSymbol(), + options: EmitOptions.Default.WithIncludePrivateMembers(false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + } + + { + // M1 should be emitted (it's the debug entry-point), M2 shouldn't (private members are excluded). + metadataStream.Position = 0; + var reference = AssemblyMetadata.CreateFromStream(metadataStream).GetReference(); + var comp = CreateCompilation("", references: [reference], + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + var m1 = comp.GetMember("Program.M1"); + Assert.Equal(Accessibility.Private, m1.DeclaredAccessibility); + Assert.Null(comp.GetMember("Program.M2")); + } + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs index 620867a4028d8..80b439bf2dfdd 100644 --- a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs @@ -4879,12 +4879,14 @@ class C { } public void AttributeArgumentAsEnumFromMetadata() { var metadataStream1 = CSharpCompilation.Create("bar.dll", + options: TestOptions.DebugDll, references: new[] { MscorlibRef }, syntaxTrees: new[] { Parse("public enum Bar { Baz }") }).EmitToStream(options: new EmitOptions(metadataOnly: true)); var ref1 = MetadataReference.CreateFromStream(metadataStream1); var metadataStream2 = CSharpCompilation.Create("goo.dll", references: new[] { MscorlibRef, ref1 }, + options: TestOptions.DebugDll, syntaxTrees: new[] { SyntaxFactory.ParseSyntaxTree( "public class Ca : System.Attribute { public Ca(object o) { } } " + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 9226f1e71e1e3..54d7002e47a40 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -507,7 +507,8 @@ End Module var vbProject = VisualBasic.VisualBasicCompilation.Create( "VBProject", references: new[] { MscorlibRef }, - syntaxTrees: new[] { VisualBasic.VisualBasicSyntaxTree.ParseText(vbSource) }); + syntaxTrees: new[] { VisualBasic.VisualBasicSyntaxTree.ParseText(vbSource) }, + options: new VisualBasic.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); var csSource = @" class Program @@ -557,7 +558,8 @@ End Module var vbProject = VisualBasic.VisualBasicCompilation.Create( "VBProject", references: new[] { MscorlibRef }, - syntaxTrees: new[] { VisualBasic.VisualBasicSyntaxTree.ParseText(vbSource) }); + syntaxTrees: new[] { VisualBasic.VisualBasicSyntaxTree.ParseText(vbSource) }, + options: new VisualBasic.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); var csSource = @" class Program diff --git a/src/Compilers/Core/Portable/PEWriter/Members.cs b/src/Compilers/Core/Portable/PEWriter/Members.cs index c1c2cd6e5ecc6..c15988d673d5b 100644 --- a/src/Compilers/Core/Portable/PEWriter/Members.cs +++ b/src/Compilers/Core/Portable/PEWriter/Members.cs @@ -1047,6 +1047,11 @@ public static bool ShouldInclude(this ITypeDefinitionMember member, EmitContext } } + if (method != null && (context.Module.PEEntryPoint == method || context.Module.DebugEntryPoint == method)) + { + return true; + } + return false; } } diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index da741a04c3d43..73db3eb903cdd 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -1869,7 +1869,7 @@ public PortablePdbBuilder GetPortablePdbBuilder(ImmutableArray typeSystemRo internal void GetEntryPoints(out MethodDefinitionHandle entryPointHandle, out MethodDefinitionHandle debugEntryPointHandle) { - if (IsFullMetadata && !MetadataOnly) + if (IsFullMetadata) { // PE entry point is set for executable programs IMethodReference entryPoint = module.PEEntryPoint; diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index cb0257ff9b975..549049a430208 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2487,6 +2487,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If SynthesizedMetadataCompiler.ProcessSynthesizedMembers(Me, moduleBeingBuilt, cancellationToken) + + If moduleBeingBuilt.OutputKind.IsApplication() Then + Dim entryPoint = GetEntryPointAndDiagnostics(cancellationToken) + diagnostics.AddRange(entryPoint.Diagnostics) + If entryPoint.MethodSymbol IsNot Nothing AndAlso Not entryPoint.Diagnostics.HasAnyErrors() Then + moduleBeingBuilt.SetPEEntryPoint(entryPoint.MethodSymbol, diagnostics) + Else + Return False + End If + End If Else ' start generating PDB checksums if we need to emit PDBs If (emittingPdb OrElse moduleBuilder.EmitOptions.InstrumentationKinds.Contains(InstrumentationKind.TestCoverage)) AndAlso diff --git a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb index 489f5ef45ae5c..0b6f776f7b276 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb @@ -1875,6 +1875,7 @@ End Class Public Sub AttributeArgumentAsEnumFromMetadata() Dim metadata1 = VisualBasicCompilation.Create("bar.dll", + options:=TestOptions.DebugDll, references:={MscorlibRef}, syntaxTrees:={Parse("Public Enum Bar : Baz : End Enum")}).EmitToArray(New EmitOptions(metadataOnly:=True)) @@ -1882,6 +1883,7 @@ End Class Dim metadata2 = VisualBasicCompilation.Create( "goo.dll", + options:=TestOptions.DebugDll, references:={MscorlibRef, ref1}, syntaxTrees:={ VisualBasicSyntaxTree.ParseText( + Public Sub EmitMetadataOnly_Exe() + CompileAndVerify( + + +Module Program + Sub Main() + System.Console.WriteLine("a") + End Sub +End Module + + , + options:=TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions:=EmitOptions.Default.WithEmitMetadataOnly(True), + symbolValidator:=Sub(m) + Assert.NotEqual(0, m.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress) + Dim main = m.GlobalNamespace.GetMember(Of MethodSymbol)("Program.Main") + Assert.Equal(Accessibility.Public, main.DeclaredAccessibility) + End Sub + ).VerifyDiagnostics() + End Sub + + + Public Sub EmitMetadataOnly_Exe_AsyncMain() + Dim emitResult = CreateCompilation( + + +Imports System.Threading.Tasks +Module Program + Public Async Function Main() As Task + Await Task.Yield() + System.Console.WriteLine("a") + End Function +End Module + + , + options:=TestOptions.ReleaseExe, + assemblyName:="MyLib" + ).Emit(New MemoryStream(), options:=EmitOptions.Default.WithEmitMetadataOnly(True)) + Assert.False(emitResult.Success) + emitResult.Diagnostics.AssertTheseDiagnostics( +BC30737: No accessible 'Main' method with an appropriate signature was found in 'MyLib'. + ) + End Sub + + + Public Sub EmitMetadataOnly_Exe_AsyncMain_Void() + Dim emitResult = CreateCompilation( + + +Imports System.Threading.Tasks +Module Program + Public Async Sub Main() + Await Task.Yield() + System.Console.WriteLine("a") + End Sub +End Module + + , + options:=TestOptions.ReleaseExe, + assemblyName:="MyLib" + ).Emit(New MemoryStream(), options:=EmitOptions.Default.WithEmitMetadataOnly(True)) + Assert.False(emitResult.Success) + emitResult.Diagnostics.AssertTheseDiagnostics( +BC36934: The 'Main' method cannot be marked 'Async'. + Public Async Sub Main() + ~~~~ + ) + End Sub + + + Public Sub EmitMetadataOnly_Exe_NoMain() + Dim emitResult = CreateCompilation( + + +Module Program +End Module + + , + options:=TestOptions.ReleaseExe, + assemblyName:="MyLib" + ).Emit(New MemoryStream(), options:=EmitOptions.Default.WithEmitMetadataOnly(True)) + Assert.False(emitResult.Success) + emitResult.Diagnostics.AssertTheseDiagnostics( +BC30420: 'Sub Main' was not found in 'MyLib'. + ) + End Sub + + + Public Sub EmitMetadataOnly_Exe_FriendMain_ExcludePrivateMembers() + CompileAndVerify( + + +Module Program + Friend Sub Main() + End Sub +End Module + + , + options:=TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions:=EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False), + symbolValidator:=Sub(m) + Assert.NotEqual(0, m.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress) + Dim main = m.GlobalNamespace.GetMember(Of MethodSymbol)("Program.Main") + Assert.Equal(Accessibility.Internal, main.DeclaredAccessibility) + End Sub + ).VerifyDiagnostics() + End Sub + + + Public Sub ExcludePrivateMembers_FriendMain() + Using peStream As New MemoryStream() + Using metadataStream As New MemoryStream() + Dim comp = CreateCompilation( + + +Module Program + Friend Sub Main() + End Sub +End Module + + , + options:=TestOptions.ReleaseExe + ) + Dim emitResult = comp.Emit( + peStream:=peStream, + metadataPEStream:=metadataStream, + options:=EmitOptions.Default.WithIncludePrivateMembers(False)) + Assert.True(emitResult.Success) + emitResult.Diagnostics.Verify() + + Verify(peStream) + Verify(metadataStream) + + CompileAndVerify(comp).VerifyDiagnostics() + End Using + End Using + End Sub + + Private Shared Sub Verify(stream As Stream) + stream.Position = 0 + Assert.NotEqual(0, New PEHeaders(stream).CorHeader.EntryPointTokenOrRelativeVirtualAddress) + + stream.Position = 0 + Dim reference = AssemblyMetadata.CreateFromStream(stream).GetReference() + Dim comp = CreateCompilation("", references:={reference}, options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + Dim main = comp.GetMember(Of MethodSymbol)("Program.Main") + Assert.Equal(Accessibility.Internal, main.DeclaredAccessibility) + End Sub + + + Public Sub ExcludePrivateMembers_DebugEntryPoint() + Using peStream As New MemoryStream() + Using metadataStream As New MemoryStream() + Dim comp = CreateCompilation( + + +Module Program + Private Sub M1() + End Sub + Private Sub M2() + End Sub +End Module + + ).VerifyDiagnostics() + Dim emitResult = comp.Emit( + peStream:=peStream, + metadataPEStream:=metadataStream, + debugEntryPoint:=comp.GetMember(Of MethodSymbol)("Program.M1"), + options:=EmitOptions.Default.WithIncludePrivateMembers(False)) + Assert.True(emitResult.Success) + emitResult.Diagnostics.Verify() + + ' M1 should be emitted (it's the debug entry-point), M2 shouldn't (private members are excluded). + metadataStream.Position = 0 + Dim reference = AssemblyMetadata.CreateFromStream(metadataStream).GetReference() + Dim comp2 = CreateCompilation("", references:={reference}, + options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + Dim m1 = comp2.GetMember(Of MethodSymbol)("Program.M1") + Assert.Equal(Accessibility.Private, m1.DeclaredAccessibility) + Assert.Null(comp2.GetMember(Of MethodSymbol)("Program.M2")) + End Using + End Using + End Sub End Class End Namespace diff --git a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs index 27f3d213052c5..e4a42458c6bfa 100644 --- a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs +++ b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs @@ -479,7 +479,11 @@ internal async Task TestGetCompilationOnCrossLanguageDependentProjectChanged( var solutionX = workspace.CurrentSolution; var document1 = new EditorTestHostDocument(@"public class C { }"); - var project1 = new EditorTestHostProject(workspace, document1, name: "project1"); + var project1 = new EditorTestHostProject(workspace, document1, name: "project1", + compilationOptions: solutionX.Services + .GetRequiredLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); var document2 = new EditorTestHostDocument(""" Public Class D @@ -533,7 +537,11 @@ public async Task TestDependentSemanticVersionChangesWhenNotOriginallyAccessed() var solutionX = workspace.CurrentSolution; var document1 = new EditorTestHostDocument(@"public class C { }"); - var project1 = new EditorTestHostProject(workspace, document1, name: "project1"); + var project1 = new EditorTestHostProject(workspace, document1, name: "project1", + compilationOptions: solutionX.Services + .GetRequiredLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); var document2 = new EditorTestHostDocument(""" Public Class D @@ -601,7 +609,11 @@ internal async Task TestGetCompilationOnCrossLanguageDependentProjectChangedInPr var solutionX = workspace.CurrentSolution; var document1 = new EditorTestHostDocument(@"public class C { }"); - var project1 = new EditorTestHostProject(workspace, document1, name: "project1"); + var project1 = new EditorTestHostProject(workspace, document1, name: "project1", + compilationOptions: solutionX.Services + .GetRequiredLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); var document2 = new EditorTestHostDocument(""" Public Class D diff --git a/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs b/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs index 7bc0ab4d97aeb..87f8dc0ae04d1 100644 --- a/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs +++ b/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs @@ -12,6 +12,7 @@ using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; @@ -36,7 +37,11 @@ private static Solution AddProjectWithMetadataReferences(Solution solution, stri projectName, languageName, metadataReferences: [metadataReference], - projectReferences: projectReferences.Select(p => new ProjectReference(p))); + projectReferences: projectReferences.Select(p => new ProjectReference(p)), + compilationOptions: solution.Services + .GetRequiredLanguageService(languageName) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); return solution.AddProject(pi).AddDocument(did, $"{projectName}.{suffix}", SourceText.From(code)); } diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 73f7d29d9fe0c..fdb847ccc5e62 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -2820,7 +2820,10 @@ public async Task TestCrossLanguageProjectsAsync() using var workspace = CreateWorkspace(); var solution = workspace.CurrentSolution - .AddProject(pm1, "goo", "goo.dll", LanguageNames.CSharp) + .AddProject(ProjectInfo.Create(pm1, VersionStamp.Create(), "goo", "goo.dll", LanguageNames.CSharp, compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))) .AddMetadataReference(pm1, s_mscorlib) .AddProject(pm2, "bar", "bar.dll", LanguageNames.VisualBasic) .AddMetadataReference(pm2, s_mscorlib) @@ -3968,6 +3971,13 @@ public class C : A { .AddProjectReference(pid3, new ProjectReference(pid1)) .AddProjectReference(pid3, new ProjectReference(pid2)); + var options = solution.Workspace.Services + .GetLanguageService(LanguageNames.VisualBasic) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary); + solution = solution.WithProjectCompilationOptions(pid1, options) + .WithProjectCompilationOptions(pid2, options); + var project3 = solution.GetProject(pid3); var comp3 = project3.GetCompilationAsync().Result; var classC = comp3.GetTypeByMetadataName("C"); @@ -4020,7 +4030,11 @@ public async Task TestProjectWithNoBrokenReferencesHasNoIncompleteReferences() "CSharpProject", "CSharpProject", LanguageNames.CSharp, - metadataReferences: [MscorlibRef])); + metadataReferences: [MscorlibRef], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); var project2 = workspace.AddProject( ProjectInfo.Create( ProjectId.CreateNewId(), @@ -4221,7 +4235,11 @@ public async Task TestFrozenPartialProjectAlwaysIsIncomplete() "CSharpProject", "CSharpProject", LanguageNames.CSharp, - metadataReferences: [MscorlibRef])); + metadataReferences: [MscorlibRef], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); var project2 = workspace.AddProject( ProjectInfo.Create( @@ -5326,7 +5344,12 @@ private static void GetMultipleProjects( "CSharpProject", "CSharpProject", LanguageNames.CSharp, - metadataReferences: [MscorlibRef]).WithHasAllInformation(hasAllInformation: false)); + metadataReferences: [MscorlibRef], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)) + .WithHasAllInformation(hasAllInformation: false)); vbNormalProject = workspace.AddProject( ProjectInfo.Create( @@ -5335,7 +5358,11 @@ private static void GetMultipleProjects( "VisualBasicProject", "VisualBasicProject", LanguageNames.VisualBasic, - metadataReferences: [MscorlibRef])); + metadataReferences: [MscorlibRef], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.VisualBasic) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); dependsOnBrokenProject = workspace.AddProject( ProjectInfo.Create( @@ -5345,7 +5372,11 @@ private static void GetMultipleProjects( "VisualBasicProject", LanguageNames.VisualBasic, metadataReferences: [MscorlibRef], - projectReferences: [new ProjectReference(csBrokenProject.Id), new ProjectReference(vbNormalProject.Id)])); + projectReferences: [new ProjectReference(csBrokenProject.Id), new ProjectReference(vbNormalProject.Id)], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.VisualBasic) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); dependsOnVbNormalProject = workspace.AddProject( ProjectInfo.Create( @@ -5355,7 +5386,11 @@ private static void GetMultipleProjects( "CSharpProject", LanguageNames.CSharp, metadataReferences: [MscorlibRef], - projectReferences: [new ProjectReference(vbNormalProject.Id)])); + projectReferences: [new ProjectReference(vbNormalProject.Id)], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); transitivelyDependsOnBrokenProjects = workspace.AddProject( ProjectInfo.Create(