]> git.proxmox.com Git - rustc.git/blobdiff - src/tools/clippy/clippy_lints/src/vec.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / src / tools / clippy / clippy_lints / src / vec.rs
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
new file mode 100644 (file)
index 0000000..c132e4d
--- /dev/null
@@ -0,0 +1,135 @@
+use crate::consts::{constant, Constant};
+use crate::rustc_target::abi::LayoutOf;
+use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, Ty};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+
+#[allow(clippy::module_name_repetitions)]
+#[derive(Copy, Clone)]
+pub struct UselessVec {
+    pub too_large_for_stack: u64,
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would
+    /// be possible.
+    ///
+    /// **Why is this bad?** This is less efficient.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// # fn foo(my_vec: &[u8]) {}
+    ///
+    /// // Bad
+    /// foo(&vec![1, 2]);
+    ///
+    /// // Good
+    /// foo(&[1, 2]);
+    /// ```
+    pub USELESS_VEC,
+    perf,
+    "useless `vec!`"
+}
+
+impl_lint_pass!(UselessVec => [USELESS_VEC]);
+
+impl<'tcx> LateLintPass<'tcx> for UselessVec {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        // search for `&vec![_]` expressions where the adjusted type is `&[_]`
+        if_chain! {
+            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
+            if let ty::Slice(..) = ty.kind();
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind;
+            if let Some(vec_args) = higher::vec_macro(cx, addressee);
+            then {
+                self.check_vec_macro(cx, &vec_args, expr.span);
+            }
+        }
+
+        // search for `for _ in vec![…]`
+        if_chain! {
+            if let Some((_, arg, _, _)) = higher::for_loop(expr);
+            if let Some(vec_args) = higher::vec_macro(cx, arg);
+            if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
+            then {
+                // report the error around the `vec!` not inside `<std macros>:`
+                let span = arg.span
+                    .ctxt()
+                    .outer_expn_data()
+                    .call_site
+                    .ctxt()
+                    .outer_expn_data()
+                    .call_site;
+                self.check_vec_macro(cx, &vec_args, span);
+            }
+        }
+    }
+}
+
+impl UselessVec {
+    fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
+        let mut applicability = Applicability::MachineApplicable;
+        let snippet = match *vec_args {
+            higher::VecArgs::Repeat(elem, len) => {
+                if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) {
+                    #[allow(clippy::cast_possible_truncation)]
+                    if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
+                        return;
+                    }
+
+                    format!(
+                        "&[{}; {}]",
+                        snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
+                        snippet_with_applicability(cx, len.span, "len", &mut applicability)
+                    )
+                } else {
+                    return;
+                }
+            },
+            higher::VecArgs::Vec(args) => {
+                if let Some(last) = args.iter().last() {
+                    #[allow(clippy::cast_possible_truncation)]
+                    if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
+                        return;
+                    }
+                    let span = args[0].span.to(last.span);
+
+                    format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
+                } else {
+                    "&[]".into()
+                }
+            },
+        };
+
+        span_lint_and_sugg(
+            cx,
+            USELESS_VEC,
+            span,
+            "useless use of `vec!`",
+            "you can use a slice directly",
+            snippet,
+            applicability,
+        );
+    }
+}
+
+fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
+    let ty = cx.typeck_results().expr_ty_adjusted(expr);
+    cx.layout_of(ty).map_or(0, |l| l.size.bytes())
+}
+
+/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
+fn vec_type(ty: Ty<'_>) -> Ty<'_> {
+    if let ty::Adt(_, substs) = ty.kind() {
+        substs.type_at(0)
+    } else {
+        panic!("The type of `vec!` is a not a struct?");
+    }
+}