Skip to content

Commit 31e2918

Browse files
committed
Add lint explicit_auto_deref
1 parent e2e363f commit 31e2918

13 files changed

+813
-58
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2063,6 +2063,7 @@ Released 2018-09-13
20632063
[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
20642064
[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
20652065
[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
2066+
[`explicit_auto_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
20662067
[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
20672068
[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
20682069
[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop

clippy_lints/src/dereference.rs

+418-41
Large diffs are not rendered by default.

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
601601
&default::DEFAULT_TRAIT_ACCESS,
602602
&default::FIELD_REASSIGN_WITH_DEFAULT,
603603
&default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
604+
&dereference::EXPLICIT_AUTO_DEREF,
604605
&dereference::EXPLICIT_DEREF_METHODS,
605606
&derive::DERIVE_HASH_XOR_EQ,
606607
&derive::DERIVE_ORD_XOR_PARTIAL_ORD,
@@ -1466,6 +1467,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14661467
LintId::of(&copies::IFS_SAME_COND),
14671468
LintId::of(&copies::IF_SAME_THEN_ELSE),
14681469
LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
1470+
LintId::of(&dereference::EXPLICIT_AUTO_DEREF),
14691471
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
14701472
LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
14711473
LintId::of(&doc::MISSING_SAFETY_DOC),
@@ -1746,6 +1748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
17461748
LintId::of(&collapsible_match::COLLAPSIBLE_MATCH),
17471749
LintId::of(&comparison_chain::COMPARISON_CHAIN),
17481750
LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
1751+
LintId::of(&dereference::EXPLICIT_AUTO_DEREF),
17491752
LintId::of(&doc::MISSING_SAFETY_DOC),
17501753
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
17511754
LintId::of(&enum_variants::ENUM_VARIANT_NAMES),

clippy_utils/src/lib.rs

+94-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ use rustc_infer::infer::TyCtxtInferExt;
7070
use rustc_lint::{LateContext, Level, Lint, LintContext};
7171
use rustc_middle::hir::exports::Export;
7272
use rustc_middle::hir::map::Map;
73-
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
74-
use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
73+
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
74+
use rustc_middle::ty::{
75+
self, layout::IntegerExt, Binder, DefIdTree, FnSig, PredicateKind, Ty, TyCtxt, TypeFoldable, TypeckResults,
76+
};
7577
use rustc_semver::RustcVersion;
7678
use rustc_session::Session;
7779
use rustc_span::hygiene::{self, ExpnKind, MacroKind};
@@ -1763,6 +1765,96 @@ where
17631765
match_expr_list
17641766
}
17651767

1768+
/// If expr is a path, resolves it. Otherwise returns `None`.
1769+
pub fn expr_res(typeck: &TypeckResults<'_>, expr: &Expr<'_>) -> Option<Res> {
1770+
if let ExprKind::Path(p) = &expr.kind {
1771+
Some(typeck.qpath_res(p, expr.hir_id))
1772+
} else {
1773+
None
1774+
}
1775+
}
1776+
1777+
/// A signature for a function like type.
1778+
pub enum ExprFnSig<'tcx> {
1779+
Sig(Binder<FnSig<'tcx>>),
1780+
Closure(Binder<FnSig<'tcx>>),
1781+
Trait(Binder<Ty<'tcx>>),
1782+
}
1783+
impl ExprFnSig<'tcx> {
1784+
/// Gets the argument type at the given offset.
1785+
pub fn input(&self, i: usize) -> Binder<Ty<'tcx>> {
1786+
match self {
1787+
Self::Sig(sig) => sig.input(i),
1788+
Self::Closure(sig) => sig.input(0).map_bound(|ty| ty.tuple_element_ty(i).unwrap()),
1789+
Self::Trait(sig) => sig.map_bound(|ty| ty.tuple_element_ty(i).unwrap()),
1790+
}
1791+
}
1792+
}
1793+
1794+
/// If expr is function like, get the signature for it.
1795+
pub fn expr_sig(tcx: TyCtxt<'tcx>, typeck: &TypeckResults<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
1796+
match expr_res(typeck, expr) {
1797+
Some(Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) | Res::SelfCtor(id)) => {
1798+
Some(ExprFnSig::Sig(tcx.fn_sig(id)))
1799+
},
1800+
_ => {
1801+
let ty = typeck.expr_ty_adjusted(expr).peel_refs();
1802+
match *ty.kind() {
1803+
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
1804+
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(tcx.fn_sig(id).subst(tcx, subs))),
1805+
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
1806+
ty::Dynamic(bounds, _) => {
1807+
let lang_items = tcx.lang_items();
1808+
match bounds.principal() {
1809+
Some(bound)
1810+
if Some(bound.def_id()) == lang_items.fn_trait()
1811+
|| Some(bound.def_id()) == lang_items.fn_once_trait()
1812+
|| Some(bound.def_id()) == lang_items.fn_mut_trait() =>
1813+
{
1814+
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0))))
1815+
},
1816+
_ => None,
1817+
}
1818+
},
1819+
ty::Param(_) | ty::Projection(..) => {
1820+
let mut id = typeck.hir_owner.to_def_id();
1821+
loop {
1822+
let predicates = tcx.predicates_of(id);
1823+
let lang_items = tcx.lang_items();
1824+
if let Some(res) = predicates
1825+
.predicates
1826+
.iter()
1827+
.find_map(|&(p, _)| {
1828+
p.kind()
1829+
.map_bound(|kind| match kind {
1830+
PredicateKind::Trait(p, _)
1831+
if (lang_items.fn_trait() == Some(p.def_id())
1832+
|| lang_items.fn_mut_trait() == Some(p.def_id())
1833+
|| lang_items.fn_once_trait() == Some(p.def_id()))
1834+
&& p.self_ty() == ty =>
1835+
{
1836+
Some(p.trait_ref.substs.type_at(1))
1837+
},
1838+
_ => None,
1839+
})
1840+
.transpose()
1841+
})
1842+
.map(ExprFnSig::Trait)
1843+
{
1844+
break Some(res);
1845+
} else if let Some(parent_id) = predicates.parent {
1846+
id = parent_id;
1847+
} else {
1848+
break None;
1849+
}
1850+
}
1851+
},
1852+
_ => None,
1853+
}
1854+
},
1855+
}
1856+
}
1857+
17661858
/// Peels off all references on the pattern. Returns the underlying pattern and the number of
17671859
/// references removed.
17681860
pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {

clippy_utils/src/paths.rs

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
4040
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
4141
pub const FILE: [&str; 3] = ["std", "fs", "File"];
4242
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
43+
pub const FORMAT_ARGS_MACRO: [&str; 4] = ["core", "macros", "builtin", "format_args"];
44+
pub const FORMAT_ARGS_NL_MACRO: [&str; 4] = ["core", "macros", "builtin", "format_args_nl"];
4345
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
4446
pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
4547
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];

tests/ui/explicit_auto_deref.fixed

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// run-rustfix
2+
3+
#![allow(
4+
unused_imports,
5+
dead_code,
6+
clippy::borrowed_box,
7+
clippy::deref_addrof,
8+
clippy::useless_vec
9+
)]
10+
#![warn(clippy::explicit_auto_deref)]
11+
12+
use std::{
13+
ops::{Deref, DerefMut},
14+
path::{Path, PathBuf},
15+
};
16+
17+
fn main() {
18+
let _: &str = &String::new();
19+
let _: &Path = &PathBuf::new();
20+
let _: &[_] = &vec![0u32, 1u32, 2u32];
21+
22+
fn f2<T>(_: &[T]) {}
23+
f2(&vec![0u32]);
24+
25+
fn f3() -> &'static str {
26+
static S: String = String::new();
27+
&S
28+
}
29+
30+
fn f4<T1, T2>(_: &(T1, T2)) {}
31+
f4(&Box::new((0u32, 0i32)));
32+
33+
let f5 = |_: &str| {};
34+
f5(&String::new());
35+
36+
fn f6(f: impl Fn(&str)) {
37+
f(&String::new());
38+
}
39+
40+
fn f7(f: &dyn Fn(&str)) {
41+
f(&String::new());
42+
}
43+
44+
let _: &Box<_> = &*Box::new(Box::new(0));
45+
let _: Box<u32> = *Box::new(Box::new(0));
46+
47+
fn f8<T: ?Sized>(_: &T) {}
48+
f8(&*String::new());
49+
50+
struct S1<T>(T);
51+
impl<T: Fn(&str)> S1<T> {
52+
fn f(&self, f: impl Fn(&str)) {
53+
f(&String::new());
54+
(self.0)(&String::new());
55+
}
56+
}
57+
58+
fn f9<T>(f: &mut T)
59+
where
60+
T: Iterator,
61+
<T as Iterator>::Item: Fn(&str),
62+
{
63+
f.next().unwrap()(&String::new())
64+
}
65+
66+
struct S2<'a> {
67+
x: &'a str,
68+
}
69+
let _ = S2 { x: &String::new() };
70+
71+
struct S3<'a>(&'a str);
72+
let _ = S3(&String::new());
73+
74+
macro_rules! m1 {
75+
($e:expr) => {{
76+
fn f(_: &str) {}
77+
f($e);
78+
}};
79+
}
80+
m1!(&*String::new());
81+
82+
macro_rules! m2 {
83+
($e:expr) => {
84+
&$e
85+
};
86+
}
87+
let _: &str = m2!(String::new());
88+
}

tests/ui/explicit_auto_deref.rs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// run-rustfix
2+
3+
#![allow(
4+
unused_imports,
5+
dead_code,
6+
clippy::borrowed_box,
7+
clippy::deref_addrof,
8+
clippy::useless_vec
9+
)]
10+
#![warn(clippy::explicit_auto_deref)]
11+
12+
use std::{
13+
ops::{Deref, DerefMut},
14+
path::{Path, PathBuf},
15+
};
16+
17+
fn main() {
18+
let _: &str = &*String::new();
19+
let _: &Path = &*PathBuf::new();
20+
let _: &[_] = vec![0u32, 1u32, 2u32].deref();
21+
22+
fn f2<T>(_: &[T]) {}
23+
f2(vec![0u32].deref());
24+
25+
fn f3() -> &'static str {
26+
static S: String = String::new();
27+
&*S
28+
}
29+
30+
fn f4<T1, T2>(_: &(T1, T2)) {}
31+
f4(Box::new((0u32, 0i32)).deref());
32+
33+
let f5 = |_: &str| {};
34+
f5(&*String::new());
35+
36+
fn f6(f: impl Fn(&str)) {
37+
f(&*String::new());
38+
}
39+
40+
fn f7(f: &dyn Fn(&str)) {
41+
f(&*String::new());
42+
}
43+
44+
let _: &Box<_> = &*Box::new(Box::new(0));
45+
let _: Box<u32> = *Box::new(Box::new(0));
46+
47+
fn f8<T: ?Sized>(_: &T) {}
48+
f8(&*String::new());
49+
50+
struct S1<T>(T);
51+
impl<T: Fn(&str)> S1<T> {
52+
fn f(&self, f: impl Fn(&str)) {
53+
f(&*String::new());
54+
(self.0)(&*String::new());
55+
}
56+
}
57+
58+
fn f9<T>(f: &mut T)
59+
where
60+
T: Iterator,
61+
<T as Iterator>::Item: Fn(&str),
62+
{
63+
f.next().unwrap()(&*String::new())
64+
}
65+
66+
struct S2<'a> {
67+
x: &'a str,
68+
}
69+
let _ = S2 { x: &*String::new() };
70+
71+
struct S3<'a>(&'a str);
72+
let _ = S3(&*String::new());
73+
74+
macro_rules! m1 {
75+
($e:expr) => {{
76+
fn f(_: &str) {}
77+
f($e);
78+
}};
79+
}
80+
m1!(&*String::new());
81+
82+
macro_rules! m2 {
83+
($e:expr) => {
84+
&$e
85+
};
86+
}
87+
let _: &str = &**m2!(String::new());
88+
}

0 commit comments

Comments
 (0)