Skip to content

Commit ad0ed39

Browse files
committed
new lint trivial_default_constructed_types
1 parent 8c8ff5f commit ad0ed39

7 files changed

+264
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5226,6 +5226,7 @@ Released 2018-09-13
52265226
[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
52275227
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
52285228
[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
5229+
[`trivial_default_constructed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_default_constructed_types
52295230
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
52305231
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
52315232
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
620620
crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO,
621621
crate::transmute::USELESS_TRANSMUTE_INFO,
622622
crate::transmute::WRONG_TRANSMUTE_INFO,
623+
crate::trivial_default_constructed_types::TRIVIAL_DEFAULT_CONSTRUCTED_TYPES_INFO,
623624
crate::types::BORROWED_BOX_INFO,
624625
crate::types::BOX_COLLECTION_INFO,
625626
crate::types::LINKEDLIST_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ mod to_digit_is_some;
309309
mod trailing_empty_array;
310310
mod trait_bounds;
311311
mod transmute;
312+
mod trivial_default_constructed_types;
312313
mod types;
313314
mod undocumented_unsafe_blocks;
314315
mod unicode;
@@ -1055,6 +1056,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10551056
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold)));
10561057
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
10571058
store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls));
1059+
store.register_late_pass(|_| Box::new(trivial_default_constructed_types::TrivialDefaultConstructedTypes));
10581060
// add lints here, do not remove this comment, it's used in `new_lint`
10591061
}
10601062

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, is_lang_item_or_ctor, last_path_segment};
2+
use rustc_errors::Applicability;
3+
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
4+
use rustc_lint::{LateContext, LateLintPass, LintContext};
5+
use rustc_middle::{
6+
lint::in_external_macro,
7+
ty::{self, Ty},
8+
};
9+
use rustc_session::{declare_lint_pass, declare_tool_lint};
10+
use rustc_span::symbol::kw;
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
/// Checks for types constructed by `default` that really don't need to be.
15+
///
16+
/// ### Why is this bad?
17+
/// It's harder for the reader to know what the value is, and it's an unnecessary function call.
18+
///
19+
/// ### Example
20+
/// ```rust
21+
/// let a = A(Option::default());
22+
/// ```
23+
/// Use instead:
24+
/// ```rust
25+
/// let a = A(None);
26+
/// ```
27+
#[clippy::version = "1.72.0"]
28+
pub TRIVIAL_DEFAULT_CONSTRUCTED_TYPES,
29+
pedantic,
30+
"checks for usage of `Default::default` to construct trivial types"
31+
}
32+
declare_lint_pass!(TrivialDefaultConstructedTypes => [TRIVIAL_DEFAULT_CONSTRUCTED_TYPES]);
33+
34+
impl<'tcx> LateLintPass<'tcx> for TrivialDefaultConstructedTypes {
35+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
36+
if !in_external_macro(cx.sess(), expr.span)
37+
&& let ExprKind::Call(call, _) = expr.kind
38+
&& let ExprKind::Path(qpath) = call.kind
39+
// `last_path_segment` ICEs if we give it a `LangItem`.
40+
&& !matches!(qpath, QPath::LangItem(..))
41+
&& last_path_segment(&qpath).ident.name == kw::Default
42+
{
43+
let ret_ty = cx
44+
.typeck_results()
45+
.expr_ty(call)
46+
.fn_sig(cx.tcx)
47+
.output()
48+
.skip_binder()
49+
.peel_refs();
50+
if let Some(default) = default_value(cx, ret_ty) && !is_from_proc_macro(cx, expr) {
51+
span_lint_and_sugg(
52+
cx,
53+
TRIVIAL_DEFAULT_CONSTRUCTED_TYPES,
54+
expr.span,
55+
"constructing a trivial type using `default`",
56+
"try",
57+
default.to_string(),
58+
Applicability::MachineApplicable,
59+
);
60+
} else if let ty::Tuple(fields) = ret_ty.kind()
61+
&& let Some(fields_default) = fields.iter()
62+
.map(|field| default_value(cx, field))
63+
.collect::<Option<Vec<&'static str>>>()
64+
&& !is_from_proc_macro(cx, expr)
65+
{
66+
let default = if fields.len() == 1 {
67+
// Needs trailing comma to be a single-element tuple
68+
fields_default[0].to_owned() + ","
69+
} else {
70+
fields_default.join(", ")
71+
};
72+
73+
span_lint_and_sugg(
74+
cx,
75+
TRIVIAL_DEFAULT_CONSTRUCTED_TYPES,
76+
expr.span,
77+
&format!(
78+
"constructing a {} using `default`",
79+
if fields.is_empty() { "unit" } else { "trivial tuple" },
80+
),
81+
"try",
82+
format!("({default})"),
83+
Applicability::MachineApplicable,
84+
);
85+
} else if let ty::Array(ty, len) = ret_ty.kind()
86+
&& let Some(default) = default_value(cx, *ty)
87+
&& !is_from_proc_macro(cx, expr)
88+
{
89+
span_lint_and_sugg(
90+
cx,
91+
TRIVIAL_DEFAULT_CONSTRUCTED_TYPES,
92+
expr.span,
93+
"constructing a trivial array using `default`",
94+
"try",
95+
format!("[{default}; {len}]"),
96+
Applicability::MachineApplicable,
97+
);
98+
}
99+
}
100+
}
101+
}
102+
103+
/// Gets the default value of `ty`.
104+
fn default_value(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<&'static str> {
105+
match ty.kind() {
106+
ty::Adt(def, _) => {
107+
if is_lang_item_or_ctor(cx, def.did(), LangItem::Option) {
108+
return Some("None");
109+
}
110+
111+
None
112+
},
113+
ty::Bool => Some("false"),
114+
ty::Str => Some(r#""""#),
115+
ty::Int(_) | ty::Uint(_) => Some("0"),
116+
ty::Float(_) => Some("0.0"),
117+
// Do not handle `ty::Char`, it's a lot less readable
118+
_ => None,
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//@run-rustfix
2+
//@aux-build:proc_macros.rs
3+
#![allow(clippy::no_effect, unused)]
4+
#![warn(clippy::trivial_default_constructed_types)]
5+
6+
#[macro_use]
7+
extern crate proc_macros;
8+
9+
fn main() {
10+
0;
11+
let x: Option<u32> = None;
12+
let y: (usize,) = (0,);
13+
();
14+
let x: [u32; 10] = [0; 10];
15+
let x: [f32; 1000] = [0.0; 1000];
16+
let x = "";
17+
let x = false;
18+
// Do not lint
19+
let x = char::default();
20+
21+
external! {
22+
u32::default();
23+
let x: Option<u32> = Option::default();
24+
let y: (usize,) = Default::default();
25+
<()>::default();
26+
let x: [u32; 10] = Default::default();
27+
let x: [f32; 1000] = [Default::default(); 1000];
28+
let x = <&str>::default();
29+
let x = bool::default();
30+
let x = char::default();
31+
}
32+
with_span! {
33+
span
34+
u32::default();
35+
let x: Option<u32> = Option::default();
36+
let y: (usize,) = Default::default();
37+
<()>::default();
38+
let x: [u32; 10] = Default::default();
39+
let x: [f32; 1000] = [Default::default(); 1000];
40+
let x = <&str>::default();
41+
let x = bool::default();
42+
let x = char::default();
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//@run-rustfix
2+
//@aux-build:proc_macros.rs
3+
#![allow(clippy::no_effect, unused)]
4+
#![warn(clippy::trivial_default_constructed_types)]
5+
6+
#[macro_use]
7+
extern crate proc_macros;
8+
9+
fn main() {
10+
u32::default();
11+
let x: Option<u32> = Option::default();
12+
let y: (usize,) = Default::default();
13+
<()>::default();
14+
let x: [u32; 10] = Default::default();
15+
let x: [f32; 1000] = [Default::default(); 1000];
16+
let x = <&str>::default();
17+
let x = bool::default();
18+
// Do not lint
19+
let x = char::default();
20+
21+
external! {
22+
u32::default();
23+
let x: Option<u32> = Option::default();
24+
let y: (usize,) = Default::default();
25+
<()>::default();
26+
let x: [u32; 10] = Default::default();
27+
let x: [f32; 1000] = [Default::default(); 1000];
28+
let x = <&str>::default();
29+
let x = bool::default();
30+
let x = char::default();
31+
}
32+
with_span! {
33+
span
34+
u32::default();
35+
let x: Option<u32> = Option::default();
36+
let y: (usize,) = Default::default();
37+
<()>::default();
38+
let x: [u32; 10] = Default::default();
39+
let x: [f32; 1000] = [Default::default(); 1000];
40+
let x = <&str>::default();
41+
let x = bool::default();
42+
let x = char::default();
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: constructing a trivial type using `default`
2+
--> $DIR/trivial_default_constructed_types.rs:10:5
3+
|
4+
LL | u32::default();
5+
| ^^^^^^^^^^^^^^ help: try: `0`
6+
|
7+
= note: `-D clippy::trivial-default-constructed-types` implied by `-D warnings`
8+
9+
error: constructing a trivial type using `default`
10+
--> $DIR/trivial_default_constructed_types.rs:11:26
11+
|
12+
LL | let x: Option<u32> = Option::default();
13+
| ^^^^^^^^^^^^^^^^^ help: try: `None`
14+
15+
error: constructing a trivial tuple using `default`
16+
--> $DIR/trivial_default_constructed_types.rs:12:23
17+
|
18+
LL | let y: (usize,) = Default::default();
19+
| ^^^^^^^^^^^^^^^^^^ help: try: `(0,)`
20+
21+
error: constructing a unit using `default`
22+
--> $DIR/trivial_default_constructed_types.rs:13:5
23+
|
24+
LL | <()>::default();
25+
| ^^^^^^^^^^^^^^^ help: try: `()`
26+
27+
error: constructing a trivial array using `default`
28+
--> $DIR/trivial_default_constructed_types.rs:14:24
29+
|
30+
LL | let x: [u32; 10] = Default::default();
31+
| ^^^^^^^^^^^^^^^^^^ help: try: `[0; 10]`
32+
33+
error: constructing a trivial type using `default`
34+
--> $DIR/trivial_default_constructed_types.rs:15:27
35+
|
36+
LL | let x: [f32; 1000] = [Default::default(); 1000];
37+
| ^^^^^^^^^^^^^^^^^^ help: try: `0.0`
38+
39+
error: constructing a trivial type using `default`
40+
--> $DIR/trivial_default_constructed_types.rs:16:13
41+
|
42+
LL | let x = <&str>::default();
43+
| ^^^^^^^^^^^^^^^^^ help: try: `""`
44+
45+
error: constructing a trivial type using `default`
46+
--> $DIR/trivial_default_constructed_types.rs:17:13
47+
|
48+
LL | let x = bool::default();
49+
| ^^^^^^^^^^^^^^^ help: try: `false`
50+
51+
error: aborting due to 8 previous errors
52+

0 commit comments

Comments
 (0)