]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_lint/src/let_underscore.rs
Rollup merge of #101423 - mkroening:hermit-warnings, r=sanxiyn
[rust.git] / compiler / rustc_lint / src / let_underscore.rs
index 4e4cedaeb782174713e3f57275d5a472861371cc..7e885e6c51aad42261b5da1eeb11ee618712875c 100644 (file)
@@ -1,10 +1,7 @@
 use crate::{LateContext, LateLintPass, LintContext};
-use rustc_errors::Applicability;
+use rustc_errors::{Applicability, LintDiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
-use rustc_middle::{
-    lint::LintDiagnosticBuilder,
-    ty::{self, subst::GenericArgKind, Ty},
-};
+use rustc_middle::ty;
 use rustc_span::Symbol;
 
 declare_lint! {
@@ -14,7 +11,7 @@
     /// scope.
     ///
     /// ### Example
-    /// ```rust
+    /// ```
     /// struct SomeStruct;
     /// impl Drop for SomeStruct {
     ///     fn drop(&mut self) {
@@ -23,6 +20,7 @@
     /// }
     ///
     /// fn main() {
+    ///    #[warn(let_underscore_drop)]
     ///     // SomeStuct is dropped immediately instead of at end of scope,
     ///     // so "Dropping SomeStruct" is printed before "end of main".
     ///     // The order of prints would be reversed if SomeStruct was bound to
@@ -31,6 +29,9 @@
     ///     println!("end of main");
     /// }
     /// ```
+    ///
+    /// {{produces}}
+    ///
     /// ### Explanation
     ///
     /// Statements which assign an expression to an underscore causes the
@@ -55,7 +56,7 @@
     /// of at end of scope, which is typically incorrect.
     ///
     /// ### Example
-    /// ```rust
+    /// ```compile_fail
     /// use std::sync::{Arc, Mutex};
     /// use std::thread;
     /// let data = Arc::new(Mutex::new(0));
@@ -69,6 +70,9 @@
     ///     *lock += 1;
     /// });
     /// ```
+    ///
+    /// {{produces}}
+    ///
     /// ### Explanation
     ///
     /// Statements which assign an expression to an underscore causes the
     /// calling `std::mem::drop` on the expression is clearer and helps convey
     /// intent.
     pub LET_UNDERSCORE_LOCK,
-    Warn,
+    Deny,
     "non-binding let on a synchronization lock"
 }
 
-declare_lint! {
-    /// The `let_underscore_must_use` lint checks for statements which don't bind
-    /// a `must_use` expression to anything, causing the lock to be released
-    /// immediately instead of at end of scope, which is typically incorrect.
-    ///
-    /// ### Example
-    /// ```rust
-    /// #[must_use]
-    /// struct SomeStruct;
-    ///
-    /// fn main() {
-    ///     // SomeStuct is dropped immediately instead of at end of scope.
-    ///     let _ = SomeStruct;
-    /// }
-    /// ```
-    /// ### Explanation
-    ///
-    /// Statements which assign an expression to an underscore causes the
-    /// expression to immediately drop. Usually, it's better to explicitly handle
-    /// the `must_use` expression.
-    pub LET_UNDERSCORE_MUST_USE,
-    Allow,
-    "non-binding let on a expression marked `must_use`"
-}
-
-declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_MUST_USE]);
+declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
 
-const SYNC_GUARD_PATHS: [&[&str]; 5] = [
-    &["std", "sync", "mutex", "MutexGuard"],
-    &["std", "sync", "rwlock", "RwLockReadGuard"],
-    &["std", "sync", "rwlock", "RwLockWriteGuard"],
-    &["parking_lot", "raw_mutex", "RawMutex"],
-    &["parking_lot", "raw_rwlock", "RawRwLock"],
+const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
+    rustc_span::sym::MutexGuard,
+    rustc_span::sym::RwLockReadGuard,
+    rustc_span::sym::RwLockWriteGuard,
 ];
 
 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
@@ -129,25 +106,29 @@ fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
         }
         if let Some(init) = local.init {
             let init_ty = cx.typeck_results().expr_ty(init);
-            let needs_drop = init_ty.needs_drop(cx.tcx, cx.param_env);
-            let is_sync_lock = init_ty.walk().any(|inner| match inner.unpack() {
-                GenericArgKind::Type(inner_ty) => {
-                    SYNC_GUARD_PATHS.iter().any(|guard_path| match inner_ty.kind() {
-                        ty::Adt(adt, _) => {
-                            let ty_path = cx.get_def_path(adt.did());
-                            guard_path.iter().map(|x| Symbol::intern(x)).eq(ty_path.iter().copied())
-                        }
-                        _ => false,
-                    })
-                }
-
-                GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-            });
-            let is_must_use_ty = is_must_use_ty(cx, cx.typeck_results().expr_ty(init));
-            let is_must_use_func_call = is_must_use_func_call(cx, init);
+            // If the type has a trivial Drop implementation, then it doesn't
+            // matter that we drop the value immediately.
+            if !init_ty.needs_drop(cx.tcx, cx.param_env) {
+                return;
+            }
+            let is_sync_lock = match init_ty.kind() {
+                ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
+                    .iter()
+                    .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
+                _ => false,
+            };
 
             if is_sync_lock {
-                cx.struct_span_lint(LET_UNDERSCORE_LOCK, local.span, |lint| {
+                let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
+                span.push_span_label(
+                    local.pat.span,
+                    "this lock is not assigned to a binding and is immediately dropped".to_string(),
+                );
+                span.push_span_label(
+                    init.span,
+                    "this binding will immediately drop the value assigned to it".to_string(),
+                );
+                cx.struct_span_lint(LET_UNDERSCORE_LOCK, span, |lint| {
                     build_and_emit_lint(
                         lint,
                         local,
@@ -155,16 +136,7 @@ fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
                         "non-binding let on a synchronization lock",
                     )
                 })
-            } else if is_must_use_ty || is_must_use_func_call {
-                cx.struct_span_lint(LET_UNDERSCORE_MUST_USE, local.span, |lint| {
-                    build_and_emit_lint(
-                        lint,
-                        local,
-                        init.span,
-                        "non-binding let on a expression marked `must_use`",
-                    );
-                })
-            } else if needs_drop {
+            } else {
                 cx.struct_span_lint(LET_UNDERSCORE_DROP, local.span, |lint| {
                     build_and_emit_lint(
                         lint,
@@ -175,96 +147,29 @@ fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
                 })
             }
         }
-
-        fn build_and_emit_lint(
-            lint: LintDiagnosticBuilder<'_, ()>,
-            local: &hir::Local<'_>,
-            init_span: rustc_span::Span,
-            msg: &str,
-        ) {
-            lint.build(msg)
-                .span_suggestion_verbose(
-                    local.pat.span,
-                    "consider binding to an unused variable",
-                    "_unused",
-                    Applicability::MachineApplicable,
-                )
-                .span_suggestion_verbose(
-                    init_span,
-                    "consider explicitly droping with `std::mem::drop`",
-                    "drop(...)",
-                    Applicability::HasPlaceholders,
-                )
-                .emit();
-        }
-
-        // return true if `ty` is a type that is marked as `must_use`
-        fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-            match ty.kind() {
-                ty::Adt(adt, _) => has_must_use_attr(cx, adt.did()),
-                ty::Foreign(ref did) => has_must_use_attr(cx, *did),
-                ty::Slice(ty)
-                | ty::Array(ty, _)
-                | ty::RawPtr(ty::TypeAndMut { ty, .. })
-                | ty::Ref(_, ty, _) => {
-                    // for the Array case we don't need to care for the len == 0 case
-                    // because we don't want to lint functions returning empty arrays
-                    is_must_use_ty(cx, *ty)
-                }
-                ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
-                ty::Opaque(ref def_id, _) => {
-                    for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
-                        if let ty::PredicateKind::Trait(trait_predicate) =
-                            predicate.kind().skip_binder()
-                        {
-                            if has_must_use_attr(cx, trait_predicate.trait_ref.def_id) {
-                                return true;
-                            }
-                        }
-                    }
-                    false
-                }
-                ty::Dynamic(binder, _) => {
-                    for predicate in binder.iter() {
-                        if let ty::ExistentialPredicate::Trait(ref trait_ref) =
-                            predicate.skip_binder()
-                        {
-                            if has_must_use_attr(cx, trait_ref.def_id) {
-                                return true;
-                            }
-                        }
-                    }
-                    false
-                }
-                _ => false,
-            }
-        }
-
-        // check if expr is calling method or function with #[must_use] attribute
-        fn is_must_use_func_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
-            let did = match expr.kind {
-                hir::ExprKind::Call(path, _) if let hir::ExprKind::Path(ref qpath) = path.kind => {
-                    if let hir::def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id) {
-                        Some(did)
-                    } else {
-                        None
-                    }
-                },
-                hir::ExprKind::MethodCall(..) => {
-                    cx.typeck_results().type_dependent_def_id(expr.hir_id)
-                }
-                _ => None,
-            };
-
-            did.map_or(false, |did| has_must_use_attr(cx, did))
-        }
-
-        // returns true if DefId contains a `#[must_use]` attribute
-        fn has_must_use_attr(cx: &LateContext<'_>, did: hir::def_id::DefId) -> bool {
-            cx.tcx
-                .get_attrs(did, rustc_span::sym::must_use)
-                .find(|a| a.has_name(rustc_span::sym::must_use))
-                .is_some()
-        }
     }
 }
+
+fn build_and_emit_lint(
+    lint: LintDiagnosticBuilder<'_, ()>,
+    local: &hir::Local<'_>,
+    init_span: rustc_span::Span,
+    msg: &str,
+) {
+    lint.build(msg)
+        .span_suggestion_verbose(
+            local.pat.span,
+            "consider binding to an unused variable to avoid immediately dropping the value",
+            "_unused",
+            Applicability::MachineApplicable,
+        )
+        .multipart_suggestion(
+            "consider immediately dropping the value",
+            vec![
+                (local.span.until(init_span), "drop(".to_string()),
+                (init_span.shrink_to_hi(), ")".to_string()),
+            ],
+            Applicability::MachineApplicable,
+        )
+        .emit();
+}