-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Do not skip emit if errors are suppressed #34174
Conversation
In the command line compilation we try to discover when to stop the compilation stages based on if an error is produced. If the error was produced by /warnaserror, then suppressed, this should not be considered a compilation-halting error. Fixes dotnet#34101
var filtered = Options.FilterDiagnostic(d); | ||
if (filtered == null || | ||
(!reportSuppressedDiagnostics && filtered.IsSuppressed)) | ||
{ | ||
continue; | ||
} | ||
else if (filtered.Severity == DiagnosticSeverity.Error) | ||
else if (filtered.Severity == DiagnosticSeverity.Error && | ||
!filtered.IsSuppressed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!filtered.IsSuppressed) [](start = 25, length = 23)
can errors be suppressed? I thought only warnings could.
I see that IsReportedError
also handles this combination (suppressed error) #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM Thanks (iteration 1) with some nits
@@ -434,6 +434,18 @@ internal bool ReportErrors(IEnumerable<Diagnostic> diagnostics, TextWriter conso | |||
private bool ReportErrors(DiagnosticBag diagnostics, TextWriter consoleOutput, ErrorLogger errorLoggerOpt) | |||
=> ReportErrors(diagnostics.ToReadOnly(), consoleOutput, errorLoggerOpt); | |||
|
|||
internal static bool HasUnsuppressedErrors(DiagnosticBag diagnostics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name threw me off here a bit. Felt like has not fake error. Consider a more positive term like HasReal/ReportedError.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suppressions may or may not be reported. A warning elevated to an error isn't a "real" error to me. This is the most accurate term I can think of.
@@ -95,6 +95,32 @@ private CSharpCommandLineArguments FullParse(string commandLine, string baseDire | |||
return CSharpCommandLineParser.Default.Parse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories); | |||
} | |||
|
|||
[ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.TestHasWindowsPaths)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is the windows path here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There technically isn't one, but I'm running an exe, which only works on Windows desktop and needed a reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -434,6 +434,18 @@ internal bool ReportErrors(IEnumerable<Diagnostic> diagnostics, TextWriter conso | |||
private bool ReportErrors(DiagnosticBag diagnostics, TextWriter consoleOutput, ErrorLogger errorLoggerOpt) | |||
=> ReportErrors(diagnostics.ToReadOnly(), consoleOutput, errorLoggerOpt); | |||
|
|||
internal static bool HasUnsuppressedErrors(DiagnosticBag diagnostics) | |||
{ | |||
foreach (var diag in diagnostics.AsEnumerableWithoutResolution()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AsEnumerableWithoutResolution [](start = 45, length = 29)
Why without resolution? What if we there is a lazy diagnostics that becomes an error after resolution? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that the next line causes resolution (checks severity) anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that the next line causes resolution (checks severity) anyways.
Then why pretend that we do not resolve?
In reply to: 266165592 [](ancestors = 266165592)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll let Andy respond, but my assumption in reading the PR was to avoid extra processing which AsEnumerable
does.
But I agree it's not obvious ;-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it was for perf and the places I was looking at it I thought it was safe because we always realized diagnostics later, but it's not particularly worth the risk. I've changed it to resolve.
var filtered = Options.FilterDiagnostic(d); | ||
if (filtered == null || | ||
(!reportSuppressedDiagnostics && filtered.IsSuppressed)) | ||
{ | ||
continue; | ||
} | ||
else if (filtered.Severity == DiagnosticSeverity.Error) | ||
else if (filtered.Severity == DiagnosticSeverity.Error && | ||
!filtered.IsSuppressed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!filtered.IsSuppressed [](start = 25, length = 22)
It looks like previous if
takes care of checking this. Shouldn't we respect the reportSuppressedDiagnostics
option? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are respecting the option. If that option is false we won't set hasErrors
, but we also won't add the diagnostic to the bag. In this case we don't set hasErrors
but we do add the diagnostic to the bag.
In other words, hasErrors
is used to mean "stop compilation" if true, but that's not what was happening. In this case there were errors, but that doesn't imply we should stop compilation.
And reporting is just a completely different option that controls whether or not the diagnostic is output to the log at all.
@@ -434,6 +434,18 @@ internal bool ReportErrors(IEnumerable<Diagnostic> diagnostics, TextWriter conso | |||
private bool ReportErrors(DiagnosticBag diagnostics, TextWriter consoleOutput, ErrorLogger errorLoggerOpt) | |||
=> ReportErrors(diagnostics.ToReadOnly(), consoleOutput, errorLoggerOpt); | |||
|
|||
internal static bool HasUnsuppressedErrors(DiagnosticBag diagnostics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HasUnsuppressedErrors [](start = 29, length = 21)
It looks like this method is supposed to be called after the filtering is applied to content of the bag. It doesn't look like this happens for every call site. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure it matters. Since you can't suppress an error that was originally an error, it shouldn't make a difference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlekseyTs Did this address your concern?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did this address your concern?
I do not think it did. If we haven't done filtering, we didn't have a chance to apply warn as error, correct?
In reply to: 266674162 [](ancestors = 266674162)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if it doesn't matter at many call sites, as you are saying. Then why adjusting those call sites?
In reply to: 266677762 [](ancestors = 266677762,266674162)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct. I guess I might be thinking of a different scenario: my intention was only to use this helper as a "bailout" mechanism to stop compilation, meaning that not catching errors as early as possible may affect how soon compilation stops but not affect semantic guarantees.
Did you find a place where I'm not doing that?
@@ -2578,7 +2581,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken) | |||
} | |||
} | |||
|
|||
if (diagnostics.HasAnyErrors()) | |||
if (CommonCompiler.HasUnsuppressedErrors(diagnostics)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CommonCompiler.HasUnsuppressedErrors(diagnostics) [](start = 16, length = 49)
Have we done filtering to get suppression state? #Closed
Done with review pass (iteration 1) #Closed |
Something to consider here: the diagnostic actually gets persisted to error log as an "error" that is "suppressed in source". I guess this matches the compiler implementation, but my mental model (and I'll bet I'm not alone) is that a warning was raised and suppressed before it could be elevated to an error. I would not expect "suppressed error" to be an observable state in the API or in the error log. |
@nguerrera That may be your mental model, but it's not really how it works. Suppression is an orthogonal boolean state that is applied or not applied. There's no temporality or ordering, it's just suppressed or not suppressed. You can't suppress diagnostics that have the default severity of error, but that's just an unrelated rule. |
I still think the way it works is counter-intuitive, and my confidence in betting that I'm not alone was based on:
I was further noting that since these "suppressed errors" actually show up as such in the log, there are user-facing consequences. In other words, we can't solve the confusion by choosing better method names because the confusion is not confined to the compiler implementation. This still looks wrong to me: "ruleId": "CS1591",
"level": "error",
"message": "Missing XML comment for publicly visible type or member 'P'",
"suppressionStates": [ "suppressedInSource" ], |
@nguerrera Nope, looks right to me. The severity was error, because that was what it was elevated to, and it was suppressed in source. |
@agocke By looks wrong, I meant it "feels" wrong. I fully understand that this is the design we have now. I just think it would be a much more intuitive design to say:
Than "you cannot suppress a diagnostic with default severity of error". |
If you put the code in a compilation and call GetFilteredDiagnostics, this is what you get out. It may not be the design we'd like to have gone with, but that ship has sailed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (iteration 5)
In the command line compilation we try to discover when to stop the
compilation stages based on if an error is produced. If the error was
produced by /warnaserror, then suppressed, this should not be considered
a compilation-halting error.
Fixes #34101