diff --git a/docs/features/refout.md b/docs/features/refout.md index 9ab450afc9ffcebae8039e48c545da907d9119b8..81253cb00f310a272970169ccbfbbbbd110c989a 100644 --- a/docs/features/refout.md +++ b/docs/features/refout.md @@ -37,11 +37,10 @@ The `/refout` parameter specifies a file path where the ref assembly should be o The `/refonly` parameter is a flag that indicates that a ref assembly should be output instead of an implementation assembly. The `/refonly` parameter is not allowed together with the `/refout` parameter, as it doesn't make sense to have both the primary and secondary outputs be ref assemblies. Also, the `/refonly` parameter silently disables outputting PDBs, as ref assemblies cannot be executed. The `/refonly` parameter translates to `EmitMetadataOnly` being `true`, and `IncludePrivateMembers` being `false` in the `Emit` API (see details below). -Neither `/refonly` nor `/refout` are permitted with `/target:module` or `/addmodule` options. - -When the compiler produces documentation, the contents produced will match the APIs that go into the primary output. In other words, the documentation will be filtered down when using the `/refonly` parameter. +Neither `/refonly` nor `/refout` are permitted with `/target:module`, `/addmodule`, or `/link:...` options. The compilation from the command-line will either produce both assemblies (implementation and ref) or neither. There is no "partial success" scenario. +When the compiler produces documentation, it is un-affected by either the `/refonly` or `/refout` parameters. ### CscTask/CoreCompile The `CoreCompile` target will support a new output, called `IntermediateRefAssembly`, which parallels the existing `IntermediateAssembly`. @@ -65,8 +64,10 @@ Going back to the 4 driving scenarios: ## Future As mentioned above, there may be further refinements after C# 7.1: -- controlling internals (producing public ref assemblies) -- produce ref assemblies even when there are errors outside method bodies (emitting error types when `EmitOptions.TolerateErrors` is set) +- Controlling internals (producing public ref assemblies) +- Produce ref assemblies even when there are errors outside method bodies (emitting error types when `EmitOptions.TolerateErrors` is set) +- When the compiler produces documentation, the contents produced could be filtered down to match the APIs that go into the primary output. In other words, the documentation could be filtered down when using the `/refonly` parameter. + ## Open questions - should explicit method implementations be included in ref assemblies? diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 24a49f2d8bd9f18082d192a0bce5daa2073ae100..5f2881190b3a986dbb94de547d09022ce9c9c073 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -6730,6 +6730,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to Cannot embed types when using /refout or /refonly.. + /// + internal static string ERR_NoEmbeddedTypeWhenRefOutOrRefOnly { + get { + return ResourceManager.GetString("ERR_NoEmbeddedTypeWhenRefOutOrRefOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to Program does not contain a static 'Main' method suitable for an entry point. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 6aedd38c798ca1a34428b6cdc39c52912285d6da..9a5efb852216867ea9a274a17e8068991262ddcc 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2300,6 +2300,9 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot compile net modules when using /refout or /refonly. + + Cannot embed types when using /refout or /refonly. + Overloadable operator expected diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index c28578c8eb9b52a648a450e0fe736b7c631948a7..cbde4e6815d1213e3356aa502514fbdf50c2237a 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -1178,6 +1178,11 @@ public new CSharpCommandLineArguments Parse(IEnumerable args, string bas AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoRefOutWhenRefOnly); } + if ((refOnly || outputRefFilePath != null) && metadataReferences.Any(r => r.Properties.EmbedInteropTypes)) + { + AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly); + } + if (outputKind == OutputKind.NetModule && (refOnly || outputRefFilePath != null)) { AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly); diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs index cfbad7e754c4bf37ea591dc44ca410268f0a496d..da2b554d0c6bdf2ee8f226c8ab0a546ced12bcc3 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs @@ -325,7 +325,6 @@ IEnumerable Cci.ITypeDefinition.GetExplicitImplementat foreach (var member in this.GetMembers()) { - // PROTOTYPE(refout) Do something here? if (member.Kind == SymbolKind.Method) { var method = (MethodSymbol)member; diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index e582e372c0c04bb73a3a5d5baecdca16b55cae64..68c162402c5ba577ee200758d94b5e19348a3366 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1476,11 +1476,12 @@ internal enum ErrorCode WRN_Experimental = 8305, ERR_TupleInferredNamesNotAvailable = 8306, - ERR_NoRefOutWhenRefOnly = 8355, - ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8356, + ERR_NoRefOutWhenRefOnly = 8307, + ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8308, + ERR_NoEmbeddedTypeWhenRefOutOrRefOnly = 8309, - ERR_BadDynamicMethodArgDefaultLiteral = 9000, - ERR_DefaultLiteralNotValid = 9001, - WRN_DefaultInSwitch = 9002, + ERR_BadDynamicMethodArgDefaultLiteral = 8310, + ERR_DefaultLiteralNotValid = 8311, + WRN_DefaultInSwitch = 8312, } } diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index a7c09bb93d260f973f806250b94fee5f71e800f0..616e19367c623c6f7b502ee3256d3fe71c86c565 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -2886,6 +2886,18 @@ public void ParseOut() // error CS8301: Do not use refout when using refonly. Diagnostic(ErrorCode.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)); + parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/link:b", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8357: Cannot embed types when using /refout or /refonly. + Diagnostic(ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1) + ); + + parsedArgs = DefaultParse(new[] { "/refonly", "/link:b", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8357: Cannot embed types when using /refout or /refonly. + Diagnostic(ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1) + ); + parsedArgs = DefaultParse(new[] { "/refonly:incorrect", "a.cs" }, baseDirectory); parsedArgs.Errors.Verify( // error CS2007: Unrecognized option: '/refonly:incorrect' @@ -9067,6 +9079,11 @@ public static void Main() { System.Console.Write(""Hello""); } + /// Private method + private static void PrivateMethod() + { + System.Console.Write(""Private""); + } }"); var outWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -9081,7 +9098,7 @@ public static void Main() MetadataReaderUtils.VerifyPEMetadata(exe, new[] { "TypeDefinition:", "TypeDefinition:C" }, - new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" }, + new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void PrivateMethod()", "MethodDefinition:Void .ctor()" }, new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } ); @@ -9099,6 +9116,9 @@ public static void Main() Main method + + Private method + "; Assert.Equal(expectedDoc, content.Trim()); @@ -9170,6 +9190,16 @@ public static void Main() remove { } } private event Action E2; + + /// Private Class Field + private int field; + + /// Private Struct + private struct S + { + /// Private Struct Field + private int field; + } }"); var outWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -9185,7 +9215,7 @@ public static void Main() // The types and members that are included needs further refinement. // See issue https://github.com/dotnet/roslyn/issues/17612 MetadataReaderUtils.VerifyPEMetadata(refDll, - new[] { "TypeDefinition:", "TypeDefinition:C" }, + new[] { "TypeDefinition:", "TypeDefinition:C", "TypeDefinition:S" }, new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" }, new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "ReferenceAssemblyAttribute" } ); @@ -9207,10 +9237,20 @@ public static void Main() Main method + + Private Class Field + + + Private Struct + + + Private Struct Field + "; Assert.Equal(expectedDoc, content.Trim()); + // Clean up temp files CleanupAllGeneratedFiles(dir.Path); } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 77ed494cf7dc765ce67c712250bfafa6736b919f..dfe2050db72b5786007cfb835ad8f8e3c2429d38 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -261,11 +261,11 @@ internal static void Main() emitResult.Diagnostics.Verify(); VerifyEntryPoint(output, expectZero: false); - VerifyMethods(output, new[] { "void C.Main()", "C..ctor()" }); + VerifyMethods(output, "C", new[] { "void C.Main()", "C..ctor()" }); VerifyMvid(output, hasMvidSection: false); VerifyEntryPoint(metadataOutput, expectZero: true); - VerifyMethods(metadataOutput, new[] { "C..ctor()" }); + VerifyMethods(metadataOutput, "C", new[] { "C..ctor()" }); VerifyMvid(metadataOutput, hasMvidSection: true); } @@ -399,9 +399,9 @@ public class C Assert.True(emitResult.Success); emitResult.Diagnostics.Verify(); - VerifyMethods(output, new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateSetter.get", "void C.PrivateSetter.set", + VerifyMethods(output, "C", new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateSetter.get", "void C.PrivateSetter.set", "C..ctor()", "System.Int32 C.PrivateSetter { get; private set; }" }); - VerifyMethods(metadataOutput, new[] { "System.Int32 C.PrivateSetter.get", "C..ctor()", "System.Int32 C.PrivateSetter { get; }" }); + VerifyMethods(metadataOutput, "C", new[] { "System.Int32 C.PrivateSetter.get", "C..ctor()", "System.Int32 C.PrivateSetter { get; }" }); VerifyMvid(output, hasMvidSection: false); VerifyMvid(metadataOutput, hasMvidSection: true); } @@ -425,9 +425,9 @@ public class C Assert.True(emitResult.Success); emitResult.Diagnostics.Verify(); - VerifyMethods(output, new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateGetter.get", "void C.PrivateGetter.set", + VerifyMethods(output, "C", new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateGetter.get", "void C.PrivateGetter.set", "C..ctor()", "System.Int32 C.PrivateGetter { private get; set; }" }); - VerifyMethods(metadataOutput, new[] { "void C.PrivateGetter.set", "C..ctor()", "System.Int32 C.PrivateGetter { set; }" }); + VerifyMethods(metadataOutput, "C", new[] { "void C.PrivateGetter.set", "C..ctor()", "System.Int32 C.PrivateGetter { set; }" }); } } @@ -449,9 +449,9 @@ public class C Assert.True(emitResult.Success); emitResult.Diagnostics.Verify(); - VerifyMethods(output, new[] { "System.Int32 C.this[System.Int32 i].get", "void C.this[System.Int32 i].set", + VerifyMethods(output, "C", new[] { "System.Int32 C.this[System.Int32 i].get", "void C.this[System.Int32 i].set", "C..ctor()", "System.Int32 C.this[System.Int32 i] { private get; set; }" }); - VerifyMethods(metadataOutput, new[] { "void C.this[System.Int32 i].set", "C..ctor()", + VerifyMethods(metadataOutput, "C", new[] { "void C.this[System.Int32 i].set", "C..ctor()", "System.Int32 C.this[System.Int32 i] { set; }" }); } } @@ -478,9 +478,9 @@ public class C : Base emitResult.Diagnostics.Verify(); Assert.True(emitResult.Success); - VerifyMethods(output, new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); + VerifyMethods(output, "C", new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); // A getter is synthesized on C.Property so that it can be marked as sealed. It is emitted despite being internal because it is virtual. - VerifyMethods(metadataOutput, new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); + VerifyMethods(metadataOutput, "C", new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); } } @@ -504,7 +504,7 @@ public class C ); } - private static void VerifyMethods(MemoryStream stream, string[] expectedMethods) + private static void VerifyMethods(MemoryStream stream, string containingType, string[] expectedMethods) { stream.Position = 0; var metadataRef = AssemblyMetadata.CreateFromImage(stream.ToArray()).GetReference(); @@ -514,7 +514,7 @@ private static void VerifyMethods(MemoryStream stream, string[] expectedMethods) AssertEx.Equal( expectedMethods, - compWithMetadata.GetMember("C").GetMembers().Select(m => m.ToTestDisplayString())); + compWithMetadata.GetMember(containingType).GetMembers().Select(m => m.ToTestDisplayString())); } [Fact] @@ -824,6 +824,10 @@ string M() )); } + /// + /// The client compilation should not be affected (except for some diagnostic differences) + /// by the library assembly only having metadata, or not including private members. + /// private void VerifyRefAssemblyClient(string lib_cs, string client_cs, Action validator, int debugFlag = -1) { // Whether the library is compiled in full, as metadata-only, or as a ref assembly should be transparent @@ -859,6 +863,55 @@ private static void VerifyRefAssemblyClient(string lib_cs, string source, Action validator(comp); } + [Theory] + [InlineData("")] + [InlineData(@"[assembly: System.Reflection.AssemblyVersion(""1"")]")] + [InlineData(@"[assembly: System.Reflection.AssemblyVersion(""1.0.*"")]")] + public void RefAssembly_EmitAsDeterministic(string source) + { + var comp = CreateStandardCompilation(source, options: TestOptions.DebugDll.WithDeterministic(false)); + + var (image1, refImage1) = emitRefOut(); + + // The resolution of the PE header time date stamp is seconds, and we want to make sure that has an opportunity to change + // between calls to Emit. + Thread.Sleep(TimeSpan.FromSeconds(1)); + var (image2, refImage2) = emitRefOut(); + var refImage3 = emitRefOnly(); + + AssertEx.Equal(refImage1, refImage2); + AssertEx.Equal(refImage1, refImage3); + + var comp2 = CreateStandardCompilation(source, options: TestOptions.DebugDll.WithDeterministic(false)); + var refImage4 = emitRefOnly(); + AssertEx.Equal(refImage1, refImage4); + + (ImmutableArray image, ImmutableArray refImage) emitRefOut() + { + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + var options = EmitOptions.Default.WithIncludePrivateMembers(false); + comp.VerifyEmitDiagnostics(); + var result = comp.Emit(output, metadataPEStream: metadataOutput, + options: options); + return (output.ToImmutable(), metadataOutput.ToImmutable()); + } + } + + ImmutableArray emitRefOnly() + { + using (var output = new MemoryStream()) + { + var options = EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false); + comp.VerifyEmitDiagnostics(); + var result = comp.Emit(output, + options: options); + return output.ToImmutable(); + } + } + } + [Theory] [InlineData("public int M() { error(); }", true)] [InlineData("public int M() { error() }", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 @@ -1057,7 +1110,7 @@ public void EmitMetadataOnly_DisallowOutputtingNetModule() } [Fact] - public void EmitMetadata_AllowEmbedding() + public void RefAssembly_AllowEmbeddingPdb() { CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, options: TestOptions.DebugDll); @@ -1069,7 +1122,7 @@ public void EmitMetadata_AllowEmbedding() options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded).WithIncludePrivateMembers(false)); VerifyEmbeddedDebugInfo(output, new[] { DebugDirectoryEntryType.CodeView, DebugDirectoryEntryType.EmbeddedPortablePdb }); - VerifyEmbeddedDebugInfo(metadataOutput, new DebugDirectoryEntryType[] { }); + VerifyEmbeddedDebugInfo(metadataOutput, new DebugDirectoryEntryType[] { DebugDirectoryEntryType.Reproducible }); } void VerifyEmbeddedDebugInfo(MemoryStream stream, DebugDirectoryEntryType[] expected) @@ -1083,7 +1136,7 @@ void VerifyEmbeddedDebugInfo(MemoryStream stream, DebugDirectoryEntryType[] expe } [Fact] - public void EmitMetadataOnly_DisallowEmbedding() + public void EmitMetadataOnly_DisallowEmbeddingPdb() { CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, options: TestOptions.DebugDll); @@ -1096,6 +1149,30 @@ public void EmitMetadataOnly_DisallowEmbedding() } } + [Fact] + public void RefAssembly_DisallowEmbeddingTypes() + { + CSharpCompilation comp = CreateCompilation("", options: TestOptions.DebugDll, + references: new MetadataReference[] + { + TestReferences.SymbolsTests.NoPia.GeneralPia.WithEmbedInteropTypes(true) + }); + + using (var output = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, + options: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false))); + } + + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, + metadataPEStream: metadataOutput, + options: EmitOptions.Default.WithIncludePrivateMembers(false))); + } + } + [Fact] public void EmitMetadata() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs index f59dff07b54fc7803572af470d50f30b89738889..b73c38922472a821ce84a60b9515e2d6ee20fc46 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -1434,7 +1434,7 @@ static void M1() var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); comp.VerifyDiagnostics( - // (7,14): error CS9000: Cannot use a default literal as an argument to a dynamically dispatched operation. + // (7,14): error CS8310: Cannot use a default literal as an argument to a dynamically dispatched operation. // d.M2(default); Diagnostic(ErrorCode.ERR_BadDynamicMethodArgDefaultLiteral, "default").WithLocation(7, 14) ); @@ -1524,7 +1524,7 @@ static void Main() var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (6,27): error CS9001: Use of default literal is not valid in this context + // (6,27): error CS8311: Use of default literal is not valid in this context // foreach (int x in default) { } Diagnostic(ErrorCode.ERR_DefaultLiteralNotValid, "default").WithLocation(6, 27), // (7,27): error CS0186: Use of null is not valid in this context @@ -1549,7 +1549,7 @@ static void Main() "; var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular7_1, references: new[] { SystemCoreRef }); compilation.VerifyDiagnostics( - // (6,35): error CS9001: Use of default literal is not valid in this context + // (6,35): error CS8311: Use of default literal is not valid in this context // var q = from x in default select x; Diagnostic(ErrorCode.ERR_DefaultLiteralNotValid, "select x").WithLocation(6, 35), // (7,43): error CS1942: The type of the expression in the select clause is incorrect. Type inference failed in the call to 'Select'. @@ -1898,7 +1898,7 @@ static void M(int x) var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (12,18): warning CS9002: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. + // (12,18): warning CS8312: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. // case default: Diagnostic(ErrorCode.WRN_DefaultInSwitch, "default").WithLocation(12, 18) ); @@ -1931,7 +1931,7 @@ static void M(object x) var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (12,18): warning CS9002: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. + // (12,18): warning CS8312: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. // case default: Diagnostic(ErrorCode.WRN_DefaultInSwitch, "default").WithLocation(12, 18) ); diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index b2fc07f7f8952e01fd44fa0ea93e5fb7eb62f713..d55296b59c2cc5583a8cb8803b8f2ef4adec20b4 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -183,9 +183,8 @@ static void Main() [Fact] public void WarningLevel_1() { - for (int i = 0; i < 10000; i++) + foreach (ErrorCode errorCode in Enum.GetValues(typeof(ErrorCode))) { - ErrorCode errorCode = (ErrorCode)i; string errorCodeName = errorCode.ToString(); if (errorCodeName.StartsWith("WRN", StringComparison.Ordinal)) { @@ -215,12 +214,15 @@ public void WarningLevel_2() Assert.Equal(3, ErrorFacts.GetWarningLevel(ErrorCode.WRN_IsDynamicIsConfusing)); Assert.Equal(2, ErrorFacts.GetWarningLevel(ErrorCode.WRN_NoSources)); - // There is space in the range of error codes from 7000-8999 that we might add for new errors in post-Dev10. // If a new warning is added, this test will fail and adding the new case with the expected error level will be required. - for (int i = 7000; i < 9000; i++) + foreach (ErrorCode errorCode in Enum.GetValues(typeof(ErrorCode))) { - ErrorCode errorCode = (ErrorCode)i; + if ((int)errorCode < 7000) + { + continue; + } + string errorCodeName = errorCode.ToString(); if (errorCodeName.StartsWith("WRN", StringComparison.Ordinal)) { @@ -244,6 +246,7 @@ public void WarningLevel_2() case ErrorCode.WRN_AlignmentMagnitude: case ErrorCode.WRN_TupleLiteralNameMismatch: case ErrorCode.WRN_Experimental: + case ErrorCode.WRN_DefaultInSwitch: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; case ErrorCode.WRN_MainIgnored: diff --git a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs index 5b12dc838c5175c176b28afc57227e5cc4575de8..03b27b3c2904c3b20149f83c524ffc01f12f31f2 100644 --- a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs +++ b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs @@ -20,6 +20,11 @@ public sealed class CopyRefAssembly : Task [Required] public string DestinationPath { get; set; } + static CopyRefAssembly() + { + AssemblyResolution.Install(); + } + public CopyRefAssembly() { TaskResources = ErrorString.ResourceManager; @@ -45,20 +50,27 @@ public override bool Execute() Log.LogMessageFromResources(MessageImportance.High, "CopyRefAssembly_BadSource3", SourcePath, e.Message, e.StackTrace); } - try + if (source.Equals(Guid.Empty)) + { + Log.LogMessageFromResources(MessageImportance.High, "CopyRefAssembly_SourceNotRef1", SourcePath); + } + else { - Guid destination = ExtractMvid(DestinationPath); + try + { + Guid destination = ExtractMvid(DestinationPath); - if (source.Equals(destination)) + if (!source.Equals(Guid.Empty) && source.Equals(destination)) + { + Log.LogMessageFromResources(MessageImportance.Low, "CopyRefAssembly_SkippingCopy1", DestinationPath); + return true; + } + } + catch (Exception) { - Log.LogMessageFromResources(MessageImportance.Low, "CopyRefAssembly_SkippingCopy1", DestinationPath); - return true; + Log.LogMessageFromResources(MessageImportance.High, "CopyRefAssembly_BadDestination1", DestinationPath); } } - catch (Exception) - { - Log.LogMessage(MessageImportance.High, "CopyRefAssembly_BadDestination1", DestinationPath); - } } return Copy(); diff --git a/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs b/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs index 560a55decf577354834f0fe7ae3424ff49ba9b79..d4db8b30d05b122a8dbe7738f3a534b2194de58a 100644 --- a/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs +++ b/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs @@ -98,6 +98,15 @@ internal class ErrorString { } } + /// + /// Looks up a localized string similar to Could not extract the MVID from "{0}". Are you sure it is a reference assembly?. + /// + internal static string CopyRefAssembly_SourceNotRef1 { + get { + return ResourceManager.GetString("CopyRefAssembly_SourceNotRef1", resourceCulture); + } + } + /// /// Looks up a localized string similar to MSB3053: The assembly alias "{1}" on reference "{0}" contains illegal characters.. /// diff --git a/src/Compilers/Core/MSBuildTask/ErrorString.resx b/src/Compilers/Core/MSBuildTask/ErrorString.resx index 9f6962ded7d75f51ab055abf6011f181ba4e188f..ac0d63fc64e5e7992918b518dfe81f59dcddd624 100644 --- a/src/Compilers/Core/MSBuildTask/ErrorString.resx +++ b/src/Compilers/Core/MSBuildTask/ErrorString.resx @@ -154,6 +154,9 @@ Reference assembly "{0}" already has latest information. Leaving it untouched. + + Could not extract the MVID from "{0}". Are you sure it is a reference assembly? + Failed to check the content hash of the source ref assembly '{0}': {1} {2} diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index 9a38358b5b06aedbf37b224742c0e5d832841f30..0a1d2c3f38c6a06f6392c564cf40726cd8b42cd1 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -477,6 +477,15 @@ internal class CodeAnalysisResources { } } + /// + /// Looks up a localized string similar to Embedded interop type references should not be given when emitting a ref assembly.. + /// + internal static string EmbedInteropTypesUnexpectedWhenEmittingRefAssembly { + get { + return ResourceManager.GetString("EmbedInteropTypesUnexpectedWhenEmittingRefAssembly", resourceCulture); + } + } + /// /// Looks up a localized string similar to A key in the pathMap is empty.. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 5843e7ac09b722965ed88fab8e4a16fada5902d0..b0d49fe7d3b07df42cb9d4d6cf8c2b4d6222ffe8 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -333,6 +333,9 @@ PDB stream should not be given when emitting metadata only. + + Embedded interop type references should not be given when emitting a ref assembly. + Metadata PE stream should not be given when emitting metadata only. diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index d1d232aa1fafcd4843527bb1dc55bf809f9e2057..be14eaa324e02cbc32098f5df47fe2e6b3230319 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -2087,6 +2087,13 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken) throw new ArgumentException(CodeAnalysisResources.IncludingPrivateMembersUnexpectedWhenEmittingToMetadataPeStream, nameof(metadataPEStream)); } + if ((metadataPEStream != null || options?.EmitMetadataOnly == true) && + options?.IncludePrivateMembers == false && + References.Any(r => r.Properties.EmbedInteropTypes)) + { + throw new ArgumentException(CodeAnalysisResources.EmbedInteropTypesUnexpectedWhenEmittingRefAssembly, nameof(metadataPEStream)); + } + if (options?.DebugInformationFormat == DebugInformationFormat.Embedded && options?.EmitMetadataOnly == true) { @@ -2636,6 +2643,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken) bool emitSecondaryAssembly = getMetadataPeStreamOpt != null; bool includePrivateMembersOnPrimaryOutput = metadataOnly ? includePrivateMembers : true; + bool deterministicPrimaryOutput = (metadataOnly && !includePrivateMembers) || isDeterministic; if (!Cci.PeWriter.WritePeToStream( new EmitContext(moduleBeingBuilt, null, metadataDiagnostics, metadataOnly, includePrivateMembersOnPrimaryOutput), messageProvider, @@ -2644,7 +2652,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken) nativePdbWriterOpt, pdbPathOpt, metadataOnly, - isDeterministic, + deterministicPrimaryOutput, emitTestCoverageData, cancellationToken)) { @@ -2665,7 +2673,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken) nativePdbWriterOpt: null, pdbPathOpt: null, metadataOnly: true, - isDeterministic: isDeterministic, + isDeterministic: true, emitTestCoverageData: false, cancellationToken: cancellationToken)) { diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index bdf7c957645d558d5d5178da4dfaada39041bc48..5cf8461f793e8613e8152fd3f775e8b9fe584ef5 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -247,7 +247,7 @@ public uint GetFakeSymbolTokenForIL(Cci.IReference symbol, SyntaxNode syntaxNode uint token = _referencesInILMap.GetOrAddTokenFor(symbol, out added); if (added) { - ReferenceDependencyWalker.VisitReference(symbol, new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true)); // PROTOTYPE(refout) Not sure about this + ReferenceDependencyWalker.VisitReference(symbol, new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true)); } return token; } diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 5f58fd2094c52d2b6946b12a402d11498902fa63..029a9e8b343a9ada46323bf10edea35b4d68da87 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -1203,6 +1203,10 @@ lVbRuntimePlus: AddDiagnostic(diagnostics, ERRID.ERR_NoRefOutWhenRefOnly) End If + If (refOnly OrElse outputRefFileName IsNot Nothing) AndAlso metadataReferences.Any(Function(r) r.Properties.EmbedInteropTypes) Then + AddDiagnostic(diagnostics, ERRID.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly) + End If + If outputKind = OutputKind.NetModule AndAlso (refOnly OrElse outputRefFileName IsNot Nothing) Then AddDiagnostic(diagnostics, ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly) End If diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 5303649b024cf1fe8e11ae2d0c1a8ef46463652a..e1354d47b40bdf363ff3460b59d96ee5a8218e17 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1733,6 +1733,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_NoRefOutWhenRefOnly = 37300 ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 37301 + ERR_NoEmbeddedTypeWhenRefOutOrRefOnly = 37302 '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index 50815e2e104e2b9dc9a905427d44bc12d8f7d8f2..19140e975e4dd87baf45b48c0fb93031cee0a0b7 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -8070,6 +8070,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Cannot embed types when using /refout or /refonly.. + ''' + Friend ReadOnly Property ERR_NoEmbeddedTypeWhenRefOutOrRefOnly() As String + Get + Return ResourceManager.GetString("ERR_NoEmbeddedTypeWhenRefOutOrRefOnly", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Array bounds cannot appear in type specifiers.. ''' diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index d7239d6c732ba1bab68e94681f07be119fbdad28..aa0c8d2866e81230ffccf862f2906d9d8db6f92f 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5471,6 +5471,9 @@ Do not use refout when using refonly. + + Cannot embed types when using /refout or /refonly. + Cannot compile net modules when using /refout or /refonly. diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index c0aa1e3150f7db11ca274c94e00fb9957af3933a..0e3f43a3cdf8162bcdb95a3bf510879b93a2125c 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -2923,6 +2923,14 @@ print Goodbye, World" parsedArgs.Errors.Verify( Diagnostic(ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)) + parsedArgs = DefaultParse({"/refout:ref.dll", "/link:b", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refonly", "/link:b", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1)) + parsedArgs = DefaultParse({"/refonly", "/target:module", "a.vb"}, baseDirectory) parsedArgs.Errors.Verify( Diagnostic(ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)) @@ -8188,6 +8196,10 @@ Public Class C Public Shared Sub Main() System.Console.Write(""Hello"") End Sub + ''' Private method + Private Shared Sub PrivateMethod() + System.Console.Write(""Private"") + End Sub End Class") Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) @@ -8202,7 +8214,7 @@ End Class") MetadataReaderUtils.VerifyPEMetadata(exe, {"TypeDefinition:", "TypeDefinition:C"}, - {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()"}, + {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()", "MethodDefinition:Void PrivateMethod()"}, {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} ) @@ -8222,6 +8234,9 @@ a Main method + + Private method + " Assert.Equal(expectedDoc, content.Trim()) @@ -8295,6 +8310,14 @@ outWriter.ToString().Trim()) Public Shared Sub Main() Bad() End Sub + ''' Field + Private Dim field As Integer + + ''' Field + Private Structure S + ''' Struct Field + Private Dim field As Integer + End Structure End Class") Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) @@ -8310,7 +8333,7 @@ End Class") ' The types and members that are included needs further refinement. ' See issue https://github.com/dotnet/roslyn/issues/17612 MetadataReaderUtils.VerifyPEMetadata(refDll, - {"TypeDefinition:", "TypeDefinition:C"}, + {"TypeDefinition:", "TypeDefinition:C", "TypeDefinition:S"}, {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()"}, {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute", "ReferenceAssemblyAttribute"} ) @@ -8334,6 +8357,15 @@ a Main method + + Field + + + Field + + + Struct Field + " Assert.Equal(expectedDoc, content.Trim()) diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb index f79bc3827226ad41b96d5c2a9937e5ecd0129d0e..a2939a18115fa4427a7ddfb1132f36a14581f3d1 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb @@ -886,6 +886,24 @@ End Class" End Using End Sub + + Public Sub RefAssembly_DisallowEmbeddingTypes() + Dim comp = CreateCompilation("", references:={MscorlibRef.WithEmbedInteropTypes(True)}, + options:=TestOptions.DebugDll) + + Using output As New MemoryStream() + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, + options:=EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False))) + End Using + + Using output As New MemoryStream() + Using metadataOutput = New MemoryStream() + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, metadataPEStream:=metadataOutput, + options:=EmitOptions.Default.WithIncludePrivateMembers(False))) + End Using + End Using + End Sub + Public Sub EmitMetadataOnly_DisallowMetadataPeStream() Dim comp = CreateCompilation("", references:={MscorlibRef},