Lint against more useless #[must_use] attributes

This expands the existing `#[must_use]` check in `unused_attributes`
to lint against pretty much everything `#[must_use]` doesn't support.
Fixes #93906.
This commit is contained in:
Ruby Lazuli 2022-02-11 18:21:02 -06:00
parent 6499c5e7fc
commit 6dcf5d8fde
No known key found for this signature in database
GPG key ID: F7E7992881AB576D
6 changed files with 527 additions and 164 deletions

View file

@ -587,7 +587,6 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
// next call to `it.next()` retrieves the next character.
while let Some(c) = it.next() {
if c == '{' && *it.peek().unwrap_or(&'\0') != '{' {
#[must_use]
let mut eat_argument = || -> Option<String> {
let mut result = String::new();
// Format specifiers look like

View file

@ -1111,7 +1111,7 @@ impl CheckAttrVisitor<'_> {
}
/// Warns against some misuses of `#[must_use]`
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, _target: Target) -> bool {
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
let node = self.tcx.hir().get(hir_id);
if let Some(fn_node) = node.fn_kind() {
if let rustc_hir::IsAsync::Async = fn_node.asyncness() {
@ -1131,6 +1131,39 @@ impl CheckAttrVisitor<'_> {
}
}
if !matches!(
target,
Target::Fn
| Target::Enum
| Target::Struct
| Target::Union
| Target::Method(_)
| Target::ForeignFn
// `impl Trait` in return position can trip
// `unused_must_use` if `Trait` is marked as
// `#[must_use]`
| Target::Trait
) {
let article = match target {
Target::ExternCrate
| Target::OpaqueTy
| Target::Enum
| Target::Impl
| Target::Expression
| Target::Arm
| Target::AssocConst
| Target::AssocTy => "an",
_ => "a",
};
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
lint.build(&format!(
"`#[must_use]` has no effect when applied to {article} {target}"
))
.emit();
});
}
// For now, its always valid
true
}

View file

@ -71,6 +71,7 @@
//~^^ WARN this was previously accepted by the compiler
// see issue-43106-gating-of-rustc_deprecated.rs
#![must_use]
//~^ WARN `#[must_use]` has no effect
// see issue-43106-gating-of-stable.rs
// see issue-43106-gating-of-unstable.rs
// see issue-43106-gating-of-deprecated.rs
@ -597,17 +598,17 @@ mod deprecated {
#[deprecated] impl super::StructForDeprecated { }
}
#[must_use]
#[must_use] //~ WARN `#[must_use]` has no effect
mod must_use {
mod inner { #![must_use] }
mod inner { #![must_use] } //~ WARN `#[must_use]` has no effect
#[must_use] fn f() { }
#[must_use] struct S;
#[must_use] type T = S;
#[must_use] type T = S; //~ WARN `#[must_use]` has no effect
#[must_use] impl S { }
#[must_use] impl S { } //~ WARN `#[must_use]` has no effect
}
#[windows_subsystem = "windows"]

View file

@ -0,0 +1,125 @@
#![allow(dead_code, path_statements)]
#![deny(unused_attributes, unused_must_use)]
#![feature(asm_experimental_arch, stmt_expr_attributes, trait_alias)]
#[must_use] //~ ERROR `#[must_use]` has no effect
extern crate std as std2;
#[must_use] //~ ERROR `#[must_use]` has no effect
mod test_mod {}
#[must_use] //~ ERROR `#[must_use]` has no effect
use std::arch::global_asm;
#[must_use] //~ ERROR `#[must_use]` has no effect
const CONST: usize = 4;
#[must_use] //~ ERROR `#[must_use]` has no effect
#[no_mangle]
static STATIC: usize = 4;
#[must_use]
struct X;
#[must_use]
enum Y {
Z,
}
#[must_use]
union U {
unit: (),
}
#[must_use] //~ ERROR `#[must_use]` has no effect
impl U {
#[must_use]
fn method() -> i32 {
4
}
}
#[must_use]
#[no_mangle]
fn foo() -> i64 {
4
}
#[must_use] //~ ERROR `#[must_use]` has no effect
extern "Rust" {
#[link_name = "STATIC"]
#[must_use] //~ ERROR `#[must_use]` has no effect
static FOREIGN_STATIC: usize;
#[link_name = "foo"]
#[must_use]
fn foreign_foo() -> i64;
}
#[must_use] //~ ERROR unused attribute
global_asm!("");
#[must_use] //~ ERROR `#[must_use]` has no effect
type UseMe = ();
fn qux<#[must_use] T>(_: T) {} //~ ERROR `#[must_use]` has no effect
#[must_use]
trait Use {
#[must_use] //~ ERROR `#[must_use]` has no effect
const ASSOC_CONST: usize = 4;
#[must_use] //~ ERROR `#[must_use]` has no effect
type AssocTy;
#[must_use]
fn get_four(&self) -> usize {
4
}
}
#[must_use] //~ ERROR `#[must_use]` has no effect
impl Use for () {
type AssocTy = ();
}
#[must_use] //~ ERROR `#[must_use]` has no effect
trait Alias = Use;
#[must_use] //~ ERROR `#[must_use]` has no effect
macro_rules! cool_macro {
() => {
4
};
}
fn main() {
#[must_use] //~ ERROR `#[must_use]` has no effect
let x = || {};
x();
let x = #[must_use] //~ ERROR `#[must_use]` has no effect
|| {};
x();
X; //~ ERROR that must be used
Y::Z; //~ ERROR that must be used
U { unit: () }; //~ ERROR that must be used
U::method(); //~ ERROR that must be used
foo(); //~ ERROR that must be used
unsafe {
foreign_foo(); //~ ERROR that must be used
};
CONST;
STATIC;
unsafe { FOREIGN_STATIC };
cool_macro!();
qux(4);
().get_four(); //~ ERROR that must be used
match Some(4) {
#[must_use] //~ ERROR `#[must_use]` has no effect
Some(res) => res,
None => 0,
};
}

View file

@ -0,0 +1,175 @@
error: unused attribute `must_use`
--> $DIR/unused_attributes-must_use.rs:58:1
|
LL | #[must_use]
| ^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/unused_attributes-must_use.rs:2:9
|
LL | #![deny(unused_attributes, unused_must_use)]
| ^^^^^^^^^^^^^^^^^
note: the built-in attribute `must_use` will be ignored, since it's applied to the macro invocation `global_asm`
--> $DIR/unused_attributes-must_use.rs:59:1
|
LL | global_asm!("");
| ^^^^^^^^^^
error: `#[must_use]` has no effect when applied to an extern crate
--> $DIR/unused_attributes-must_use.rs:5:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a module
--> $DIR/unused_attributes-must_use.rs:8:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a use
--> $DIR/unused_attributes-must_use.rs:11:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a constant item
--> $DIR/unused_attributes-must_use.rs:14:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a static item
--> $DIR/unused_attributes-must_use.rs:16:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to an item
--> $DIR/unused_attributes-must_use.rs:33:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a foreign module
--> $DIR/unused_attributes-must_use.rs:47:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a type alias
--> $DIR/unused_attributes-must_use.rs:61:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a type parameter
--> $DIR/unused_attributes-must_use.rs:64:8
|
LL | fn qux<#[must_use] T>(_: T) {}
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to an item
--> $DIR/unused_attributes-must_use.rs:79:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a trait alias
--> $DIR/unused_attributes-must_use.rs:84:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a macro def
--> $DIR/unused_attributes-must_use.rs:87:1
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a statement
--> $DIR/unused_attributes-must_use.rs:95:5
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a closure
--> $DIR/unused_attributes-must_use.rs:99:13
|
LL | let x = #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to an match arm
--> $DIR/unused_attributes-must_use.rs:121:9
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to an associated const
--> $DIR/unused_attributes-must_use.rs:68:5
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to an associated type
--> $DIR/unused_attributes-must_use.rs:70:5
|
LL | #[must_use]
| ^^^^^^^^^^^
error: `#[must_use]` has no effect when applied to a foreign static item
--> $DIR/unused_attributes-must_use.rs:50:5
|
LL | #[must_use]
| ^^^^^^^^^^^
error: unused `X` that must be used
--> $DIR/unused_attributes-must_use.rs:103:5
|
LL | X;
| ^^
|
note: the lint level is defined here
--> $DIR/unused_attributes-must_use.rs:2:28
|
LL | #![deny(unused_attributes, unused_must_use)]
| ^^^^^^^^^^^^^^^
error: unused `Y` that must be used
--> $DIR/unused_attributes-must_use.rs:104:5
|
LL | Y::Z;
| ^^^^^
error: unused `U` that must be used
--> $DIR/unused_attributes-must_use.rs:105:5
|
LL | U { unit: () };
| ^^^^^^^^^^^^^^^
error: unused return value of `U::method` that must be used
--> $DIR/unused_attributes-must_use.rs:106:5
|
LL | U::method();
| ^^^^^^^^^^^^
error: unused return value of `foo` that must be used
--> $DIR/unused_attributes-must_use.rs:107:5
|
LL | foo();
| ^^^^^^
error: unused return value of `foreign_foo` that must be used
--> $DIR/unused_attributes-must_use.rs:110:9
|
LL | foreign_foo();
| ^^^^^^^^^^^^^^
error: unused return value of `Use::get_four` that must be used
--> $DIR/unused_attributes-must_use.rs:118:5
|
LL | ().get_four();
| ^^^^^^^^^^^^^^
error: aborting due to 26 previous errors