diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2c34fc13919bc..cb7a766d04934 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2017,7 +2017,7 @@ pub enum LocalSource { AsyncFn, /// A desugared `.await`. AwaitDesugar, - /// A desugared `expr = expr`, where the LHS is a tuple, struct or array. + /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression. /// The span is that of the `=` sign. AssignDesugar(Span), } diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 3eefd1b0e0833..bdace8e01f6c5 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -108,6 +108,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if !matches!(local.pat.kind, hir::PatKind::Wild) { return; } + + if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { + return; + } if let Some(init) = local.init { let init_ty = cx.typeck_results().expr_ty(init); // If the type has a trivial Drop implementation, then it doesn't @@ -126,6 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { suggestion: local.pat.span, multi_suggestion_start: local.span.until(init.span), multi_suggestion_end: init.span.shrink_to_hi(), + is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)), }; if is_sync_lock { let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index ca6408bdf3ddb..588b1541c4b70 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -950,6 +950,7 @@ pub struct NonBindingLetSub { pub suggestion: Span, pub multi_suggestion_start: Span, pub multi_suggestion_end: Span, + pub is_assign_desugar: bool, } impl AddToDiagnostic for NonBindingLetSub { @@ -960,10 +961,11 @@ impl AddToDiagnostic for NonBindingLetSub { rustc_errors::SubdiagnosticMessage, ) -> rustc_errors::SubdiagnosticMessage, { + let prefix = if self.is_assign_desugar { "let " } else { "" }; diag.span_suggestion_verbose( self.suggestion, fluent::lint_non_binding_let_suggestion, - "_unused", + format!("{prefix}_unused"), Applicability::MachineApplicable, ); diag.multipart_suggestion( diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs new file mode 100644 index 0000000000000..8e15b4da35a49 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs @@ -0,0 +1,21 @@ +// edition: 2021 + +#![deny(let_underscore_drop)] +fn main() { + let _ = foo(); //~ ERROR non-binding let on a type that implements `Drop` +} + +async fn from_config(_: Config) {} + +async fn foo() { + from_config(Config { + nickname: None, + ..Default::default() + }) + .await; +} + +#[derive(Default)] +struct Config { + nickname: Option>, +} diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr new file mode 100644 index 0000000000000..86e521580b87a --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr @@ -0,0 +1,22 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119696-err-on-fn.rs:5:5 + | +LL | let _ = foo(); + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/issue-119696-err-on-fn.rs:3:9 + | +LL | #![deny(let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^ +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = foo(); + | ~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(foo()); + | ~~~~~ + + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.rs b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs new file mode 100644 index 0000000000000..1dc80a123f613 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs @@ -0,0 +1,21 @@ +#![deny(let_underscore_drop)] +#![feature(type_alias_impl_trait)] + +pub struct Foo { + /// This type must have nontrivial drop glue + field: String, +} + +pub type Tait = impl Sized; + +pub fn ice_cold(beverage: Tait) { + // Must destructure at least one field of `Foo` + let Foo { field } = beverage; + // boom + _ = field; //~ ERROR non-binding let on a type that implements `Drop` + + let _ = field; //~ ERROR non-binding let on a type that implements `Drop` +} + + +pub fn main() {} diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr new file mode 100644 index 0000000000000..16df2c720eaf2 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr @@ -0,0 +1,37 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119697-extra-let.rs:15:5 + | +LL | _ = field; + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/issue-119697-extra-let.rs:1:9 + | +LL | #![deny(let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^ +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = field; + | ~~~~~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(field); + | ~~~~~ + + +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119697-extra-let.rs:17:5 + | +LL | let _ = field; + | ^^^^^^^^^^^^^^ + | +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = field; + | ~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(field); + | ~~~~~ + + +error: aborting due to 2 previous errors +