1 WARNING pragmas with categories¶
This proposal extends the syntax of WARNING pragmas to allow them to specify
a “warning category”. Users can then enable or disable specific
warning categories using command-line flags. This will make it more reasonable
to attach WARNING pragmas in cases where it is desirable to give users the
option to disable the warning individually.
1.1 Motivation¶
The recent core-libraries proposal #87, “Add {-#
WARNING #-} to Data.List.{head,tail}”, has lead to a great deal of debate. That
proposal seeks to discourage users from using certain partial Prelude
functions through the use of WARNING pragmas, which cause warnings to be
emitted when the functions are used. It has been controversial because, while
there is broad consensus that partial functions should usually be avoided,
there is less consensus that they should never be used.
A key problem with WARNING pragmas as they currently exist is that there is
no way to disable them selectively. It is possible to disable them entirely,
with -Wno-warnings-deprecations, but this will suppress all warnings from
WARNING pragmas, regardless of source. Thus if the Core Libraries Committee
decide to add a WARNING to partial functions, users who wish to continue
using them will either need to put up with warnings that they consider
unhelpful, suppress potentially-helpful warnings, or define their own
non-standard versions of the functions. This makes the change more
controversial than it should be, as it potentially inconveniences existing
users.
Instead, this ghc-proposal extends WARNING pragmas so they can be annotated
with a “warning category”. Users can then enable or disable these
categories individually. For example, it would allow the core-libraries
proposal to introduce:
{-# WARNING in "x-partial" head "This is a partial function, it throws an error on empty lists." #-}
{-# WARNING in "x-partial" tail "This is a partial function, it throws an error on empty lists." #-}
Such warnings are enabled by default, so the appropriate warning would be
displayed at any use of head or tail. However, users may control this
behaviour to their liking, prefixing the warning category name with an x- to
distinguish it from the built-in warning flags:
Users who prefer to use the functions unimpeded may specify
-Wno-x-partialto suppress all warnings annotated with thepartialwarning category, while still seeing other warnings.Users who do not wish to use the functions at all may specify
-Werror=x-partialso that any use of the functions will be reported as an error, while other warnings are unaffected.
1.2 Proposed Change Specification¶
A warning category is a string consisting of valid identifier characters or dashes.
A WARNING pragma may be immediately followed by the
inkeyword and a single warning category in double quotes. See below for details of the grammatical changes.Individual warning categories may be enabled or disabled using new
-W<category>or-Wno-<category>options, and their priority may be controlled using the-Werror=<category>or-Wwarn=<category>options.A
WARNINGpragma without a category, or aDEPRECATEDpragma, is interpreted as if it was aWARNINGpragma with the single categorydeprecationsspecified.The existing
-Wwarnings-deprecationswarning flag is interpreted as a synonym for-Wdeprecations(and similarly for-Wno-warnings-deprecations,-Werror=warnings-deprecationsand so on).A new warning flag
-Wextended-warningsswitches on warnings fromWARNINGpragmas regardless of category;-Wno-extended-warningsswitches them all off.A category is recognised if it either starts with
x-or isdeprecations. GHC will emit an error if aWARNINGpragma uses a category that is not recognised.
There is no change to the existing rules for when WARNING pragmas give rise
to warnings; these changes merely affect whether the warnings are displayed.
The command-line flags are processed from left to right, with later
flags overriding previous ones, as at present.
Points 3 and 4 make the proposal backwards compatible: existing WARNING or
DEPRECATED pragmas will still be controlled via the -Wdeprecations or
-Wwarnings-deprecations options. However if a library introduces a category
for a previously uncategorised warning, the warning will no longer be suppressed
by -Wno-deprecations. (Prior to this proposal, all WARNING and
DEPRECATED pragmas were treated uniformly and could be disabled either by
-Wno-deprecations or by -Wno-warnings-deprecations.)
Point 5 is not strictly necessary, but -Wno-extended-warnings will allow
users to suppress all WARNING messages much like
-Wno-warnings-deprecations does at present. The naming is chosen to allow
for other sources of “extended warnings” in the future.
Point 6 makes sure that the command line options -Wx-partial and -Wno-x-partial
can readily be distinguished from the existing large number of built-in warning
categories, such as -Wtabs, -Wdodgy-import, -Winaccessible-code etc. (See the
user manual section for a complete list).
This way GHC can still
report unrecognised warning flags, rather than silently accepting them.
1.2.1 Grammar of warning declarations¶
The grammar of declarations is extended as follows:
decl |
→ |
|
category |
→ |
{ small | large | digit | |
things |
→ |
thing1, …, thingN |
thing |
→ |
varid | conid |
strings |
→ |
string | |
The category can be omitted entirely, so this subsumes the existing
syntax for WARNING pragmas. If present,
the in keyword is followed by a single category as a double-quoted string.
Similarly the list of things is optional as it may be omitted (for a WARNING on a module header).
The strings may be a single string or a list (with the latter giving a
multi-line warning message). Since a module header may have a pragma with no
things, e.g. {-# WARNING "message" #-} or {-# WARNING ["message1", "message2"] #-},
we use the in keyword to indicate the presence of a warning category.
The category non-terminal subsumes both varid and conid, so it is
possible to use the name of the thing to which a warning is being attached as
the category, provided it is not an operator. The dash character (-) is permitted as a character
in addition to identifier characters, since dashes are frequently used in
warning names.
(The original version of this proposal used square brackets surrounding the
warning category, and did not put the category in quotes, but then it was not
obvious whether {-# WARNING [ should be followed by a category or a string,
and it was difficult to lex category names containing dashes.)
1.3 Examples¶
Suppose the definitions of head and nub are annotated with:
{-# WARNING in "x-partial" head "This is a partial function, it throws an error on empty lists." #-}
{-# WARNING in "x-quadratic" nub "The nub function has quadratic run-time complexity. If possible, use nubBy or nubOn." #-}
and the user program contains occurrences of both head and nub:
module M where
foo = head
bar = nub
This will result in the following warnings:
M.hs:2:7: warning: [-Wx-partial]
In the use of ‘head’ (imported from Prelude):
"This is a partial function, it throws an error on empty lists."
|
2 | foo = head
| ^^^^
M.hs:3:7: warning: [-Wx-quadratic]
In the use of ‘nub’ (imported from Prelude):
"The nub function has quadratic run-time complexity. If possible, use nubBy or nubOn."
|
3 | bar = nub
| ^^^
Notice that the message lists the warning category that applies. In current
versions of GHC, this displays -Wdeprecations.
The following examples show the effect of various combinations of warning flags:
Warning flags |
Result |
|---|---|
None |
Warnings displayed by default |
|
Warning for |
|
No warnings |
|
Warnings displayed (category is not |
Warning severity levels may be overridden by subsequent arguments on the
command-line. For example, -Wno-extended-warnings -Werror=x-partial
will result in errors instead of warnings with the category partial,
but no other warnings from WARNING or DEPRECATED pragmas. On the other
hand, -Werror=x-partial -Wno-extended-warnings will result in no
warnings because the second option overrides the first.
1.4 Effect and Interactions¶
This proposal should help resolve the controversy over whether head and
tail should be annotated with WARNING pragmas. By annotating them with
categorised warnings, users will be warned about their use by default, but may
choose to override the warnings as they wish.
This approach also provides an alternative to proposal #528, which is about
discouraging users from importing from “internal” modules, without completely
prohibiting their import. For example, a WARNING in "x-ghc-prim-internals"
pragma could be attached to all modules in ghc-prim. Users would then be
advised that such imports are discouraged, but could silence the warning with
-Wno-x-ghc-prim-internals.
These pragmas may be useful for libraries outside base as well, in
particular where library authors wish to selectively discourage use of certain
parts of their API.
1.5 Costs and Drawbacks¶
This is yet one more feature to implement, although the implementation cost should be fairly modest.
Overall this should make the language more accessible to newcomers, as library
authors will be able to use WARNING pragmas to discourage certain features
even if those warnings can be reasonably be disabled in some contexts.
This proposal does not provide a way to disable warnings at specific use sites, only at the module level. In some cases, it would be nice to be able to mark individual uses as having been approved and the warning suppressed for that use alone, rather than for all uses in the module.
It might be helpful to establish conventions around which categories exist, such
as x-partial for warnings about partial functions. These issues are currently
left to individual library authors.
This proposal does not provide a mechanism for organising or namespacing warning categories, as they are simply bare identifiers. Thus if libraries use the names of their functions as categories, the names cannot be qualified to distinguish definitions from separate modules.
1.6 Alternatives¶
1.6.1 Custom type warnings¶
This proposal may be contrasted with proposal #454, which introduces a
built-in constraint Warning that can be used for custom warnings along
similar lines to the existing support for custom type errors. That proposal
allows categorisation of warnings in a similar way, and moreover allows
type-level programming to control the presence and content of warnings, and the
suppression of warnings at individual use sites.
However, this proposal is simpler, and by keeping the WARNING annotations as
separate pragmas rather than requiring them to be part of the types, avoids the
risk that introducing Warning constraints may have unexpected effects on
program semantics. Assuming this proposal is accepted, it would be fairly
simple to change the Warning class to be controlled using the same flags.
1.6.2 Multiple categories¶
The current version of this proposal (like proposal #454) does not allow multiple categories to be attached to a single warning. It could be useful to include this feature, because it allows for multiple categories at different levels of granularity (potentially including a different category for every identifier).
Support for multiple categories it is omitted for now in the interests of
simplicity. It can lead to confusing effects, e.g. if a warning on head was
given both the head and partial categories, a user might specify
-Werror=head -Wno-partial.
It would be easy to change this later and allow a comma-separated list of warning categories.
1.6.3 Control over severity¶
Under this proposal, all warnings from WARNING pragmas are treated as
belonging to -Wdefault. One might imagine libraries wanting to customise
this, e.g. showing them only with -Wall or -Wcompat, or treating them as
errors with -Werror. This introduces more complexity, however. Ideally,
severity should be a property of the entire category, but there is no up-front
definition of categories.
A plausible alternative would be to indicate that
classification in the prefix (xw-, xe-, xi-), so that categories
starting with xe- are errors by default. This does not
currently seem worth the additional complexity.
1.6.4 Glob patterns¶
Users may wish to disable multiple related warning categories in one go. One way
to achieve this would be to support glob-style command-line flags such as
-Wno-x-partial*, which would disable all of the warning categories
x-partial, x-partial-foo, x-partial-bar and so on.
However, glob support is not part of the current proposal, in the interests of simplicity. If in the future categorised warnings become sufficiently widely used that glob support becomes necessary, this question can be revisited.
1.7 Unresolved Questions¶
None.
1.8 Implementation Plan¶
Support with the implementation of this proposal would be welcome.