Skip to content

Commit 4839c79

Browse files
authored
Merge pull request #2844 from illicitonion/default_trait_access
Add default_trait_access lint
2 parents 7d67288 + b24d753 commit 4839c79

File tree

10 files changed

+432
-192
lines changed

10 files changed

+432
-192
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use rustc::hir::*;
2+
use rustc::lint::*;
3+
use rustc::ty::TypeVariants;
4+
5+
use crate::utils::{any_parent_is_automatically_derived, match_def_path, opt_def_id, paths, span_lint_and_sugg};
6+
7+
8+
/// **What it does:** Checks for literal calls to `Default::default()`.
9+
///
10+
/// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
11+
/// being gotten than the generic `Default`.
12+
///
13+
/// **Known problems:** None.
14+
///
15+
/// **Example:**
16+
/// ```rust
17+
/// // Bad
18+
/// let s: String = Default::default();
19+
///
20+
/// // Good
21+
/// let s = String::default();
22+
/// ```
23+
declare_clippy_lint! {
24+
pub DEFAULT_TRAIT_ACCESS,
25+
style,
26+
"checks for literal calls to Default::default()"
27+
}
28+
29+
#[derive(Copy, Clone)]
30+
pub struct DefaultTraitAccess;
31+
32+
impl LintPass for DefaultTraitAccess {
33+
fn get_lints(&self) -> LintArray {
34+
lint_array!(DEFAULT_TRAIT_ACCESS)
35+
}
36+
}
37+
38+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DefaultTraitAccess {
39+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
40+
if_chain! {
41+
if let ExprCall(ref path, ..) = expr.node;
42+
if !any_parent_is_automatically_derived(cx.tcx, expr.id);
43+
if let ExprPath(ref qpath) = path.node;
44+
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id));
45+
if match_def_path(cx.tcx, def_id, &paths::DEFAULT_TRAIT_METHOD);
46+
then {
47+
match qpath {
48+
QPath::Resolved(..) => {
49+
// TODO: Work out a way to put "whatever the imported way of referencing
50+
// this type in this file" rather than a fully-qualified type.
51+
let expr_ty = cx.tables.expr_ty(expr);
52+
if let TypeVariants::TyAdt(..) = expr_ty.sty {
53+
let replacement = format!("{}::default()", expr_ty);
54+
span_lint_and_sugg(
55+
cx,
56+
DEFAULT_TRAIT_ACCESS,
57+
expr.span,
58+
&format!("Calling {} is more clear than this expression", replacement),
59+
"try",
60+
replacement);
61+
}
62+
},
63+
QPath::TypeRelative(..) => {},
64+
}
65+
}
66+
}
67+
}
68+
}

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ pub mod collapsible_if;
111111
pub mod const_static_lifetime;
112112
pub mod copies;
113113
pub mod cyclomatic_complexity;
114+
pub mod default_trait_access;
114115
pub mod derive;
115116
pub mod doc;
116117
pub mod double_comparison;
@@ -430,6 +431,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
430431
reg.register_late_lint_pass(box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
431432
reg.register_late_lint_pass(box unwrap::Pass);
432433
reg.register_late_lint_pass(box duration_subsec::DurationSubsec);
434+
reg.register_late_lint_pass(box default_trait_access::DefaultTraitAccess);
433435

434436

435437
reg.register_lint_group("clippy_restriction", vec![
@@ -517,6 +519,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
517519
copies::IF_SAME_THEN_ELSE,
518520
copies::IFS_SAME_COND,
519521
cyclomatic_complexity::CYCLOMATIC_COMPLEXITY,
522+
default_trait_access::DEFAULT_TRAIT_ACCESS,
520523
derive::DERIVE_HASH_XOR_EQ,
521524
double_comparison::DOUBLE_COMPARISONS,
522525
double_parens::DOUBLE_PARENS,
@@ -715,6 +718,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
715718
block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT,
716719
collapsible_if::COLLAPSIBLE_IF,
717720
const_static_lifetime::CONST_STATIC_LIFETIME,
721+
default_trait_access::DEFAULT_TRAIT_ACCESS,
718722
enum_variants::ENUM_VARIANT_NAMES,
719723
enum_variants::MODULE_INCEPTION,
720724
eq_op::OP_REF,

clippy_lints/src/utils/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,3 +1129,17 @@ pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
11291129

11301130
without
11311131
}
1132+
1133+
pub fn any_parent_is_automatically_derived(tcx: TyCtxt, node: NodeId) -> bool {
1134+
let map = &tcx.hir;
1135+
let mut prev_enclosing_node = None;
1136+
let mut enclosing_node = node;
1137+
while Some(enclosing_node) != prev_enclosing_node {
1138+
if is_automatically_derived(map.attrs(enclosing_node)) {
1139+
return true;
1140+
}
1141+
prev_enclosing_node = Some(enclosing_node);
1142+
enclosing_node = map.get_parent(enclosing_node);
1143+
}
1144+
false
1145+
}

clippy_lints/src/utils/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub const C_VOID: [&str; 4] = ["std", "os", "raw", "c_void"];
2424
pub const C_VOID_LIBC: [&str; 2] = ["libc", "c_void"];
2525
pub const DEBUG_FMT_METHOD: [&str; 4] = ["core", "fmt", "Debug", "fmt"];
2626
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
27+
pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
2728
pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
2829
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
2930
pub const DROP: [&str; 3] = ["core", "mem", "drop"];

tests/ui/default_trait_access.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#![warn(default_trait_access)]
2+
3+
use std::default::Default as D2;
4+
use std::string;
5+
use std::default;
6+
7+
fn main() {
8+
let s1: String = Default::default();
9+
10+
let s2 = String::default();
11+
12+
let s3: String = D2::default();
13+
14+
let s4: String = std::default::Default::default();
15+
16+
let s5 = string::String::default();
17+
18+
let s6: String = default::Default::default();
19+
20+
let s7 = std::string::String::default();
21+
22+
let s8: String = DefaultFactory::make_t_badly();
23+
24+
let s9: String = DefaultFactory::make_t_nicely();
25+
26+
let s10 = DerivedDefault::default();
27+
28+
let s11: GenericDerivedDefault<String> = Default::default();
29+
30+
let s12 = GenericDerivedDefault::<String>::default();
31+
32+
let s13 = TupleDerivedDefault::default();
33+
34+
let s14: TupleDerivedDefault = Default::default();
35+
36+
let s15: ArrayDerivedDefault = Default::default();
37+
38+
let s16 = ArrayDerivedDefault::default();
39+
40+
let s17: TupleStructDerivedDefault = Default::default();
41+
42+
let s18 = TupleStructDerivedDefault::default();
43+
44+
println!(
45+
"[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]",
46+
s1,
47+
s2,
48+
s3,
49+
s4,
50+
s5,
51+
s6,
52+
s7,
53+
s8,
54+
s9,
55+
s10,
56+
s11,
57+
s12,
58+
s13,
59+
s14,
60+
s15,
61+
s16,
62+
s17,
63+
s18,
64+
);
65+
}
66+
67+
struct DefaultFactory;
68+
69+
impl DefaultFactory {
70+
pub fn make_t_badly<T: Default>() -> T {
71+
Default::default()
72+
}
73+
74+
pub fn make_t_nicely<T: Default>() -> T {
75+
T::default()
76+
}
77+
}
78+
79+
#[derive(Debug, Default)]
80+
struct DerivedDefault {
81+
pub s: String,
82+
}
83+
84+
#[derive(Debug, Default)]
85+
struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
86+
pub s: T,
87+
}
88+
89+
#[derive(Debug, Default)]
90+
struct TupleDerivedDefault {
91+
pub s: (String, String),
92+
}
93+
94+
#[derive(Debug, Default)]
95+
struct ArrayDerivedDefault {
96+
pub s: [String; 10],
97+
}
98+
99+
#[derive(Debug, Default)]
100+
struct TupleStructDerivedDefault(String);

tests/ui/default_trait_access.stderr

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: Calling std::string::String::default() is more clear than this expression
2+
--> $DIR/default_trait_access.rs:8:22
3+
|
4+
8 | let s1: String = Default::default();
5+
| ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
6+
|
7+
= note: `-D default-trait-access` implied by `-D warnings`
8+
9+
error: Calling std::string::String::default() is more clear than this expression
10+
--> $DIR/default_trait_access.rs:12:22
11+
|
12+
12 | let s3: String = D2::default();
13+
| ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
14+
15+
error: Calling std::string::String::default() is more clear than this expression
16+
--> $DIR/default_trait_access.rs:14:22
17+
|
18+
14 | let s4: String = std::default::Default::default();
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
20+
21+
error: Calling std::string::String::default() is more clear than this expression
22+
--> $DIR/default_trait_access.rs:18:22
23+
|
24+
18 | let s6: String = default::Default::default();
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
26+
27+
error: Calling GenericDerivedDefault<std::string::String>::default() is more clear than this expression
28+
--> $DIR/default_trait_access.rs:28:46
29+
|
30+
28 | let s11: GenericDerivedDefault<String> = Default::default();
31+
| ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault<std::string::String>::default()`
32+
33+
error: Calling TupleDerivedDefault::default() is more clear than this expression
34+
--> $DIR/default_trait_access.rs:34:36
35+
|
36+
34 | let s14: TupleDerivedDefault = Default::default();
37+
| ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()`
38+
39+
error: Calling ArrayDerivedDefault::default() is more clear than this expression
40+
--> $DIR/default_trait_access.rs:36:36
41+
|
42+
36 | let s15: ArrayDerivedDefault = Default::default();
43+
| ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()`
44+
45+
error: Calling TupleStructDerivedDefault::default() is more clear than this expression
46+
--> $DIR/default_trait_access.rs:40:42
47+
|
48+
40 | let s17: TupleStructDerivedDefault = Default::default();
49+
| ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()`
50+
51+
error: aborting due to 8 previous errors
52+

tests/ui/default_trait_access.stdout

Whitespace-only changes.

tests/ui/implicit_hasher.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ impl Foo<i16> for HashMap<String, String> {
3030

3131
impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> {
3232
fn make() -> (Self, Self) {
33-
(HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
33+
(HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
3434
}
3535
}
3636
impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> {
3737
fn make() -> (Self, Self) {
38-
(HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
38+
(HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
3939
}
4040
}
4141

@@ -53,12 +53,12 @@ impl Foo<i16> for HashSet<String> {
5353

5454
impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> {
5555
fn make() -> (Self, Self) {
56-
(HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
56+
(HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
5757
}
5858
}
5959
impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
6060
fn make() -> (Self, Self) {
61-
(HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
61+
(HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
6262
}
6363
}
6464

tests/ui/methods.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
#![warn(clippy, clippy_pedantic, option_unwrap_used)]
55
#![allow(blacklisted_name, unused, print_stdout, non_ascii_literal, new_without_default,
6-
new_without_default_derive, missing_docs_in_private_items, needless_pass_by_value)]
6+
new_without_default_derive, missing_docs_in_private_items, needless_pass_by_value,
7+
default_trait_access)]
78

89
use std::collections::BTreeMap;
910
use std::collections::HashMap;

0 commit comments

Comments
 (0)