about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-02-16 12:30:26 +0000
committerbors <bors@rust-lang.org>2023-02-16 12:30:26 +0000
commit4369a67a9b5654c016329fa34e1281fc920c2f33 (patch)
treec55c21429c2e641e33489be1439fec037042adab
parent86fb33a9738ae4793b136204e011a7cf1a7e255d (diff)
parent1b286b128b529d558f92f8dda39ae72759c3fa15 (diff)
downloadrust-4369a67a9b5654c016329fa34e1281fc920c2f33.tar.gz
rust-4369a67a9b5654c016329fa34e1281fc920c2f33.zip
Auto merge of #10163 - c410-f3r:lock-1, r=llogiq
[significant_drop_tightening] Add MVP

cc #9399

Creates the lint with minimum functionalities, which is a good start IMO.

---

changelog: new lint: [`significant_drop_tightening`]
[#10163](https://github.com/rust-lang/rust-clippy/pull/10163)
<!-- changelog_checked -->
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/lib.rs3
-rw-r--r--clippy_lints/src/significant_drop_tightening.rs366
-rw-r--r--tests/ui/significant_drop_tightening.fixed56
-rw-r--r--tests/ui/significant_drop_tightening.rs53
-rw-r--r--tests/ui/significant_drop_tightening.stderr71
7 files changed, 551 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30727beb8e7..d14a6b5d31e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4734,6 +4734,7 @@ Released 2018-09-13
 [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 [`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
+[`significant_drop_tightening`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_tightening
 [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 [`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
 [`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index c1feabf05aa..f543a2d3a1c 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -537,6 +537,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::shadow::SHADOW_REUSE_INFO,
     crate::shadow::SHADOW_SAME_INFO,
     crate::shadow::SHADOW_UNRELATED_INFO,
+    crate::significant_drop_tightening::SIGNIFICANT_DROP_TIGHTENING_INFO,
     crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
     crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
     crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 565c5b7af00..223e6698b72 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -2,6 +2,7 @@
 #![feature(binary_heap_into_iter_sorted)]
 #![feature(box_patterns)]
 #![feature(drain_filter)]
+#![feature(if_let_guard)]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(lint_reasons)]
@@ -263,6 +264,7 @@ mod semicolon_block;
 mod semicolon_if_nothing_returned;
 mod serde_api;
 mod shadow;
+mod significant_drop_tightening;
 mod single_char_lifetime_names;
 mod single_component_path_imports;
 mod size_of_in_element_count;
@@ -558,6 +560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
     store.register_late_pass(|_| Box::new(mut_mut::MutMut));
     store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
+    store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default());
     store.register_late_pass(|_| Box::new(len_zero::LenZero));
     store.register_late_pass(|_| Box::new(attrs::Attributes));
     store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs
new file mode 100644
index 00000000000..4cc85e38564
--- /dev/null
+++ b/clippy_lints/src/significant_drop_tightening.rs
@@ -0,0 +1,366 @@
+use crate::FxHashSet;
+use clippy_utils::{
+    diagnostics::span_lint_and_then,
+    get_attr,
+    source::{indent_of, snippet},
+};
+use rustc_errors::{Applicability, Diagnostic};
+use rustc_hir::{
+    self as hir,
+    intravisit::{walk_expr, Visitor},
+};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::ty::{subst::GenericArgKind, Ty, TypeAndMut};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{symbol::Ident, Span, DUMMY_SP};
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Searches for elements marked with `#[clippy::significant_drop]` that could be early
+    /// dropped but are in fact dropped at the end of their scopes. In other words, enforces the
+    /// "tightening" of their possible lifetimes.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Elements marked with `#[clippy::has_significant_drop]` are generally synchronizing
+    /// primitives that manage shared resources, as such, it is desired to release them as soon as
+    /// possible to avoid unnecessary resource contention.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore
+    /// fn main() {
+    ///   let lock = some_sync_resource.lock();
+    ///   let owned_rslt = lock.do_stuff_with_resource();
+    ///   // Only `owned_rslt` is needed but `lock` is still held.
+    ///   do_heavy_computation_that_takes_time(owned_rslt);
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    ///
+    /// ```rust,ignore
+    /// fn main() {
+    ///     let owned_rslt = some_sync_resource.lock().do_stuff_with_resource();
+    ///     do_heavy_computation_that_takes_time(owned_rslt);
+    /// }
+    /// ```
+    #[clippy::version = "1.67.0"]
+    pub SIGNIFICANT_DROP_TIGHTENING,
+    nursery,
+    "Searches for elements marked with `#[clippy::has_significant_drop]` that could be early dropped but are in fact dropped at the end of their scopes"
+}
+
+impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]);
+
+#[derive(Default)]
+pub struct SignificantDropTightening<'tcx> {
+    /// Auxiliary structure used to avoid having to verify the same type multiple times.
+    seen_types: FxHashSet<Ty<'tcx>>,
+}
+
+impl<'tcx> SignificantDropTightening<'tcx> {
+    /// Verifies if the expression is of type `drop(some_lock_path)` to assert that the temporary
+    /// is already being dropped before the end of its scope.
+    fn has_drop(expr: &'tcx hir::Expr<'_>, init_bind_ident: Ident) -> bool {
+        if let hir::ExprKind::Call(fun, args) = expr.kind
+            && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind
+            && let [fun_ident, ..] = fun_path.segments
+            && fun_ident.ident.name == rustc_span::sym::drop
+            && let [first_arg, ..] = args
+            && let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &first_arg.kind
+            && let [first_arg_ps, .. ] = arg_path.segments
+        {
+            first_arg_ps.ident == init_bind_ident
+        }
+        else {
+            false
+        }
+    }
+
+    /// Tries to find types marked with `#[has_significant_drop]` of an expression `expr` that is
+    /// originated from `stmt` and then performs common logic on `sdap`.
+    fn modify_sdap_if_sig_drop_exists(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        expr: &'tcx hir::Expr<'_>,
+        idx: usize,
+        sdap: &mut SigDropAuxParams,
+        stmt: &'tcx hir::Stmt<'_>,
+        cb: impl Fn(&mut SigDropAuxParams),
+    ) {
+        let mut sig_drop_finder = SigDropFinder::new(cx, &mut self.seen_types);
+        sig_drop_finder.visit_expr(expr);
+        if sig_drop_finder.has_sig_drop {
+            cb(sdap);
+            if sdap.number_of_stmts > 0 {
+                sdap.last_use_stmt_idx = idx;
+                sdap.last_use_stmt_span = stmt.span;
+                if let hir::ExprKind::MethodCall(_, _, _, span) = expr.kind {
+                    sdap.last_use_method_span = span;
+                }
+            }
+            sdap.number_of_stmts = sdap.number_of_stmts.wrapping_add(1);
+        }
+    }
+
+    /// Shows a generic overall message as well as specialized messages depending on the usage.
+    fn set_suggestions(cx: &LateContext<'tcx>, block_span: Span, diag: &mut Diagnostic, sdap: &SigDropAuxParams) {
+        match sdap.number_of_stmts {
+            0 | 1 => {},
+            2 => {
+                let indent = " ".repeat(indent_of(cx, sdap.last_use_stmt_span).unwrap_or(0));
+                let init_method = snippet(cx, sdap.init_method_span, "..");
+                let usage_method = snippet(cx, sdap.last_use_method_span, "..");
+                let stmt = if let Some(last_use_bind_span) = sdap.last_use_bind_span {
+                    format!(
+                        "\n{indent}let {} = {init_method}.{usage_method};",
+                        snippet(cx, last_use_bind_span, ".."),
+                    )
+                } else {
+                    format!("\n{indent}{init_method}.{usage_method};")
+                };
+                diag.span_suggestion_verbose(
+                    sdap.init_stmt_span,
+                    "merge the temporary construction with its single usage",
+                    stmt,
+                    Applicability::MaybeIncorrect,
+                );
+                diag.span_suggestion(
+                    sdap.last_use_stmt_span,
+                    "remove separated single usage",
+                    "",
+                    Applicability::MaybeIncorrect,
+                );
+            },
+            _ => {
+                diag.span_suggestion(
+                    sdap.last_use_stmt_span.shrink_to_hi(),
+                    "drop the temporary after the end of its last usage",
+                    format!(
+                        "\n{}drop({});",
+                        " ".repeat(indent_of(cx, sdap.last_use_stmt_span).unwrap_or(0)),
+                        sdap.init_bind_ident
+                    ),
+                    Applicability::MaybeIncorrect,
+                );
+            },
+        }
+        diag.note("this might lead to unnecessary resource contention");
+        diag.span_label(
+            block_span,
+            format!(
+                "temporary `{}` is currently being dropped at the end of its contained scope",
+                sdap.init_bind_ident
+            ),
+        );
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
+        let mut sdap = SigDropAuxParams::default();
+        for (idx, stmt) in block.stmts.iter().enumerate() {
+            match stmt.kind {
+                hir::StmtKind::Expr(expr) => self.modify_sdap_if_sig_drop_exists(
+                    cx,
+                    expr,
+                    idx,
+                    &mut sdap,
+                    stmt,
+                    |_| {}
+                ),
+                hir::StmtKind::Local(local) if let Some(expr) = local.init => self.modify_sdap_if_sig_drop_exists(
+                    cx,
+                    expr,
+                    idx,
+                    &mut sdap,
+                    stmt,
+                    |local_sdap| {
+                        if local_sdap.number_of_stmts == 0 {
+                            if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
+                                local_sdap.init_bind_ident = ident;
+                            }
+                            if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr.kind {
+                                local_sdap.init_method_span = local_expr.span.to(span);
+                            }
+                            local_sdap.init_stmt_span = stmt.span;
+                        }
+                        else if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
+                            local_sdap.last_use_bind_span = Some(ident.span);
+                        }
+                    }
+                ),
+                hir::StmtKind::Semi(expr) => {
+                    if Self::has_drop(expr, sdap.init_bind_ident) {
+                        return;
+                    }
+                    self.modify_sdap_if_sig_drop_exists(cx, expr, idx, &mut sdap, stmt, |_| {});
+                },
+                _ => continue
+            };
+        }
+        if sdap.number_of_stmts > 1 && {
+            let last_stmts_idx = block.stmts.len().wrapping_sub(1);
+            sdap.last_use_stmt_idx != last_stmts_idx
+        } {
+            span_lint_and_then(
+                cx,
+                SIGNIFICANT_DROP_TIGHTENING,
+                sdap.init_bind_ident.span,
+                "temporary with significant `Drop` can be early dropped",
+                |diag| {
+                    Self::set_suggestions(cx, block.span, diag, &sdap);
+                },
+            );
+        }
+    }
+}
+
+/// Auxiliary parameters used on each block check.
+struct SigDropAuxParams {
+    /// The binding or variable that references the initial construction of the type marked with
+    /// `#[has_significant_drop]`.
+    init_bind_ident: Ident,
+    /// Similar to `init_bind_ident` but encompasses the right-hand method call.
+    init_method_span: Span,
+    /// Similar to `init_bind_ident` but encompasses the whole contained statement.
+    init_stmt_span: Span,
+
+    /// The last visited binding or variable span within a block that had any referenced inner type
+    /// marked with `#[has_significant_drop]`.
+    last_use_bind_span: Option<Span>,
+    /// Index of the last visited statement within a block that had any referenced inner type
+    /// marked with `#[has_significant_drop]`.
+    last_use_stmt_idx: usize,
+    /// Similar to `last_use_bind_span` but encompasses the whole contained statement.
+    last_use_stmt_span: Span,
+    /// Similar to `last_use_bind_span` but encompasses the right-hand method call.
+    last_use_method_span: Span,
+
+    /// Total number of statements within a block that have any referenced inner type marked with
+    /// `#[has_significant_drop]`.
+    number_of_stmts: usize,
+}
+
+impl Default for SigDropAuxParams {
+    fn default() -> Self {
+        Self {
+            init_bind_ident: Ident::empty(),
+            init_method_span: DUMMY_SP,
+            init_stmt_span: DUMMY_SP,
+            last_use_bind_span: None,
+            last_use_method_span: DUMMY_SP,
+            last_use_stmt_idx: 0,
+            last_use_stmt_span: DUMMY_SP,
+            number_of_stmts: 0,
+        }
+    }
+}
+
+/// Checks the existence of the `#[has_significant_drop]` attribute
+struct SigDropChecker<'cx, 'sdt, 'tcx> {
+    cx: &'cx LateContext<'tcx>,
+    seen_types: &'sdt mut FxHashSet<Ty<'tcx>>,
+}
+
+impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> {
+    pub(crate) fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet<Ty<'tcx>>) -> Self {
+        seen_types.clear();
+        Self { cx, seen_types }
+    }
+
+    pub(crate) fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
+        if let Some(adt) = ty.ty_adt_def() {
+            let mut iter = get_attr(
+                self.cx.sess(),
+                self.cx.tcx.get_attrs_unchecked(adt.did()),
+                "has_significant_drop",
+            );
+            if iter.next().is_some() {
+                return true;
+            }
+        }
+        match ty.kind() {
+            rustc_middle::ty::Adt(a, b) => {
+                for f in a.all_fields() {
+                    let ty = f.ty(self.cx.tcx, b);
+                    if !self.has_seen_ty(ty) && self.has_sig_drop_attr(ty) {
+                        return true;
+                    }
+                }
+                for generic_arg in b.iter() {
+                    if let GenericArgKind::Type(ty) = generic_arg.unpack() {
+                        if self.has_sig_drop_attr(ty) {
+                            return true;
+                        }
+                    }
+                }
+                false
+            },
+            rustc_middle::ty::Array(ty, _)
+            | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
+            | rustc_middle::ty::Ref(_, ty, _)
+            | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty),
+            _ => false,
+        }
+    }
+
+    fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool {
+        !self.seen_types.insert(ty)
+    }
+}
+
+/// Performs recursive calls to find any inner type marked with `#[has_significant_drop]`.
+struct SigDropFinder<'cx, 'sdt, 'tcx> {
+    cx: &'cx LateContext<'tcx>,
+    has_sig_drop: bool,
+    sig_drop_checker: SigDropChecker<'cx, 'sdt, 'tcx>,
+}
+
+impl<'cx, 'sdt, 'tcx> SigDropFinder<'cx, 'sdt, 'tcx> {
+    fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet<Ty<'tcx>>) -> Self {
+        Self {
+            cx,
+            has_sig_drop: false,
+            sig_drop_checker: SigDropChecker::new(cx, seen_types),
+        }
+    }
+}
+
+impl<'cx, 'sdt, 'tcx> Visitor<'tcx> for SigDropFinder<'cx, 'sdt, 'tcx> {
+    fn visit_expr(&mut self, ex: &'tcx hir::Expr<'_>) {
+        if self
+            .sig_drop_checker
+            .has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex))
+        {
+            self.has_sig_drop = true;
+            return;
+        }
+
+        match ex.kind {
+            hir::ExprKind::MethodCall(_, expr, ..) => {
+                self.visit_expr(expr);
+            },
+            hir::ExprKind::Array(..)
+            | hir::ExprKind::Assign(..)
+            | hir::ExprKind::AssignOp(..)
+            | hir::ExprKind::Binary(..)
+            | hir::ExprKind::Box(..)
+            | hir::ExprKind::Call(..)
+            | hir::ExprKind::Field(..)
+            | hir::ExprKind::If(..)
+            | hir::ExprKind::Index(..)
+            | hir::ExprKind::Match(..)
+            | hir::ExprKind::Repeat(..)
+            | hir::ExprKind::Ret(..)
+            | hir::ExprKind::Tup(..)
+            | hir::ExprKind::Unary(..)
+            | hir::ExprKind::Yield(..) => {
+                walk_expr(self, ex);
+            },
+            _ => {},
+        }
+    }
+}
diff --git a/tests/ui/significant_drop_tightening.fixed b/tests/ui/significant_drop_tightening.fixed
new file mode 100644
index 00000000000..7d56a87053f
--- /dev/null
+++ b/tests/ui/significant_drop_tightening.fixed
@@ -0,0 +1,56 @@
+// run-rustfix
+
+#![warn(clippy::significant_drop_tightening)]
+
+use std::sync::Mutex;
+
+pub fn unnecessary_contention_with_multiple_owned_results() {
+    {
+        let mutex = Mutex::new(1i32);
+        let lock = mutex.lock().unwrap();
+        let _ = lock.abs();
+        let _ = lock.is_positive();
+    }
+
+    {
+        let mutex = Mutex::new(1i32);
+        let lock = mutex.lock().unwrap();
+        let rslt0 = lock.abs();
+        let rslt1 = lock.is_positive();
+        drop(lock);
+        do_heavy_computation_that_takes_time((rslt0, rslt1));
+    }
+}
+
+pub fn unnecessary_contention_with_single_owned_results() {
+    {
+        let mutex = Mutex::new(1i32);
+        let lock = mutex.lock().unwrap();
+        let _ = lock.abs();
+    }
+    {
+        let mutex = Mutex::new(vec![1i32]);
+        let mut lock = mutex.lock().unwrap();
+        lock.clear();
+    }
+
+    {
+        let mutex = Mutex::new(1i32);
+        
+        let rslt0 = mutex.lock().unwrap().abs();
+        
+        do_heavy_computation_that_takes_time(rslt0);
+    }
+    {
+        let mutex = Mutex::new(vec![1i32]);
+        
+        mutex.lock().unwrap().clear();
+        
+        do_heavy_computation_that_takes_time(());
+    }
+}
+
+// Marker used for illustration purposes.
+pub fn do_heavy_computation_that_takes_time<T>(_: T) {}
+
+fn main() {}
diff --git a/tests/ui/significant_drop_tightening.rs b/tests/ui/significant_drop_tightening.rs
new file mode 100644
index 00000000000..6f3ce328e5d
--- /dev/null
+++ b/tests/ui/significant_drop_tightening.rs
@@ -0,0 +1,53 @@
+// run-rustfix
+
+#![warn(clippy::significant_drop_tightening)]
+
+use std::sync::Mutex;
+
+pub fn unnecessary_contention_with_multiple_owned_results() {
+    {
+        let mutex = Mutex::new(1i32);
+        let lock = mutex.lock().unwrap();
+        let _ = lock.abs();
+        let _ = lock.is_positive();
+    }
+
+    {
+        let mutex = Mutex::new(1i32);
+        let lock = mutex.lock().unwrap();
+        let rslt0 = lock.abs();
+        let rslt1 = lock.is_positive();
+        do_heavy_computation_that_takes_time((rslt0, rslt1));
+    }
+}
+
+pub fn unnecessary_contention_with_single_owned_results() {
+    {
+        let mutex = Mutex::new(1i32);
+        let lock = mutex.lock().unwrap();
+        let _ = lock.abs();
+    }
+    {
+        let mutex = Mutex::new(vec![1i32]);
+        let mut lock = mutex.lock().unwrap();
+        lock.clear();
+    }
+
+    {
+        let mutex = Mutex::new(1i32);
+        let lock = mutex.lock().unwrap();
+        let rslt0 = lock.abs();
+        do_heavy_computation_that_takes_time(rslt0);
+    }
+    {
+        let mutex = Mutex::new(vec![1i32]);
+        let mut lock = mutex.lock().unwrap();
+        lock.clear();
+        do_heavy_computation_that_takes_time(());
+    }
+}
+
+// Marker used for illustration purposes.
+pub fn do_heavy_computation_that_takes_time<T>(_: T) {}
+
+fn main() {}
diff --git a/tests/ui/significant_drop_tightening.stderr b/tests/ui/significant_drop_tightening.stderr
new file mode 100644
index 00000000000..52754aac782
--- /dev/null
+++ b/tests/ui/significant_drop_tightening.stderr
@@ -0,0 +1,71 @@
+error: temporary with significant `Drop` can be early dropped
+  --> $DIR/significant_drop_tightening.rs:17:13
+   |
+LL | /     {
+LL | |         let mutex = Mutex::new(1i32);
+LL | |         let lock = mutex.lock().unwrap();
+   | |             ^^^^
+LL | |         let rslt0 = lock.abs();
+LL | |         let rslt1 = lock.is_positive();
+LL | |         do_heavy_computation_that_takes_time((rslt0, rslt1));
+LL | |     }
+   | |_____- temporary `lock` is currently being dropped at the end of its contained scope
+   |
+   = note: this might lead to unnecessary resource contention
+   = note: `-D clippy::significant-drop-tightening` implied by `-D warnings`
+help: drop the temporary after the end of its last usage
+   |
+LL ~         let rslt1 = lock.is_positive();
+LL +         drop(lock);
+   |
+
+error: temporary with significant `Drop` can be early dropped
+  --> $DIR/significant_drop_tightening.rs:38:13
+   |
+LL | /     {
+LL | |         let mutex = Mutex::new(1i32);
+LL | |         let lock = mutex.lock().unwrap();
+   | |             ^^^^
+LL | |         let rslt0 = lock.abs();
+LL | |         do_heavy_computation_that_takes_time(rslt0);
+LL | |     }
+   | |_____- temporary `lock` is currently being dropped at the end of its contained scope
+   |
+   = note: this might lead to unnecessary resource contention
+help: merge the temporary construction with its single usage
+   |
+LL ~         
+LL +         let rslt0 = mutex.lock().unwrap().abs();
+   |
+help: remove separated single usage
+   |
+LL -         let rslt0 = lock.abs();
+LL +         
+   |
+
+error: temporary with significant `Drop` can be early dropped
+  --> $DIR/significant_drop_tightening.rs:44:17
+   |
+LL | /     {
+LL | |         let mutex = Mutex::new(vec![1i32]);
+LL | |         let mut lock = mutex.lock().unwrap();
+   | |                 ^^^^
+LL | |         lock.clear();
+LL | |         do_heavy_computation_that_takes_time(());
+LL | |     }
+   | |_____- temporary `lock` is currently being dropped at the end of its contained scope
+   |
+   = note: this might lead to unnecessary resource contention
+help: merge the temporary construction with its single usage
+   |
+LL ~         
+LL +         mutex.lock().unwrap().clear();
+   |
+help: remove separated single usage
+   |
+LL -         lock.clear();
+LL +         
+   |
+
+error: aborting due to 3 previous errors
+