Skip to content

Commit b033883

Browse files
committed
Auto merge of rust-lang#10864 - y21:issue10437, r=blyxyas,xFrednet
[`unnecessary_lazy_eval`]: don't lint on types with deref impl Fixes rust-lang#10437. This PR changes clippy's util module `eager_or_lazy` to also consider deref expressions whose type has a non-builtin deref impl and not suggest replacing it as that might have observable side effects. A prominent example might be the `lazy_static` macro, which creates a newtype with a `Deref` impl that you need to go through to get access to the inner value. Going from lazy to eager can make a difference there. changelog: [`unnecessary_lazy_eval`]: don't lint on types with non-builtin deref impl
2 parents 4895a40 + a239c8b commit b033883

File tree

4 files changed

+97
-37
lines changed

4 files changed

+97
-37
lines changed

clippy_utils/src/eager_or_lazy.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,15 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
173173
self.eagerness = Lazy;
174174
}
175175
},
176-
176+
// Custom `Deref` impl might have side effects
177+
ExprKind::Unary(UnOp::Deref, e)
178+
if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() =>
179+
{
180+
self.eagerness |= NoChange;
181+
},
177182
// Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
178183
ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
179184
ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
180-
181185
ExprKind::Unary(_, e)
182186
if matches!(
183187
self.cx.typeck_results().expr_ty(e).kind(),

tests/ui/unnecessary_lazy_eval.fixed

+20
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#![allow(clippy::bind_instead_of_map)]
66
#![allow(clippy::map_identity)]
77

8+
use std::ops::Deref;
9+
810
extern crate proc_macros;
911
use proc_macros::with_span;
1012

@@ -41,6 +43,15 @@ impl Drop for Issue9427FollowUp {
4143
}
4244
}
4345

46+
struct Issue10437;
47+
impl Deref for Issue10437 {
48+
type Target = u32;
49+
fn deref(&self) -> &Self::Target {
50+
println!("side effect deref");
51+
&0
52+
}
53+
}
54+
4455
fn main() {
4556
let astronomers_pi = 10;
4657
let ext_arr: [usize; 1] = [2];
@@ -65,6 +76,12 @@ fn main() {
6576
let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
6677
let _ = cond.then_some(astronomers_pi);
6778

79+
// Should lint - Builtin deref
80+
let r = &1;
81+
let _ = Some(1).unwrap_or(*r);
82+
let b = Box::new(1);
83+
let _ = Some(1).unwrap_or(*b);
84+
6885
// Cases when unwrap is not called on a simple variable
6986
let _ = Some(10).unwrap_or(2);
7087
let _ = Some(10).and(ext_opt);
@@ -93,6 +110,9 @@ fn main() {
93110
let _ = deep.0.or_else(|| some_call());
94111
let _ = opt.ok_or_else(|| ext_arr[0]);
95112

113+
let _ = Some(1).unwrap_or_else(|| *Issue10437); // Issue10437 has a deref impl
114+
let _ = Some(1).unwrap_or(*Issue10437);
115+
96116
// Should not lint - bool
97117
let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
98118
let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop

tests/ui/unnecessary_lazy_eval.rs

+20
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#![allow(clippy::bind_instead_of_map)]
66
#![allow(clippy::map_identity)]
77

8+
use std::ops::Deref;
9+
810
extern crate proc_macros;
911
use proc_macros::with_span;
1012

@@ -41,6 +43,15 @@ impl Drop for Issue9427FollowUp {
4143
}
4244
}
4345

46+
struct Issue10437;
47+
impl Deref for Issue10437 {
48+
type Target = u32;
49+
fn deref(&self) -> &Self::Target {
50+
println!("side effect deref");
51+
&0
52+
}
53+
}
54+
4455
fn main() {
4556
let astronomers_pi = 10;
4657
let ext_arr: [usize; 1] = [2];
@@ -65,6 +76,12 @@ fn main() {
6576
let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
6677
let _ = cond.then(|| astronomers_pi);
6778

79+
// Should lint - Builtin deref
80+
let r = &1;
81+
let _ = Some(1).unwrap_or_else(|| *r);
82+
let b = Box::new(1);
83+
let _ = Some(1).unwrap_or_else(|| *b);
84+
6885
// Cases when unwrap is not called on a simple variable
6986
let _ = Some(10).unwrap_or_else(|| 2);
7087
let _ = Some(10).and_then(|_| ext_opt);
@@ -93,6 +110,9 @@ fn main() {
93110
let _ = deep.0.or_else(|| some_call());
94111
let _ = opt.ok_or_else(|| ext_arr[0]);
95112

113+
let _ = Some(1).unwrap_or_else(|| *Issue10437); // Issue10437 has a deref impl
114+
let _ = Some(1).unwrap_or(*Issue10437);
115+
96116
// Should not lint - bool
97117
let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
98118
let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop

0 commit comments

Comments
 (0)