From 5a259513a9bae12676ed5009c9d26b2d91c33ce2 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 13 Apr 2017 14:17:49 -0700 Subject: [PATCH] Filter "missing method body" error when emitting metadata-only (#18542) --- .../Portable/Compilation/CSharpCompilation.cs | 9 ++++- .../Test/Emit/Emit/CompilationEmitTests.cs | 35 +++++++++++++------ .../Core/Portable/Compilation/Compilation.cs | 9 +++-- .../Compilation/VisualBasicCompilation.vb | 2 +- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index fe5d5899819..915ff1af349 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2311,7 +2311,14 @@ internal override StrongNameKeys StrongNameKeys { // The diagnostics should include syntax and declaration errors. We insert these before calling Emitter.Emit, so that the emitter // does not attempt to emit if there are declaration errors (but we do insert all errors from method body binding...) - bool hasDeclarationErrors = !FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, true, cancellationToken)); + PooledHashSet excludeDiagnostics = null; + if (emitMetadataOnly) + { + excludeDiagnostics = PooledHashSet.GetInstance(); + excludeDiagnostics.Add((int)ErrorCode.ERR_ConcreteMissingBody); + } + bool hasDeclarationErrors = !FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, true, cancellationToken), excludeDiagnostics); + excludeDiagnostics?.Free(); // TODO (tomat): NoPIA: // EmbeddedSymbolManager.MarkAllDeferredSymbolsAsReferenced(this) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 69dc56b4444..22295584ff9 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -321,7 +321,6 @@ public void RefAssembly_ReferenceAssemblyAttributeAlsoInSource() [InlineData("public struct S { private int i; }", "public struct S { }", Match.Different)] [InlineData("private int i;", "", Match.RefOut)] [InlineData("public C() { }", "", Match.BothMetadataAndRefOut)] - //[InlineData("public int NoBody();", "public int NoBody() { }", Match.BothMetadataAndRefOut)] // PROTOTYPE(refout) Further refinement https://github.com/dotnet/roslyn/issues/17612 public void RefAssembly_InvariantToSomeChanges(string left, string right, Match expectedMatch) { string sourceTemplate = @" @@ -569,26 +568,40 @@ private static void VerifyRefAssemblyClient(string lib_cs, string source, Action [Theory] [InlineData("public int M() { error(); }", true)] - [InlineData("public int M() { error() }", false)] // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("public int M() { error() }", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("public int M();", true)] + [InlineData("public int M() { int Local(); }", true)] + [InlineData("public C();", true)] + [InlineData("~ C();", true)] [InlineData("public Error M() { return null; }", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("public static explicit operator C(int i);", true)] + [InlineData("public async Task M();", false)] + [InlineData("partial void M(); partial void M();", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 public void RefAssembly_IgnoresSomeDiagnostics(string change, bool expectSuccess) { string sourceTemplate = @" -public class C +using System.Threading.Tasks; +public partial class C { CHANGE } "; - string source = sourceTemplate.Replace("CHANGE", change); - string name = GetUniqueName(); - CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source), - options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + VerifyIgnoresDiagnostics(EmitOptions.Default.WithEmitMetadataOnly(false).WithTolerateErrors(false), success: false); + VerifyIgnoresDiagnostics(EmitOptions.Default.WithEmitMetadataOnly(true).WithTolerateErrors(false), success: expectSuccess); - using (var output = new MemoryStream()) + void VerifyIgnoresDiagnostics(EmitOptions emitOptions, bool success) { - var emitResult = comp1.Emit(output, options: EmitOptions.Default.WithEmitMetadataOnly(true)); - Assert.Equal(expectSuccess, emitResult.Success); - Assert.Equal(!expectSuccess, emitResult.Diagnostics.Any()); + string source = sourceTemplate.Replace("CHANGE", change); + string name = GetUniqueName(); + CSharpCompilation comp = CreateCompilationWithMscorlib(Parse(source), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + + using (var output = new MemoryStream()) + { + var emitResult = comp.Emit(output, options: emitOptions); + Assert.Equal(!success, emitResult.Diagnostics.HasAnyErrors()); + Assert.Equal(success, emitResult.Success); + } } } diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index a550001f4e3..d1d232aa1fa 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1151,7 +1151,7 @@ internal void CompleteCompilationEventQueue_NoLock() /// True if there were no errors or warnings-as-errors. internal bool FilterAndAppendAndFreeDiagnostics(DiagnosticBag accumulator, ref DiagnosticBag incoming) { - bool result = FilterAndAppendDiagnostics(accumulator, incoming.AsEnumerableWithoutResolution()); + bool result = FilterAndAppendDiagnostics(accumulator, incoming.AsEnumerableWithoutResolution(), exclude: null); incoming.Free(); incoming = null; return result; @@ -1161,13 +1161,18 @@ internal bool FilterAndAppendAndFreeDiagnostics(DiagnosticBag accumulator, ref D /// Filter out warnings based on the compiler options (/nowarn, /warn and /warnaserror) and the pragma warning directives. /// /// True when there is no error. - internal bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable incoming) + internal bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable incoming, HashSet exclude) { bool hasError = false; bool reportSuppressedDiagnostics = Options.ReportSuppressedDiagnostics; foreach (Diagnostic d in incoming) { + if (exclude?.Contains(d.Code) == true) + { + continue; + } + var filtered = Options.FilterDiagnostic(d); if (filtered == null || (!reportSuppressedDiagnostics && filtered.IsSuppressed)) diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index bbb44f49d5c..a0b37688456 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2221,7 +2221,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' The diagnostics should include syntax and declaration errors. We insert these before calling Emitter.Emit, so that we don't emit ' metadata if there are declaration errors or method body errors (but we do insert all errors from method body binding...) - Dim hasDeclarationErrors = Not FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, True, cancellationToken)) + Dim hasDeclarationErrors = Not FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, True, cancellationToken), exclude:=Nothing) Dim moduleBeingBuilt = DirectCast(moduleBuilder, PEModuleBuilder) -- GitLab