]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/box_default.rs
Auto merge of #9510 - Alexendoo:lintcheck-server2, r=matthiaskrgr
[rust.git] / clippy_lints / src / box_default.rs
1 use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id};
2 use rustc_hir::{Expr, ExprKind, QPath};
3 use rustc_lint::{LateContext, LateLintPass, LintContext};
4 use rustc_middle::lint::in_external_macro;
5 use rustc_session::{declare_lint_pass, declare_tool_lint};
6 use rustc_span::sym;
7
8 declare_clippy_lint! {
9     /// ### What it does
10     /// checks for `Box::new(T::default())`, which is better written as
11     /// `Box::<T>::default()`.
12     ///
13     /// ### Why is this bad?
14     /// First, it's more complex, involving two calls instead of one.
15     /// Second, `Box::default()` can be faster
16     /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box).
17     ///
18     /// ### Known problems
19     /// The lint may miss some cases (e.g. Box::new(String::from(""))).
20     /// On the other hand, it will trigger on cases where the `default`
21     /// code comes from a macro that does something different based on
22     /// e.g. target operating system.
23     ///
24     /// ### Example
25     /// ```rust
26     /// let x: Box<String> = Box::new(Default::default());
27     /// ```
28     /// Use instead:
29     /// ```rust
30     /// let x: Box<String> = Box::default();
31     /// ```
32     #[clippy::version = "1.65.0"]
33     pub BOX_DEFAULT,
34     perf,
35     "Using Box::new(T::default()) instead of Box::default()"
36 }
37
38 declare_lint_pass!(BoxDefault => [BOX_DEFAULT]);
39
40 impl LateLintPass<'_> for BoxDefault {
41     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
42         if let ExprKind::Call(box_new, [arg]) = expr.kind
43             && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind
44             && let ExprKind::Call(..) = arg.kind
45             && !in_external_macro(cx.sess(), expr.span)
46             && expr.span.eq_ctxt(arg.span)
47             && seg.ident.name == sym::new
48             && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box()
49             && is_default_equivalent(cx, arg)
50         {
51             span_lint_and_help(
52                 cx,
53                 BOX_DEFAULT,
54                 expr.span,
55                 "`Box::new(_)` of default value",
56                 None,
57                 "use `Box::default()` instead",
58             );
59         }
60     }
61 }