Skip to content

Commit

Permalink
new lint trivial_default_constructed_types
Browse files Browse the repository at this point in the history
  • Loading branch information
Centri3 committed Jun 18, 2023
1 parent 8c8ff5f commit fa1c3c8
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5226,6 +5226,7 @@ Released 2018-09-13
[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
[`trivial_default_constructed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_default_constructed_types
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO,
crate::transmute::USELESS_TRANSMUTE_INFO,
crate::transmute::WRONG_TRANSMUTE_INFO,
crate::trivial_default_constructed_types::TRIVIAL_DEFAULT_CONSTRUCTED_TYPES_INFO,
crate::types::BORROWED_BOX_INFO,
crate::types::BOX_COLLECTION_INFO,
crate::types::LINKEDLIST_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ mod to_digit_is_some;
mod trailing_empty_array;
mod trait_bounds;
mod transmute;
mod trivial_default_constructed_types;
mod types;
mod undocumented_unsafe_blocks;
mod unicode;
Expand Down Expand Up @@ -1055,6 +1056,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold)));
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls));
store.register_late_pass(|_| Box::new(trivial_default_constructed_types::TrivialDefaultConstructedTypes));
// add lints here, do not remove this comment, it's used in `new_lint`
}

Expand Down
120 changes: 120 additions & 0 deletions clippy_lints/src/trivial_default_constructed_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, is_lang_item_or_ctor, last_path_segment};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::{
lint::in_external_macro,
ty::{self, Ty},
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::kw;

declare_clippy_lint! {
/// ### What it does
/// Checks for types constructed by `default` that really don't need to be.
///
/// ### Why is this bad?
/// It's harder for the reader to know what the value is, and it's an unnecessary function call.
///
/// ### Example
/// ```rust,ignore
/// let a = A(Option::default());
/// ```
/// Use instead:
/// ```rust,ignore
/// let a = A(None);
/// ```
#[clippy::version = "1.72.0"]
pub TRIVIAL_DEFAULT_CONSTRUCTED_TYPES,
pedantic,
"checks for usage of `Default::default` to construct trivial types"
}
declare_lint_pass!(TrivialDefaultConstructedTypes => [TRIVIAL_DEFAULT_CONSTRUCTED_TYPES]);

impl<'tcx> LateLintPass<'tcx> for TrivialDefaultConstructedTypes {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if !in_external_macro(cx.sess(), expr.span)
&& let ExprKind::Call(call, _) = expr.kind
&& let ExprKind::Path(qpath) = call.kind
// `last_path_segment` ICEs if we give it a `LangItem`.
&& !matches!(qpath, QPath::LangItem(..))
&& last_path_segment(&qpath).ident.name == kw::Default
{
let ret_ty = cx
.typeck_results()
.expr_ty(call)
.fn_sig(cx.tcx)
.output()
.skip_binder()
.peel_refs();
if let Some(default) = default_value(cx, ret_ty) && !is_from_proc_macro(cx, expr) {
span_lint_and_sugg(
cx,
TRIVIAL_DEFAULT_CONSTRUCTED_TYPES,
expr.span,
"constructing a trivial type using `default`",
"try",
default.to_string(),
Applicability::MachineApplicable,
);
} else if let ty::Tuple(fields) = ret_ty.kind()
&& let Some(fields_default) = fields.iter()
.map(|field| default_value(cx, field))
.collect::<Option<Vec<&'static str>>>()
&& !is_from_proc_macro(cx, expr)
{
let default = if fields.len() == 1 {
// Needs trailing comma to be a single-element tuple
fields_default[0].to_owned() + ","
} else {
fields_default.join(", ")
};

span_lint_and_sugg(
cx,
TRIVIAL_DEFAULT_CONSTRUCTED_TYPES,
expr.span,
&format!(
"constructing a {} using `default`",
if fields.is_empty() { "unit" } else { "trivial tuple" },
),
"try",
format!("({default})"),
Applicability::MachineApplicable,
);
} else if let ty::Array(ty, len) = ret_ty.kind()
&& let Some(default) = default_value(cx, *ty)
&& !is_from_proc_macro(cx, expr)
{
span_lint_and_sugg(
cx,
TRIVIAL_DEFAULT_CONSTRUCTED_TYPES,
expr.span,
"constructing a trivial array using `default`",
"try",
format!("[{default}; {len}]"),
Applicability::MachineApplicable,
);
}
}
}
}

/// Gets the default value of `ty`.
fn default_value(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<&'static str> {
match ty.kind() {
ty::Adt(def, _) => {
if is_lang_item_or_ctor(cx, def.did(), LangItem::Option) {
return Some("None");
}

None
},
ty::Bool => Some("false"),
ty::Str => Some(r#""""#),
ty::Int(_) | ty::Uint(_) => Some("0"),
ty::Float(_) => Some("0.0"),
// Do not handle `ty::Char`, it's a lot less readable
_ => None,
}
}
44 changes: 44 additions & 0 deletions tests/ui/trivial_default_constructed_types.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//@run-rustfix
//@aux-build:proc_macros.rs
#![allow(clippy::no_effect, unused)]
#![warn(clippy::trivial_default_constructed_types)]

#[macro_use]
extern crate proc_macros;

fn main() {
0;
let x: Option<u32> = None;
let y: (usize,) = (0,);
();
let x: [u32; 10] = [0; 10];
let x: [f32; 1000] = [0.0; 1000];
let x = "";
let x = false;
// Do not lint
let x = char::default();

external! {
u32::default();
let x: Option<u32> = Option::default();
let y: (usize,) = Default::default();
<()>::default();
let x: [u32; 10] = Default::default();
let x: [f32; 1000] = [Default::default(); 1000];
let x = <&str>::default();
let x = bool::default();
let x = char::default();
}
with_span! {
span
u32::default();
let x: Option<u32> = Option::default();
let y: (usize,) = Default::default();
<()>::default();
let x: [u32; 10] = Default::default();
let x: [f32; 1000] = [Default::default(); 1000];
let x = <&str>::default();
let x = bool::default();
let x = char::default();
}
}
44 changes: 44 additions & 0 deletions tests/ui/trivial_default_constructed_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//@run-rustfix
//@aux-build:proc_macros.rs
#![allow(clippy::no_effect, unused)]
#![warn(clippy::trivial_default_constructed_types)]

#[macro_use]
extern crate proc_macros;

fn main() {
u32::default();
let x: Option<u32> = Option::default();
let y: (usize,) = Default::default();
<()>::default();
let x: [u32; 10] = Default::default();
let x: [f32; 1000] = [Default::default(); 1000];
let x = <&str>::default();
let x = bool::default();
// Do not lint
let x = char::default();

external! {
u32::default();
let x: Option<u32> = Option::default();
let y: (usize,) = Default::default();
<()>::default();
let x: [u32; 10] = Default::default();
let x: [f32; 1000] = [Default::default(); 1000];
let x = <&str>::default();
let x = bool::default();
let x = char::default();
}
with_span! {
span
u32::default();
let x: Option<u32> = Option::default();
let y: (usize,) = Default::default();
<()>::default();
let x: [u32; 10] = Default::default();
let x: [f32; 1000] = [Default::default(); 1000];
let x = <&str>::default();
let x = bool::default();
let x = char::default();
}
}
52 changes: 52 additions & 0 deletions tests/ui/trivial_default_constructed_types.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
error: constructing a trivial type using `default`
--> $DIR/trivial_default_constructed_types.rs:10:5
|
LL | u32::default();
| ^^^^^^^^^^^^^^ help: try: `0`
|
= note: `-D clippy::trivial-default-constructed-types` implied by `-D warnings`

error: constructing a trivial type using `default`
--> $DIR/trivial_default_constructed_types.rs:11:26
|
LL | let x: Option<u32> = Option::default();
| ^^^^^^^^^^^^^^^^^ help: try: `None`

error: constructing a trivial tuple using `default`
--> $DIR/trivial_default_constructed_types.rs:12:23
|
LL | let y: (usize,) = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `(0,)`

error: constructing a unit using `default`
--> $DIR/trivial_default_constructed_types.rs:13:5
|
LL | <()>::default();
| ^^^^^^^^^^^^^^^ help: try: `()`

error: constructing a trivial array using `default`
--> $DIR/trivial_default_constructed_types.rs:14:24
|
LL | let x: [u32; 10] = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `[0; 10]`

error: constructing a trivial type using `default`
--> $DIR/trivial_default_constructed_types.rs:15:27
|
LL | let x: [f32; 1000] = [Default::default(); 1000];
| ^^^^^^^^^^^^^^^^^^ help: try: `0.0`

error: constructing a trivial type using `default`
--> $DIR/trivial_default_constructed_types.rs:16:13
|
LL | let x = <&str>::default();
| ^^^^^^^^^^^^^^^^^ help: try: `""`

error: constructing a trivial type using `default`
--> $DIR/trivial_default_constructed_types.rs:17:13
|
LL | let x = bool::default();
| ^^^^^^^^^^^^^^^ help: try: `false`

error: aborting due to 8 previous errors

0 comments on commit fa1c3c8

Please sign in to comment.