1
Fork 0

Rollup merge of #126682 - Zalathar:coverage-attr, r=lcnr

coverage: Overhaul validation of the `#[coverage(..)]` attribute

This PR makes sweeping changes to how the (currently-unstable) coverage attribute is validated:
- Multiple coverage attributes on the same item/expression are now treated as an error.
- The attribute must always be `#[coverage(off)]` or `#[coverage(on)]`, and the error messages for this are more consistent.
  -  A trailing comma is still allowed after off/on, since that's part of the normal attribute syntax.
- Some places that silently ignored a coverage attribute now produce an error instead.
  - These cases were all clearly bugs.
- Some places that ignored a coverage attribute (with a warning) now produce an error instead.
  - These were originally added as lints, but I don't think it makes much sense to knowingly allow new attributes to be used in meaningless places.
  - Some of these errors might soon disappear, if it's easy to extend recursive coverage attributes to things like modules and impl blocks.

---

One of the goals of this PR is to lay a more solid foundation for making the coverage attribute recursive, so that it applies to all nested functions/closures instead of just the one it is directly attached to.

Fixes #126658.

This PR incorporates #126659, which adds more tests for validation of the coverage attribute.

`@rustbot` label +A-code-coverage
This commit is contained in:
Michael Goulet 2024-06-24 15:51:03 -04:00 committed by GitHub
commit 9ce2a070b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 706 additions and 377 deletions

View file

@ -103,18 +103,9 @@ passes_continue_labeled_block =
.label = labeled blocks cannot be `continue`'d
.block_label = labeled block the `continue` points to
passes_coverage_fn_defn =
`#[coverage]` may only be applied to function definitions
passes_coverage_ignored_function_prototype =
`#[coverage]` is ignored on function prototypes
passes_coverage_not_coverable =
`#[coverage]` must be applied to coverable code
.label = not coverable code
passes_coverage_propagate =
`#[coverage]` does not propagate into items and must be applied to the contained functions directly
passes_coverage_not_fn_or_closure =
attribute should be applied to a function definition or closure
.label = not a function or closure
passes_dead_codes =
{ $multiple ->

View file

@ -122,7 +122,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.check_diagnostic_on_unimplemented(attr.span, hir_id, target)
}
[sym::inline] => self.check_inline(hir_id, attr, span, target),
[sym::coverage] => self.check_coverage(hir_id, attr, span, target),
[sym::coverage] => self.check_coverage(attr, span, target),
[sym::non_exhaustive] => self.check_non_exhaustive(hir_id, attr, span, target),
[sym::marker] => self.check_marker(hir_id, attr, span, target),
[sym::target_feature] => {
@ -369,47 +369,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
/// Checks if a `#[coverage]` is applied directly to a function
fn check_coverage(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
/// Checks that `#[coverage(..)]` is applied to a function or closure.
fn check_coverage(&self, attr: &Attribute, span: Span, target: Target) -> bool {
match target {
// #[coverage] on function is fine
// #[coverage(..)] on function is fine
Target::Fn
| Target::Closure
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
// function prototypes can't be covered
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span,
errors::IgnoredCoverageFnProto,
);
true
}
Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span,
errors::IgnoredCoveragePropagate,
);
true
}
Target::Expression | Target::Statement | Target::Arm => {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span,
errors::IgnoredCoverageFnDefn,
);
true
}
_ => {
self.dcx().emit_err(errors::IgnoredCoverageNotCoverable {
self.dcx().emit_err(errors::CoverageNotFnOrClosure {
attr_span: attr.span,
defn_span: span,
});

View file

@ -60,21 +60,9 @@ pub struct InlineNotFnOrClosure {
pub defn_span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_coverage_ignored_function_prototype)]
pub struct IgnoredCoverageFnProto;
#[derive(LintDiagnostic)]
#[diag(passes_coverage_propagate)]
pub struct IgnoredCoveragePropagate;
#[derive(LintDiagnostic)]
#[diag(passes_coverage_fn_defn)]
pub struct IgnoredCoverageFnDefn;
#[derive(Diagnostic)]
#[diag(passes_coverage_not_coverable, code = E0788)]
pub struct IgnoredCoverageNotCoverable {
#[diag(passes_coverage_not_fn_or_closure, code = E0788)]
pub struct CoverageNotFnOrClosure {
#[primary_span]
pub attr_span: Span,
#[label]