-
Notifications
You must be signed in to change notification settings - Fork 325
Add blog post about warning configuration and suppression #1184
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
Merged
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
a649150
Add blog post about warning configuration and suppression
lrytz 6a49bc5
Typo fixes by Julien
lrytz 21b91ca
Typo fixes by Séb
lrytz 0d2e71e
clarify \. in regex
lrytz fccfc60
Address feedback by Seth
lrytz 0f16652
change post date
lrytz 86855ef
typo
lrytz a2bb238
change date
lrytz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
199 changes: 199 additions & 0 deletions
199
_posts/2020-12-01-configuring-and-suppressing-warnings.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
--- | ||
layout: blog-detail | ||
post-type: blog | ||
by: Lukas Rytz | ||
title: "Configuring and suppressing warnings in Scala" | ||
isHighlight: true | ||
--- | ||
|
||
Scala 2.13.2 introduced the `-Wconf` compiler flag to globally configure reporting of warnings, and the `@nowarn` annotation to locally suppress them. This addition to Scala 2.13 proved very popular, so it was backported to 2.12 and just released in 2.12.13. Having more control over compiler warnings makes them a lot more valuable: | ||
|
||
* In projects where the build log shows a lot of warnings that are mostly ignored, new helpful warnings can easily go undetected. The new functionality can be used to clean up the build log with manageable efforts and potentially enable fatal warnings (`-Werror` in 2.13, `-Xfatal-warnings` in 2.12). This has happened for example in the Scala compiler and standard library projects in the past few months – thank you, [@NthPortal](https://github.com/NthPortal) and [@som-snytt](https://github.com/som-snytt)! | ||
* Projects that already use fatal warnings get better utilities to work around corner cases where a warning cannot be avoided. This can allow further `-Xlint` checks to be enabled. | ||
|
||
In this post we go through the mechanics of configuring warnings and also look at the new `@nowarn` annotation. | ||
|
||
## Warnings from the Scala compiler | ||
|
||
We start off by recapping some of the most common scenarios where the Scala compiler emits warnings. A first category is suspicious code that is likely the result of a programming mistake: | ||
|
||
```scala | ||
scala> 1 == "" | ||
^ | ||
warning: comparing values of types Int and String using `==` will always yield false | ||
|
||
scala> def f(x: Int) = { x; x } | ||
^ | ||
warning: a pure expression does nothing in statement position; | ||
multiline expressions might require enclosing parentheses | ||
|
||
scala> def f(x: Int): Unit = return x + 1 | ||
^ | ||
warning: enclosing method f has result type Unit: return value of type Int discarded | ||
``` | ||
|
||
Warnings about non-exhaustive pattern matches and uncheckable type arguments are also issued by default (note that exhaustivity warnings just received a big upgrade in [Scala 2.13.4](https://github.com/scala/scala/releases/tag/v2.13.4)): | ||
|
||
```scala | ||
scala> def get[T](o: Option[T]): T = o match { case Some(t) => t } | ||
^ | ||
warning: match may not be exhaustive. | ||
It would fail on the following input: None | ||
|
||
scala> def f(l: List[Any]) = l match { case l: List[Int] => l.sum; case _ => l.length } | ||
^ | ||
warning: non-variable type argument Int in type pattern List[Int] | ||
is unchecked since it is eliminated by erasure | ||
``` | ||
|
||
Annotating the scrutinee with `@unchecked` disables exhaustivity checking: `(o: @unchecked) match ...`. Similarly, unchecked warnings are not issued for annotated type arguments: `case l: List[Int @unchecked] => ...`. | ||
|
||
Warnings about using deprecated features or APIs are not issued individually by default, but counted and summarized. The same applies to feature warnings, which warn about using advanced language features that are not generally encouraged. | ||
|
||
```scala | ||
$> scalac Test.scala | ||
warning: 1 deprecation (since 2.13.0) | ||
warning: 1 deprecation (since 2.13.3) | ||
warning: 2 deprecations in total; re-run with -deprecation for details | ||
warning: 1 feature warning; re-run with -feature for details | ||
4 warnings | ||
``` | ||
|
||
Using `-deprecation` and `-feature` these warnings are reported individually. As we will see later, these two flags are shorthands for changing the `-Wconf` configurarion. | ||
|
||
```scala | ||
$> scalac Test.scala -deprecation -feature | ||
Test.scala:2: warning: method → in class ArrowAssoc is deprecated (since 2.13.0): Use `->` instead [...] | ||
def f() = 1 → 2 | ||
^ | ||
Test.scala:3: warning: Auto-application to `()` is deprecated [...] | ||
def g = f._1 | ||
^ | ||
Test.scala:4: warning: reflective access of structural type member method bar [...] | ||
def h(x: { def bar: Int }) = x.bar | ||
^ | ||
3 warnings | ||
``` | ||
|
||
The Scala compiler supports additional compile-time checks that are not enabled by default to identify potential programming errors or discouraged code patterns. These checks are enabled using compiler flags and result in additional warnings being issued: | ||
|
||
* `-Wunused` (`-Ywarn-unused` in 2.12) warns about unused entities, for example unused local variables or unused imports. Run `scalac -Wunused:help` (`-Ywarn-unused:help` in 2.12) for details. | ||
* `-Xlint` enables a number of additional checks, for example when a type argument is inferred to `Any`. See `scalac -Xlint:help` for details. | ||
* `scalac -W` (`-Y` in 2.12) lists a few additional warnings, such as `-Wdead-code` (`-Ywarn-dead-code` in 2.12). | ||
|
||
Finally, warnings can be globally disabled using `-nowarn` or turned into errors with `-Werror` (`-Xfatal-warnings` in 2.12). The new `-Wconf` compiler option allows for more fine-grained configuration. | ||
|
||
## Configuring warnings | ||
|
||
The `-Wconf` compiler option allows filtering compiler warnings and applying an action to messages matching the filter. For example, the default configuration | ||
|
||
``` | ||
-Wconf:cat=deprecation:ws,cat=feature:ws,cat=optimizer:ws | ||
``` | ||
|
||
defines that warnings with category `deprecation` should be summarized as a single warning (`ws`, which means `warning-summary`), and the same for feature and optimizer warnings. | ||
|
||
Running `scalac -Wconf:help` explains how to specify a configuration, but we take a detailed look in the following sections. Generally, the syntax is `-Wconf:<filters>:<action>,<filters>:<action>,...`. | ||
|
||
### Actions | ||
|
||
The `<action>` defines how warnings matching a filter are handled: | ||
|
||
* `error` / `e` reports them as errors. | ||
* `warning` / `w` reports them as warnings (this is the default). | ||
* `info` / `i` reports them without counting them as warnings, and without causing `-Werror` to fail. | ||
* `silent` / `s` ignores them. | ||
|
||
Like deprecations and feature warnings, a group of `warning`s and `info`s can be reported as a single summary (`warning-summary` / `ws` and `info-summary` / `is`). Specifying `-Wconf:cat=deprecation:w` overrides the default and reports every deprecation warning individually – this is exactly what the `-deprecation` flag does internally. | ||
|
||
Warnings and infos can be issued in verbose mode (`warning-verbose` / `wv` and `info-verbose` / `iv`). This displays additional information about the warning that is helpful for writing filters. For example: | ||
|
||
```scala | ||
$> scalac -Wconf:any:wv Test.scala | ||
Test.scala:4: warning: [deprecation @ my.app.C.f | origin=scala.Predef.ArrowAssoc.→ | version=2.13.0] method → in class ArrowAssoc is deprecated [...] | ||
def f() = 1 → 2 | ||
^ | ||
``` | ||
|
||
Here, the warning message includes the following additional information: | ||
|
||
* the warning category `deprecation` | ||
* the site where the warning is issued `my.app.C.f` | ||
* the origin of the deprecation `scala.Predef.ArrowAssoc.→` | ||
* the `since` version of the deprecation `2.13.0` | ||
|
||
### Filters | ||
|
||
The actions explained above are always applied to a set of warnings selected by a filter expression. The following filters are available: | ||
|
||
* `any` matches every message. | ||
* `cat=deprecation` filters according to the message category, for example deprecations (details below). | ||
* `msg=regex` applies if some part of the message matches the regex. | ||
* `site=my\.package\..*` filters on the site where the warning is triggered. The regex must match the entity's full name (`package.Class.method`). Note that `.` in a regex matches any character while `\.` matches a single period. | ||
* `src=src_managed/.*` filters warnings issued in a source file (details below). | ||
* Deprecation warnings can be filtered on two additional criteria: | ||
* `origin=external\.package\..*` filters on full name of the deprecated entity. | ||
* `since<1.24` filters on the `since` annotation argument of the deprecated entity (details below). | ||
|
||
Multiple filters can be combined using `&` to narrow down the selection. For example, the following configuration turns deprecation warnings for `scala.Predef` into errors: | ||
|
||
```scala | ||
$> scalac '-Wconf:cat=deprecation&origin=scala\.Predef\..*:e' Test.scala | ||
Test.scala:4: error: method → in class ArrowAssoc is deprecated [...] | ||
def f() = 1 → 2 | ||
^ | ||
1 error | ||
``` | ||
|
||
Note that the `-Wconf:...` compiler argument is between quotes (`'`) in the command line, which prevents the shell from interpreting characters like `&` or `*`. | ||
|
||
For some of the filters the syntax is not trivial, so we look at them in more detail. | ||
|
||
* **Message category**: Every message has a category that is displayed in verbose mode (`-Wconf:any:wv`). The `-Wconf:help` option displays the full list of available categories. For example, every `-Xlint` warning has its own category (`lint-infer-any`), the super-category `lint` matches all lint warnings. | ||
* **Source file**: By default, the source file filter is a regex that must match the file path relative to any path segment. For example, `b/.*Test.scala` matches `/a/b/XTest.scala` but not `/ab/Test.scala`. If the `-rootdir` compiler option is specified, the regex must match the file path relative to that root directory. | ||
* **Since version for deprecations**: In a `since<1.24` filter expression, valid operators are `<`, `=` and `>` and valid version numbers are `N`, `N.M` and `N.M.P`. Because the `since` annotation argument can contain arbitrary text, the first version number found in the text is used for the comparison, for example `1.2.3` in `@deprecated("", "some lib 1.2.3-foo")`. | ||
|
||
## Local warning suppression using `@nowarn` | ||
|
||
The [`@nowarn` annotation](https://www.scala-lang.org/api/current/scala/annotation/nowarn.html) allows suppressing warnings locally within a source file. It can be applied to method or class definitions, or to individual expressions using the ascription syntax `expression: @nowarn`. | ||
|
||
```scala | ||
scala> @deprecated def dpr = 0 | ||
def dpr: Int | ||
|
||
// don't issue any warnings for code in method `f` | ||
scala> @annotation.nowarn def f = { 1; dpr } | ||
def f: Int | ||
|
||
// don't issue the "a pure expression does nothing in statement position" warning | ||
scala> def f = { | ||
| 1: @annotation.nowarn | ||
| dpr | ||
| } | ||
^ | ||
warning: method dpr is deprecated | ||
def f: Int | ||
``` | ||
|
||
|
||
The `@nowarn` annotation has an optional value parameter to silence warnings selectively, where the syntax is the same as a filter expression of the `-Wconf` compiler option. | ||
|
||
```scala | ||
scala> @annotation.nowarn("msg=pure expression does nothing") def f = { 1; dpr } | ||
^ | ||
warning: method dpr is deprecated | ||
def f: Int | ||
``` | ||
|
||
To ensure that `@nowarn` annotations actually suppress warnings, enable `-Xlint:unused` or `-Wunused:nowarn`. With this option, the compiler checks that every `@nowarn` annoation suppresses at least one warning and issues a warning otherwise: | ||
lrytz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
``` | ||
scala> @annotation.nowarn def f = 1 | ||
^ | ||
warning: @nowarn annotation does not suppress any warnings | ||
def f: Int | ||
``` | ||
|
||
## Credits | ||
|
||
The `@nowarn` annotation is heavily inspired from the fantastic [silencer compiler plugin](https://github.com/ghik/silencer) by [Roman Janusz](https://github.com/ghik), so I thank Roman for inventing, implementing and maintaining this feature before it was adopted by the compiler. | ||
lrytz marked this conversation as resolved.
Show resolved
Hide resolved
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.