-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new lint
trivial_default_constructed_types
- Loading branch information
Showing
7 changed files
with
264 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
/// let a = A(Option::default()); | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// 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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|