Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
smoelius committed Aug 30, 2022
1 parent 75ea471 commit 414add3
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 52 deletions.
28 changes: 22 additions & 6 deletions clippy_lints/src/dereference.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::mir::{dropped_without_further_use, enclosing_mir, expr_local, local_assignments};
use clippy_utils::mir::{
dropped_without_further_use, enclosing_mir, expr_local, local_assignments, PossibleBorrowerMap,
};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
Expand Down Expand Up @@ -1059,14 +1061,19 @@ fn needless_borrow_impl_arg_position<'tcx>(
return Position::Other(precedence);
}

let mut possible_borrower = None;

// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
// elements are modified each time `check_referent` is called.
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();

let mut check_reference_and_referent = |reference, referent| {
let referent_ty = cx.typeck_results().expr_ty(referent);

if !(is_copy(cx, referent_ty) || referent_dropped_without_further_use(cx.tcx, reference)) {
if !is_copy(cx, referent_ty)
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
|| !referent_dropped_without_further_use(cx, &mut possible_borrower, reference))
{
return false;
}

Expand Down Expand Up @@ -1136,15 +1143,24 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
})
}

fn referent_dropped_without_further_use(tcx: TyCtxt<'_>, reference: &Expr<'_>) -> bool {
let mir = enclosing_mir(tcx, reference.hir_id);
if let Some(local) = expr_local(tcx, reference)
fn referent_dropped_without_further_use<'a, 'tcx>(
cx: &'a LateContext<'tcx>,
possible_borrower: &mut Option<PossibleBorrowerMap<'a, 'tcx>>,
reference: &Expr<'tcx>,
) -> bool {
let mir = enclosing_mir(cx.tcx, reference.hir_id);
if let Some(local) = expr_local(cx.tcx, reference)
&& let [location] = *local_assignments(mir, local).as_slice()
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) =
mir.basic_blocks()[location.block].statements[location.statement_index].kind
&& !place.has_deref()
{
dropped_without_further_use(mir, place.local, location).unwrap_or(false)
let possible_borrower = possible_borrower.get_or_insert_with(|| PossibleBorrowerMap::new(cx, mir));
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
// itself. See the comment in that method for an explanation as to why.
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
&& dropped_without_further_use(mir, place.local, location).unwrap_or(false)
} else {
false
}
Expand Down
1 change: 0 additions & 1 deletion clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ extern crate rustc_infer;
extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_mir_dataflow;
extern crate rustc_parse;
extern crate rustc_parse_format;
extern crate rustc_session;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
use clippy_utils::mir::{visit_local_usage, LocalUsage};
use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
Expand All @@ -8,24 +8,12 @@ use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{def_id, Body, FnDecl, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::{self, visit::Visitor as _};
use rustc_middle::mir;
use rustc_middle::ty::{self, Ty};
use rustc_mir_dataflow::Analysis;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{BytePos, Span};
use rustc_span::sym;

mod maybe_storage_live;
use maybe_storage_live::MaybeStorageLive;

mod possible_borrower;
use possible_borrower::PossibleBorrowerVisitor;

mod possible_origin;
use possible_origin::PossibleOriginVisitor;

mod transitive_relation;

macro_rules! unwrap_or_continue {
($x:expr) => {
match $x {
Expand Down Expand Up @@ -94,21 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {

let mir = cx.tcx.optimized_mir(def_id.to_def_id());

let possible_origin = {
let mut vis = PossibleOriginVisitor::new(mir);
vis.visit_body(mir);
vis.into_map(cx)
};
let maybe_storage_live_result = MaybeStorageLive
.into_engine(cx.tcx, mir)
.pass_name("redundant_clone")
.iterate_to_fixpoint()
.into_results_cursor(mir);
let mut possible_borrower = {
let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
vis.visit_body(mir);
vis.into_map(cx, maybe_storage_live_result)
};
let mut possible_borrower = PossibleBorrowerMap::new(cx, mir);

for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
let terminator = bbdata.terminator();
Expand Down
2 changes: 2 additions & 0 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ extern crate rustc_attr;
extern crate rustc_data_structures;
extern crate rustc_errors;
extern crate rustc_hir;
extern crate rustc_index;
extern crate rustc_infer;
extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_mir_dataflow;
extern crate rustc_parse_format;
extern crate rustc_session;
extern crate rustc_span;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnaly

/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
#[derive(Copy, Clone)]
pub struct MaybeStorageLive;
pub(super) struct MaybeStorageLive;

impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
type Domain = BitSet<mir::Local>;
Expand Down
9 changes: 9 additions & 0 deletions clippy_utils/src/mir.rs → clippy_utils/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC
use rustc_middle::mir::{traversal, Body, Local, Location, Place, StatementKind};
use rustc_middle::ty::TyCtxt;

mod maybe_storage_live;

mod possible_borrower;
pub use possible_borrower::PossibleBorrowerMap;

mod possible_origin;

mod transitive_relation;

#[derive(Clone, Default)]
pub struct LocalUsage {
/// The first location where the local is used, if any.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
use super::{maybe_storage_live::MaybeStorageLive, transitive_relation::TransitiveRelation};
use clippy_utils::ty::is_copy;
use super::{
maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor,
transitive_relation::TransitiveRelation,
};
use crate::ty::is_copy;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::{BitSet, HybridBitSet};
use rustc_lint::LateContext;
use rustc_middle::mir::{self, Mutability};
use rustc_middle::mir::{self, visit::Visitor as _, Mutability};
use rustc_middle::ty::{self, visit::TypeVisitor};
use rustc_mir_dataflow::ResultsCursor;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
use std::ops::ControlFlow;

/// Collects the possible borrowers of each local.
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
/// possible borrowers of `a`.
#[allow(clippy::module_name_repetitions)]
pub struct PossibleBorrowerVisitor<'a, 'tcx> {
struct PossibleBorrowerVisitor<'a, 'tcx> {
possible_borrower: TransitiveRelation,
body: &'a mir::Body<'tcx>,
cx: &'a LateContext<'tcx>,
possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
}

impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
pub fn new(
fn new(
cx: &'a LateContext<'tcx>,
body: &'a mir::Body<'tcx>,
possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
Expand All @@ -33,10 +36,10 @@ impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
}
}

pub fn into_map(
fn into_map(
self,
cx: &LateContext<'tcx>,
maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>,
maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
) -> PossibleBorrowerMap<'a, 'tcx> {
let mut map = FxHashMap::default();
for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
Expand Down Expand Up @@ -172,9 +175,37 @@ pub struct PossibleBorrowerMap<'a, 'tcx> {
bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
}

impl PossibleBorrowerMap<'_, '_> {
impl<'a, 'tcx> PossibleBorrowerMap<'a, 'tcx> {
pub fn new(cx: &'a LateContext<'tcx>, mir: &'a mir::Body<'tcx>) -> Self {
let possible_origin = {
let mut vis = PossibleOriginVisitor::new(mir);
vis.visit_body(mir);
vis.into_map(cx)
};
let maybe_storage_live_result = MaybeStorageLive
.into_engine(cx.tcx, mir)
.pass_name("redundant_clone")
.iterate_to_fixpoint()
.into_results_cursor(mir);
let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
vis.visit_body(mir);
vis.into_map(cx, maybe_storage_live_result)
}

/// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
self.bounded_borrowers(borrowers, borrowers, borrowed, at)
}

/// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below`
/// but no more than `above`.
pub fn bounded_borrowers(
&mut self,
below: &[mir::Local],
above: &[mir::Local],
borrowed: mir::Local,
at: mir::Location,
) -> bool {
self.maybe_live.seek_after_primary_effect(at);

self.bitset.0.clear();
Expand All @@ -188,11 +219,19 @@ impl PossibleBorrowerMap<'_, '_> {
}

self.bitset.1.clear();
for b in borrowers {
for b in below {
self.bitset.1.insert(*b);
}

self.bitset.0 == self.bitset.1
if !self.bitset.0.superset(&self.bitset.1) {
return false;
}

for b in above {
self.bitset.0.remove(*b);
}

self.bitset.0.is_empty()
}

pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::transitive_relation::TransitiveRelation;
use clippy_utils::ty::is_copy;
use crate::ty::is_copy;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::HybridBitSet;
use rustc_lint::LateContext;
Expand All @@ -9,7 +9,7 @@ use rustc_middle::mir;
/// For example, `_1 = &mut _2` generate _1: {_2,...}
/// Known Problems: not sure all borrowed are tracked
#[allow(clippy::module_name_repetitions)]
pub struct PossibleOriginVisitor<'a, 'tcx> {
pub(super) struct PossibleOriginVisitor<'a, 'tcx> {
possible_origin: TransitiveRelation,
body: &'a mir::Body<'tcx>,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use rustc_index::bit_set::HybridBitSet;
use rustc_middle::mir;

#[derive(Default)]
pub struct TransitiveRelation {
pub(super) struct TransitiveRelation {
relations: FxHashMap<mir::Local, Vec<mir::Local>>,
}

Expand Down
21 changes: 21 additions & 0 deletions tests/ui/needless_borrow.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -311,5 +311,26 @@ fn closure_test() {
let _ = std::fs::write("x", arg);
let _ = std::fs::write("x", loc);
};
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
f(arg);
}

#[allow(dead_code)]
mod significant_drop {
#[derive(Debug)]
struct X;

#[derive(Debug)]
struct Y;

impl Drop for Y {
fn drop(&mut self) {}
}

fn foo(x: X, y: Y) {
debug(x);
debug(&y); // Don't lint. Has significant drop
}

fn debug(_: impl std::fmt::Debug) {}
}
21 changes: 21 additions & 0 deletions tests/ui/needless_borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,5 +311,26 @@ fn closure_test() {
let _ = std::fs::write("x", &arg);
let _ = std::fs::write("x", &loc);
};
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
f(arg);
}

#[allow(dead_code)]
mod significant_drop {
#[derive(Debug)]
struct X;

#[derive(Debug)]
struct Y;

impl Drop for Y {
fn drop(&mut self) {}
}

fn foo(x: X, y: Y) {
debug(&x);
debug(&y); // Don't lint. Has significant drop
}

fn debug(_: impl std::fmt::Debug) {}
}
8 changes: 7 additions & 1 deletion tests/ui/needless_borrow.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -198,5 +198,11 @@ error: the borrowed expression implements the required traits
LL | let _ = std::fs::write("x", &loc);
| ^^^^ help: change this to: `loc`

error: aborting due to 33 previous errors
error: the borrowed expression implements the required traits
--> $DIR/needless_borrow.rs:331:15
|
LL | debug(&x);
| ^^ help: change this to: `x`

error: aborting due to 34 previous errors

0 comments on commit 414add3

Please sign in to comment.