Skip to content

Commit dac8a3c

Browse files
committed
let_and_return: do not lint if last statement borrows
1 parent 9c205d7 commit dac8a3c

File tree

3 files changed

+129
-2
lines changed

3 files changed

+129
-2
lines changed

clippy_lints/src/let_and_return.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use if_chain::if_chain;
22
use rustc_errors::Applicability;
3-
use rustc_hir::{Block, ExprKind, PatKind, StmtKind};
3+
use rustc_hir::def_id::DefId;
4+
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
5+
use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
46
use rustc_lint::{LateContext, LateLintPass, LintContext};
7+
use rustc_middle::hir::map::Map;
58
use rustc_middle::lint::in_external_macro;
9+
use rustc_middle::ty::subst::GenericArgKind;
610
use rustc_session::{declare_lint_pass, declare_tool_lint};
711

812
use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then};
@@ -49,6 +53,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn {
4953
if let PatKind::Binding(.., ident, _) = local.pat.kind;
5054
if let ExprKind::Path(qpath) = &retexpr.kind;
5155
if match_qpath(qpath, &[&*ident.name.as_str()]);
56+
if !last_statement_borrows(cx, initexpr);
5257
if !in_external_macro(cx.sess(), initexpr.span);
5358
if !in_external_macro(cx.sess(), retexpr.span);
5459
if !in_external_macro(cx.sess(), local.span);
@@ -80,3 +85,57 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn {
8085
}
8186
}
8287
}
88+
89+
fn last_statement_borrows<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
90+
let mut visitor = BorrowVisitor { cx, borrows: false };
91+
walk_expr(&mut visitor, expr);
92+
visitor.borrows
93+
}
94+
95+
struct BorrowVisitor<'a, 'tcx> {
96+
cx: &'a LateContext<'a, 'tcx>,
97+
borrows: bool,
98+
}
99+
100+
impl BorrowVisitor<'_, '_> {
101+
fn fn_def_id(&self, expr: &Expr<'_>) -> Option<DefId> {
102+
match &expr.kind {
103+
ExprKind::MethodCall(..) => self.cx.tables.type_dependent_def_id(expr.hir_id),
104+
ExprKind::Call(
105+
Expr {
106+
kind: ExprKind::Path(qpath),
107+
..
108+
},
109+
..,
110+
) => self.cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id(),
111+
_ => None,
112+
}
113+
}
114+
}
115+
116+
impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
117+
type Map = Map<'tcx>;
118+
119+
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
120+
if self.borrows {
121+
return;
122+
}
123+
124+
if let Some(def_id) = self.fn_def_id(expr) {
125+
self.borrows = self
126+
.cx
127+
.tcx
128+
.fn_sig(def_id)
129+
.output()
130+
.skip_binder()
131+
.walk()
132+
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
133+
}
134+
135+
walk_expr(self, expr);
136+
}
137+
138+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
139+
NestedVisitorMap::None
140+
}
141+
}

clippy_lints/src/utils/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ pub fn method_calls<'tcx>(
400400
/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
401401
///
402402
/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
403-
/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec`
403+
/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
404404
/// containing the `Expr`s for
405405
/// `.bar()` and `.baz()`
406406
pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {

tests/ui/let_and_return.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,72 @@ macro_rules! tuple_encode {
6767

6868
tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
6969

70+
mod no_lint_if_stmt_borrows {
71+
mod issue_3792 {
72+
use std::io::{self, BufRead, Stdin};
73+
74+
fn read_line() -> String {
75+
let stdin = io::stdin();
76+
let line = stdin.lock().lines().next().unwrap().unwrap();
77+
line
78+
}
79+
}
80+
81+
mod issue_3324 {
82+
use std::cell::RefCell;
83+
use std::rc::{Rc, Weak};
84+
85+
fn test(value: Weak<RefCell<Bar>>) -> u32 {
86+
let value = value.upgrade().unwrap();
87+
let ret = value.borrow().baz();
88+
ret
89+
}
90+
91+
struct Bar {}
92+
93+
impl Bar {
94+
fn new() -> Self {
95+
Bar {}
96+
}
97+
fn baz(&self) -> u32 {
98+
0
99+
}
100+
}
101+
102+
fn main() {
103+
let a = Rc::new(RefCell::new(Bar::new()));
104+
let b = Rc::downgrade(&a);
105+
test(b);
106+
}
107+
}
108+
109+
mod free_function {
110+
struct Inner;
111+
112+
struct Foo<'a> {
113+
inner: &'a Inner,
114+
}
115+
116+
impl Drop for Foo<'_> {
117+
fn drop(&mut self) {}
118+
}
119+
120+
impl Foo<'_> {
121+
fn value(&self) -> i32 {
122+
42
123+
}
124+
}
125+
126+
fn some_foo(inner: &Inner) -> Foo<'_> {
127+
Foo { inner }
128+
}
129+
130+
fn test() -> i32 {
131+
let x = Inner {};
132+
let value = some_foo(&x).value();
133+
value
134+
}
135+
}
136+
}
137+
70138
fn main() {}

0 commit comments

Comments
 (0)