[ ] Figure out if there is ever a valid reason to suppress an error (and a warning?)
[ ] Create SPT tests
[ ] Implement
[ ] Add to documentation
[ ] Make it clear that the comment field in the suppression is optional but highly recommended.
This means that it should not just be mentioned in the description of that field, but also in the main description of suppressions.
Reason
Not every message can be fixed. In particular, notes may not be fixable. Having notes that have already been deemed unfixable is noise, so it would be good if these can be suppressed.
Description
Annotations to suppress messages. This may make it possible to add notes when code does not follow language convention: if you don't want that, you can simply suppress them (possibly project wide, with suppress { PROJECT, conventionBreak }). Example of a convention: PROJECT suppressions should have their own file at the top level directory of the project, FILE suppressions should be before the module statement.
Parameters:
CaptureCount (optional, option name can be omitted): the number of messages that this suppression can capture. If the suppression matches outside the range of CaptureCount, there is a warning on suppress. The default is SINGLE, i.e. each suppression only captures a single message. Options:
Name
sugar for
SINGLE
RANGE(1, 1)
OPT
RANGE(0, 1)
ALL
RANGE(1, inf)
ANY
RANGE(0, inf)
MIN(x: int)
RANGE(x, inf)
MAX(x: int\|inf)
RANGE(1, x)
MAXOPT(x: int\|inf)
RANGE(0, x)
EXACTLY(x: int)
RANGE(x, x)
RANGE(min: int, max: int\|inf)
-
MessageName (required, option name can be omitted): the name of the message to suppress. Message names form taxonomies, so you could e.g. use suppress { shadowing } to suppress all messages related to shadowing
match (optional): a filter that is matched against the text of messages. This suppression only matches messages for which the text matches. Takes a string ("hello") or a regex (/he..o/).
priority (optional): the priority of the suppression (any int). Higher priority suppressions capture messages earlier.
scope (optional): can be either PROJECT, FILE, MODULE, TARGET (default) or a lexical scope created by adding a second set of brackets to the annotation. The second set can be either just { and }, or {x| and |x}, where x must be a non-negative integer (opening and closing bracket must use the same integer). PROJECT applies the suppression to the whole project. FILE applies the suppression to the whole file. MODULE applies the suppression to the whole module and its nested modules. It does not (?) apply to pseudo-modules. TARGET is the default and applies the suppression to the element that the annotation is put on.
comment (optional, option name can be omitted): a comment describing why this message is suppressed. It is a string which does not require enclosing brackets.
If multiple suppressions with equal priorities match a message, it is captured by the suppression in the lexically closest (smallest) scope. If the outer suppression matches any messages not captured by another suppression, the inner suppression shows a warning (suppressed message is already captured in enclosing scope, see E15 below). If not, the outer suppression emits a warning (all matched messages are already captured by inner suppressions).
Warnings that are suppressed do not count for #158.
Example
module org:example:suppress_messages
// No notes unless specified in comment
data E1[E1] = foreign java Example {} // note on type parameter name E1.
data E2[suppress { shadowingTypeParameter, example to show how suppression on name works } E2] = foreign java Example {}
suppress { shadowingTypeParameter, example to show how suppression on data definition works } // annotation applies only to next definition, i.e. E3
data E3[E3] = foreign java Example {}
suppress { shadowingTypeParameter, example to show what happens if no message to suppress } // warning on `suppress`: "No shadowingTypeParameter messages to suppress. Remove this suppression or use option `OPT` to allow useless suppressions"
data E4[E] = foreign java Example {}
suppress { OPT, shadowingTypeParameter, example to show how optional suppression works }
data E5[E] = foreign java Example {}
suppress { shadowingTypeParameter, match: "E6", example to show how suppression with match works }
data E6[E6, E5] = foreign java Example {} // note on E5
suppress { shadowingTypeParameter, match: "E5", example to show how suppression with non-matching match works } // warning on `suppress`: "No messages to suppress. There were shadowingTypeParameter messages but they did not match "E5". Remove this suppression or use option `OPT` to allow useless suppressions"
data E7[E7, E6] = foreign java Example {} // note on type parameters E7, E6
suppress { shadowingTypeParameter, example to show what happens with multiple matching messages } // warning on `suppress`: "Multiple matching messages. Use option `ALL` to suppress them all, use a match to filter on the message text, or add a specific suppression for each message }
data E8[E8, E9] = foreign java Example {}
suppress { ALL, shadowingTypeParameter, example to show the ALL option }
data E9[E9, E8] = foreign java Example {}
suppress { shadowingTypeParameter } // no comment
data E10[E10] = foreign java Example {}
suppress { ALL, shadowingTypeParameter, example to show how suppression within a scope works}{
data E11[E11] = foreign java Example {}
data E12[E12] = foreign java Example {}
}
suppress { ALL, shadowingTypeParameter, example to show how custom brackets for annotation scope works}{1|
data E13[E13] = foreign java Example {}
data E14[E14] = foreign java Example {}
|1}
suppress { ALL, shadowingTypeParameter, example to show how suppression of something that was already suppressed works}{
suppress { shadowingTypeParameter } // applies only to E15. warning on `suppress`: "Matching shadowingTypeParameter message "[message]" is already suppressed in surrounding scope. Add a priority higher than 0 to override suppression in surrounding scope. Alternatively, remove this annotation." Warnings like these are only emitted when this suppression captures nothing that is not already captured by suppressions in higher scopes.
data E15[E15] = foreign java Example {}
data E16[E16] = foreign java Example {}
}
suppress { ALL, shadowingTypeParameter, example to show how suppression of something that was already suppressed works}{
suppress { shadowingTypeParameter, priority: 1 } // applies only to E17.
data E17[E17] = foreign java Example {}
data E18[E18] = foreign java Example {}
}
Implementation
I don't think most of this is possible in Statix right now. For the lexical scoping, add lexical syntax AnnotationClosingBracket[}] and [|[INT]}] which can be added at a variety of positions
Extensions
Create a way to view/filter all suppressions. Already possible by just searching for suppress, but that may turn up some false positives. Such a view could show the comments (i.e. reason) as well. Might be useful to find suppressions without comments.
Related issues
Annotations: #120
158 allows treating warnings as errors. Suppressions would allow ignoring certain warnings.
Fun fact
You can upgrade a note to a warning by doing suppress { EXACT(0), ... }, which will show a warning on this suppression if such a note is found. That could be useful in combination with #158 to fail the build for certain notes.
Summary Add annotations to suppress messages
Todo
Reason Not every message can be fixed. In particular, notes may not be fixable. Having notes that have already been deemed unfixable is noise, so it would be good if these can be suppressed.
suppress { PROJECT, conventionBreak }
). Example of a convention: PROJECT suppressions should have their own file at the top level directory of the project, FILE suppressions should be before the module statement. Parameters:CaptureCount
(optional, option name can be omitted): the number of messages that this suppression can capture. If the suppression matches outside the range ofCaptureCount
, there is a warning onsuppress
. The default isSINGLE
, i.e. each suppression only captures a single message. Options:SINGLE
OPT
ALL
ANY
MIN(x: int)
MAX(x: int\|inf)
MAXOPT(x: int\|inf)
EXACTLY(x: int)
RANGE(min: int, max: int\|inf)
MessageName
(required, option name can be omitted): the name of the message to suppress. Message names form taxonomies, so you could e.g. usesuppress { shadowing }
to suppress all messages related to shadowingmatch
(optional): a filter that is matched against the text of messages. This suppression only matches messages for which the text matches. Takes a string ("hello"
) or a regex (/he..o/
).priority
(optional): the priority of the suppression (any int). Higher priority suppressions capture messages earlier.scope
(optional): can be eitherPROJECT
,FILE
,MODULE
,TARGET
(default) or a lexical scope created by adding a second set of brackets to the annotation. The second set can be either just{
and}
, or{x|
and|x}
, wherex
must be a non-negative integer (opening and closing bracket must use the same integer).PROJECT
applies the suppression to the whole project.FILE
applies the suppression to the whole file.MODULE
applies the suppression to the whole module and its nested modules. It does not (?) apply to pseudo-modules.TARGET
is the default and applies the suppression to the element that the annotation is put on.comment
(optional, option name can be omitted): a comment describing why this message is suppressed. It is a string which does not require enclosing brackets.If multiple suppressions with equal priorities match a message, it is captured by the suppression in the lexically closest (smallest) scope. If the outer suppression matches any messages not captured by another suppression, the inner suppression shows a warning (suppressed message is already captured in enclosing scope, see E15 below). If not, the outer suppression emits a warning (all matched messages are already captured by inner suppressions).
Warnings that are suppressed do not count for #158.
Example
Implementation I don't think most of this is possible in Statix right now. For the lexical scoping, add lexical syntax
AnnotationClosingBracket
[}]
and[|[INT]}]
which can be added at a variety of positionsExtensions Create a way to view/filter all suppressions. Already possible by just searching for
suppress
, but that may turn up some false positives. Such a view could show the comments (i.e. reason) as well. Might be useful to find suppressions without comments.Related issues
158 allows treating warnings as errors. Suppressions would allow ignoring certain warnings.
Fun fact You can upgrade a note to a warning by doing
suppress { EXACT(0), ... }
, which will show a warning on this suppression if such a note is found. That could be useful in combination with #158 to fail the build for certain notes.