-
Notifications
You must be signed in to change notification settings - Fork 283
Rework warn-as-error option #4971
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
Rework warn-as-error option #4971
Conversation
Just commenting on the description rather than the implementation. I'm definitely in favour of no longer converting warnings to errors and instead having the option to fail on warnings. I don't think it makes sense to fail on warnings by default though. IMO the whole point of a "warning" as opposed to an "error" is that it isn't fatal by default, and if we have warnings that should fail the pipeline by default we should upgrade them to errors with ways to quickly fix/suppress them if necessary. I suspect based on other conversations that the underlying reason for wanting this is that Dafny currently has things it only conditionally flags as errors based on command, which we want to make warnings instead. For example, I'd suggest we try to tackle supporting these different use cases better more directly instead. Perhaps there should be a project file property indicating whether the Dafny code will only be used for verification and not execution, similar to the |
It's unclear to me how you are positioning warnings. It sounds to me like you're saying warnings are never fatal, which implies that they are allowed to proliferate in a codebase, which I think makes them more annoying than useful. I would like to position a warning as something that is fatal by default in CI, but not fatal during development. With this PR, warnings are effectively not fatal during development. You run Dafny code during development by calling I have considered letting
I disagree. I think even as a proof assistant you want to differentiate between an |
Okay I get where you're coming from: you want all warnings to always block committing changes to a project. I don't think that's the only valid definition of "warning" but I do like the idea of being that strict about it. Really it makes warnings "non-fatal errors" - ones that ultimately mean the program isn't valid but that don't block doing useful local development with it such as executing it. If we're going that route though, I think we should review all our warnings to look for ones that shouldn't block CI before we make failing on warnings the default. There are definitely ones that should not be interpreted as "non-fatal errors" and aren't necessarily quick to address. For example: "a forall statement with no bound variables is deprecated; use an 'assert by' statement instead". Similarly, although I think it's reasonable to make It might also be to have more severity levels: "warning" for things that may or may not be issues, "error" for definite problems that shouldn't be allowed in CI but don't block development, and "fatal" for things where the pipeline can't proceed any further. |
Minor quibble: I'd suggest |
What do you think is missing from the test-suite to complete that review?
One change that I forgot to list in the PR details is that the option
I think we should let these two behave the same, so a bodyless method will produce a warning whether you are executing or not, unless you annotate it with {:axiom}, similar to an
I'm not sure I understand what you're suggesting. The labels we have at our disposal through LSP are "error", "warning" and "information". I think the common way to map these to behavior are:
It's nicer for boolean options to have false be the default, so you can turn them on using just |
I did a quick scan myself - I just wanted to check for any warnings that were obviously unreasonable to block on, and the point about not warning on deprecation any more helps. The only one that gave me pause was the one in DafnyProject about the IDE only recognizing files named
Ah fair enough, having to add
Damn, didn't realize the LSP was prescriptive about that, never mind.
Sure, let's go with |
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.
Implementation looks great, just asking for a couple of tweaks.
I did realize that --succeed-on-warnings
will be confusing for dafny run
and dafny test
, since it's only the pipeline that is "succeeding" and not the process' exit code. I suggest --allow-warnings
instead, assuming it's just an easy search-and-replace.
@@ -179,11 +179,12 @@ public class CsharpSynthesizer { | |||
var methodName = method.GetCompileName(Options); | |||
|
|||
if (((Function)method).Ens.Count != 0) { | |||
compiler.Error(CompilerErrors.ErrorId.c_possibly_unsatisfied_postconditions, lastSynthesizedMethod.tok, |
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'd be happier if this stayed as an error, does it have to change?
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.
Previously errors did not block code from being generated. A current test is hitting this line but still generating tests and running those. This is the test: https://github.com/dafny-lang/dafny/blob/master/Source/IntegrationTests/TestFiles/LitTests/LitTest/DafnyTests/TestAttribute/TestAttribute.dfy#L5
You can see it expects
// CHECK: .*Error: Post-conditions on function Identity might be unsatisfied when synthesizing code for method mockUnsafe.*
but also expects the test runner to run:
// CHECK: .*_module.__default.FailingTestUsingMock.*
With this PR, errors block code from being generated (see the changes here and here). Previously, errors would block code from being downstream compiled and run, but code could still be emitted.
Because of errors being more blocking, the line you commented on needs to be a warning and the TestAttribute tests needs --succeed-on-warnings
, for it to still run. I think effect of this PR is more restrictive when it comes to hitting this line, because the test needs an extra flag before it passes, so it's an improvement. Whether you want this line to be an error and update TestAttribute.dfy
, I don't now but I'd leave that for another PR.
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.
Got it. I don't love downgrading the severity of the issue but we should still support the use case. I cut #5075 for follow up instead.
@@ -720,7 +720,7 @@ public enum IncludesModes { | |||
} | |||
|
|||
case "warningsAsErrors": |
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 release notes should mention that /warningsAsErrors
is changing behavior too.
public static bool OptionValuesImplied(Option option, object first, object second) { | ||
try { | ||
return !(bool)first || (bool)second; | ||
} catch (NullReferenceException) { |
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'd prefer not to swallow this as it shouldn't happen with released Dafny versions: AFAICT you can only be missing options in doo files if they were built with an older version of Dafny, which is explicitly blocked.
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.
Note that I return false
here, so it will prevent the doo file from being used.
It also sounds like you're saying that you'd like a version check when using Doo files, and that it's currently not there, otherwise I wouldn't have been able to hit this exception.
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.
No the version check totally exists! https://github.com/dafny-lang/dafny/blob/master/Source/DafnyCore/DooFile.cs#L129-L133. I think we're missing an integration test for that though - I recall writing one that depended on checking in a doo file with an older version, but I think we had to pull it out for some reason.
However that only kicks in when we bump the Dafny version number, which we usually don't do until just before releasing. So it's not hard to hit the NRE in development if you change the set of recorded options and have doo files lying around from before the change.
It might be worth defensively emitting a clearer error message, even if this would be an internal error that shouldn't happen on released Dafny versions. But I definitely don't want to silently return false since it would indicate something else has gone wrong.
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.
It might be worth defensively emitting a clearer error message, even if this would be an internal error that shouldn't happen on released Dafny versions. But I definitely don't want to silently return false since it would indicate something else has gone wrong.
I think I disagree, but I've made the change you suggested.
@@ -11,7 +11,7 @@ namespace Microsoft.Dafny; | |||
public interface ILegacyParseArguments { } | |||
|
|||
// TODO: Refactor so that non-errors (NOT_VERIFIED, DONT_PROCESS_FILES) don't result in non-zero exit codes | |||
public enum ExitValue { SUCCESS = 0, PREPROCESSING_ERROR, DAFNY_ERROR, COMPILE_ERROR, VERIFICATION_ERROR, FORMAT_ERROR } | |||
public enum ExitValue { SUCCESS = 0, PREPROCESSING_ERROR = 1, DAFNY_ERROR = 2, COMPILE_ERROR = 3, VERIFICATION_ERROR = 4, FORMAT_ERROR = 5 } |
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.
Ah does C# not guarantee this indexing by default?
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.
It does, but sometimes you have to know what the numbers are when looking at test output, so I found this easier to read. Also, I think when you're casting enums to numbers and exposing those numbers to your clients, it's better to set them explicitly, so they don't accidentally change when adding a new enum member in the middle.
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.
What happened here? it looks like goimports
didn't get applied, but CI should be catching the mismatch.
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.
O is this when it does not get applied? I couldn't judge by the changes. It looked to me like it was now suddenly applying the formatting :-D but apparently goimports really likes indentation.
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.
Made a change so this gets reverted.
@@ -1,4 +1,4 @@ | |||
// RUN: %testDafnyForEachCompiler --refresh-exit-code=0 "%s" -- --relax-definite-assignment --spill-translation --unicode-char:false |
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.
Why this change?
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.
Just cleanup. The test does not need that option.
@@ -6,19 +6,15 @@ git-issue-1151-more.dfy(13,5): Error: Abstract type ('_module.Opaque') cannot be | |||
|
|||
Dafny program verifier did not attempt verification | |||
git-issue-1151-more.dfy(13,5): Error: Abstract type ('_module.Opaque') cannot be compiled; perhaps make it a type synonym or use :extern. | |||
Wrote textual form of partial target program to git-issue-1151-more-java/git_issue_1151_more.java |
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.
Was it intentional to lose these outputs?
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. I decided in this PR that it's dangerous to generate target code when the Dafny compiler emits errors. Previously translation errors would only block downstream compilation and running.
A side-effect is that these partial target programs are no longer emitted. I think that partially translated programs are only useful for debugging the Dafny language, so I'm OK to lose that feature now and if someone needs it they can add a hidden developer option for it.
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.
Completely agree with that and was tempted to do this myself in the past, just didn't spot the connection - thanks for that change then!
docs/dev/news/4971.feat
Outdated
- Flip the behavior of `--warn-deprecation` and change the name to `--allow-deprecation`, so the default is now false, which is standard for boolean options. | ||
- When using `--allow-deprecation`, deprecated code is shown using tooltips in the IDE, and on the CLI when using `--show-tooltips`. | ||
- Replace the option `--warn-as-error` with the option `--succeed-on-warnings`. The new option, when false, the default value, causes Dafny to stop generating executable output and return a failure exit code, when warnings occur in the program. Contrary to the previous `--warn-as-error` option, warnings are always reported as warnings. | ||
- During development, users must use `dafny run --succeed-on-warnings` if they want to run their Dafny code when it contains warnings. |
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.
This is worth mentioning in the user guide as well somewhere.
@@ -233,7 +233,7 @@ implementation. | |||
|
|||
The options relevant to this command are | |||
- those relevant to the command-line itself | |||
- `--warn-as-errors` --- turn all warnings into errors, which alters [dafny's exit code](#sec-exit-codes) | |||
- `--allow-warnings` --- return a success [exit code](#sec-exit-codes), even when there are warnings |
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.
Not true though since dafny run
or dafny test
still could return a non-zero exit code.
Edit: missed that this was specific to dafny resolve
, disregard
The existing option
--warn-as-error
causes everything that would otherwise be a warning, to be recorded as an error. Because the Dafny compiler often uses the presence of errors to determine whether to continue, using--warn-as-error
causes compilation to potentially halt earlier when it's turned on. This can hide errors until warnings that occur earlier in the pipeline have been resolved. In addition, using this option makes it impossible for the user to see what was originally a warning instead of an error.Changes
--warn-as-error
with the option--succeed-on-warnings
. Besides having the opposite meaning, this option no longer turns warnings into errors, nor does it reduce the amount of diagnostics collected in compilation. When turned off, the default, any warnings will cause the CLI to return an error exit code and prevent the execution of the Dafny code. All forms of execution, whether directly executing it or through the creation of build artifacts such as generated code (cs/java/go/etc) or further compiled generated code (dll/jar/etc), are prevented.--succeed-on-warnings
causes a warning if used by a consumer that does not use it.--warn-deprecation=false
so that deprecation diagnostics are reported as information, which means they show up in the IDE--warn-deprecation
and change the name to--allow-deprecation
, so the default is now false, which is common for boolean options. It's slightly nicer for user to type--allow-deprecation
instead of--warn-deprecation=false
.Caveats
--succeed-on-warnings
to theirdafny run
invocation.--warn-as-error
--succeed-on-warnings
to allow them to still pass--succeed-on-warnings
, and do not want to migrate off of deprecated feature, they will have to use--allow-deprecation
. In the future we will introducedafny migrate
to enable use to automatically move off of deprecated features, allowing them to instantly upgrade to newer Dafny versions without having to use--allow-deprecation
or--succeed-on-warnings
.--warn-deprecation false
must be replaced with--allow-deprecation
How has this been tested?
By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.