about summary refs log tree commit diff
path: root/clippy_utils/src
diff options
context:
space:
mode:
authorflip1995 <philipp.krones@embecosm.com>2021-05-06 11:22:33 +0200
committerflip1995 <philipp.krones@embecosm.com>2021-05-06 11:32:03 +0200
commit767cc7cd894085c67776c96b7e0bfed95031c2fd (patch)
tree8a3fcf573f681e908f6466aa1349e2938c75705b /clippy_utils/src
parent45d4e61098f9e0593fd0d28ba07a738d7e909212 (diff)
parent796a6f00e19ac44b4f75cf47ef3cdcf4fdaed123 (diff)
downloadrust-767cc7cd894085c67776c96b7e0bfed95031c2fd.tar.gz
rust-767cc7cd894085c67776c96b7e0bfed95031c2fd.zip
Merge remote-tracking branch 'upstream/master' into rustup
Diffstat (limited to 'clippy_utils/src')
-rw-r--r--clippy_utils/src/diagnostics.rs8
-rw-r--r--clippy_utils/src/hir_utils.rs93
-rw-r--r--clippy_utils/src/lib.rs50
-rw-r--r--clippy_utils/src/paths.rs13
-rw-r--r--clippy_utils/src/source.rs53
-rw-r--r--clippy_utils/src/visitors.rs55
6 files changed, 238 insertions, 34 deletions
diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs
index 7f827f1759d..a4efae54894 100644
--- a/clippy_utils/src/diagnostics.rs
+++ b/clippy_utils/src/diagnostics.rs
@@ -1,4 +1,12 @@
 //! Clippy wrappers around rustc's diagnostic functions.
+//!
+//! These functions are used by the `INTERNAL_METADATA_COLLECTOR` lint to collect the corresponding
+//! lint applicability. Please make sure that you update the `LINT_EMISSION_FUNCTIONS` variable in
+//! `clippy_lints::utils::internal_lints::metadata_collector` when a new function is added
+//! or renamed.
+//!
+//! Thank you!
+//! ~The `INTERNAL_METADATA_COLLECTOR` lint
 
 use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir::HirId;
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index 07ae6e924e2..3b01158acd9 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -713,7 +713,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 self.hash_expr(e);
 
                 for arm in arms {
-                    // TODO: arm.pat?
+                    self.hash_pat(arm.pat);
                     if let Some(ref e) = arm.guard {
                         self.hash_guard(e);
                     }
@@ -791,6 +791,72 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
         // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
     }
 
+    pub fn hash_pat(&mut self, pat: &Pat<'_>) {
+        std::mem::discriminant(&pat.kind).hash(&mut self.s);
+        match pat.kind {
+            PatKind::Binding(ann, _, _, pat) => {
+                ann.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
+                if let Some(pat) = pat {
+                    self.hash_pat(pat);
+                }
+            },
+            PatKind::Box(pat) => self.hash_pat(pat),
+            PatKind::Lit(expr) => self.hash_expr(expr),
+            PatKind::Or(pats) => {
+                for pat in pats {
+                    self.hash_pat(pat);
+                }
+            },
+            PatKind::Path(ref qpath) => self.hash_qpath(qpath),
+            PatKind::Range(s, e, i) => {
+                if let Some(s) = s {
+                    self.hash_expr(s);
+                }
+                if let Some(e) = e {
+                    self.hash_expr(e);
+                }
+                i.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
+            },
+            PatKind::Ref(pat, m) => {
+                self.hash_pat(pat);
+                m.hash(&mut self.s);
+            },
+            PatKind::Slice(l, m, r) => {
+                for pat in l {
+                    self.hash_pat(pat);
+                }
+                if let Some(pat) = m {
+                    self.hash_pat(pat);
+                }
+                for pat in r {
+                    self.hash_pat(pat);
+                }
+            },
+            PatKind::Struct(ref qpath, fields, e) => {
+                self.hash_qpath(qpath);
+                for f in fields {
+                    self.hash_name(f.ident.name);
+                    self.hash_pat(f.pat);
+                }
+                e.hash(&mut self.s)
+            },
+            PatKind::Tuple(pats, e) => {
+                for pat in pats {
+                    self.hash_pat(pat);
+                }
+                e.hash(&mut self.s);
+            },
+            PatKind::TupleStruct(ref qpath, pats, e) => {
+                self.hash_qpath(qpath);
+                for pat in pats {
+                    self.hash_pat(pat);
+                }
+                e.hash(&mut self.s);
+            },
+            PatKind::Wild => {},
+        }
+    }
+
     pub fn hash_path(&mut self, path: &Path<'_>) {
         match path.res {
             // constant hash since equality is dependant on inter-expression context
@@ -808,6 +874,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 
         match &b.kind {
             StmtKind::Local(local) => {
+                self.hash_pat(local.pat);
                 if let Some(ref init) = local.init {
                     self.hash_expr(init);
                 }
@@ -827,7 +894,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
         }
     }
 
-    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
+    pub fn hash_lifetime(&mut self, lifetime: Lifetime) {
         std::mem::discriminant(&lifetime.name).hash(&mut self.s);
         if let LifetimeName::Param(ref name) = lifetime.name {
             std::mem::discriminant(name).hash(&mut self.s);
@@ -844,12 +911,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
     }
 
     pub fn hash_ty(&mut self, ty: &Ty<'_>) {
-        self.hash_tykind(&ty.kind);
-    }
-
-    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
-        std::mem::discriminant(ty).hash(&mut self.s);
-        match ty {
+        std::mem::discriminant(&ty.kind).hash(&mut self.s);
+        match ty.kind {
             TyKind::Slice(ty) => {
                 self.hash_ty(ty);
             },
@@ -857,11 +920,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 self.hash_ty(ty);
                 self.hash_body(anon_const.body);
             },
-            TyKind::Ptr(mut_ty) => {
+            TyKind::Ptr(ref mut_ty) => {
                 self.hash_ty(&mut_ty.ty);
                 mut_ty.mutbl.hash(&mut self.s);
             },
-            TyKind::Rptr(lifetime, mut_ty) => {
+            TyKind::Rptr(lifetime, ref mut_ty) => {
                 self.hash_lifetime(lifetime);
                 self.hash_ty(&mut_ty.ty);
                 mut_ty.mutbl.hash(&mut self.s);
@@ -883,11 +946,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 bfn.decl.c_variadic.hash(&mut self.s);
             },
             TyKind::Tup(ty_list) => {
-                for ty in *ty_list {
+                for ty in ty_list {
                     self.hash_ty(ty);
                 }
             },
-            TyKind::Path(qpath) => match qpath {
+            TyKind::Path(ref qpath) => match qpath {
                 QPath::Resolved(ref maybe_ty, ref path) => {
                     if let Some(ref ty) = maybe_ty {
                         self.hash_ty(ty);
@@ -927,9 +990,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 
     fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
         for arg in arg_list {
-            match arg {
-                GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
-                GenericArg::Type(ref ty) => self.hash_ty(&ty),
+            match *arg {
+                GenericArg::Lifetime(l) => self.hash_lifetime(l),
+                GenericArg::Type(ref ty) => self.hash_ty(ty),
                 GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
             }
         }
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index e81a92eb74c..7ca9d3a860d 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -1,7 +1,6 @@
 #![feature(box_patterns)]
 #![feature(in_band_lifetimes)]
 #![feature(iter_zip)]
-#![cfg_attr(bootstrap, feature(or_patterns))]
 #![feature(rustc_private)]
 #![recursion_limit = "512"]
 #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
@@ -14,6 +13,7 @@ extern crate rustc_attr;
 extern crate rustc_data_structures;
 extern crate rustc_errors;
 extern crate rustc_hir;
+extern crate rustc_hir_pretty;
 extern crate rustc_infer;
 extern crate rustc_lexer;
 extern crate rustc_lint;
@@ -61,12 +61,12 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::LangItem::{ResultErr, ResultOk};
 use rustc_hir::{
     def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
-    ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment,
-    QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
+    ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
+    PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
 };
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::exports::Export;
@@ -821,7 +821,13 @@ pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
 
 /// Gets the parent expression, if any –- this is useful to constrain a lint.
 pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    match get_parent_node(cx.tcx, e.hir_id) {
+    get_parent_expr_for_hir(cx, e.hir_id)
+}
+
+/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
+/// constraint lints
+pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
+    match get_parent_node(cx.tcx, hir_id) {
         Some(Node::Expr(parent)) => Some(parent),
         _ => None,
     }
@@ -1301,6 +1307,40 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
     (conds, blocks)
 }
 
+/// Checks if the given function kind is an async function.
+pub fn is_async_fn(kind: FnKind) -> bool {
+    matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async)
+}
+
+/// Peels away all the compiler generated code surrounding the body of an async function,
+pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Call(
+        _,
+        &[Expr {
+            kind: ExprKind::Closure(_, _, body, _, _),
+            ..
+        }],
+    ) = body.value.kind
+    {
+        if let ExprKind::Block(
+            Block {
+                stmts: [],
+                expr:
+                    Some(Expr {
+                        kind: ExprKind::DropTemps(expr),
+                        ..
+                    }),
+                ..
+            },
+            _,
+        ) = tcx.hir().body(body).value.kind
+        {
+            return Some(expr);
+        }
+    };
+    None
+}
+
 // Finds the `#[must_use]` attribute, if any
 pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
     attrs.iter().find(|a| a.has_name(sym::must_use))
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 1fa439639b2..8037d670500 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -5,6 +5,17 @@
 //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 
 pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
+#[cfg(feature = "metadata-collector-lint")]
+pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
+#[cfg(feature = "metadata-collector-lint")]
+pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
+    ["rustc_lint_defs", "Applicability", "Unspecified"],
+    ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
+    ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
+    ["rustc_lint_defs", "Applicability", "MachineApplicable"],
+];
+#[cfg(feature = "metadata-collector-lint")]
+pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
@@ -72,7 +83,7 @@ pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
 pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
-#[cfg(feature = "internal-lints")]
+#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
 pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index 53180d1f9f5..4d49b43bde9 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -280,17 +280,17 @@ pub fn snippet_with_context(
     default: &'a str,
     applicability: &mut Applicability,
 ) -> (Cow<'a, str>, bool) {
-    let outer_span = hygiene::walk_chain(span, outer);
-    let (span, is_macro_call) = if outer_span.ctxt() == outer {
-        (outer_span, span.ctxt() != outer)
-    } else {
-        // The span is from a macro argument, and the outer context is the macro using the argument
-        if *applicability != Applicability::Unspecified {
-            *applicability = Applicability::MaybeIncorrect;
-        }
-        // TODO: get the argument span.
-        (span, false)
-    };
+    let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else(
+        || {
+            // The span is from a macro argument, and the outer context is the macro using the argument
+            if *applicability != Applicability::Unspecified {
+                *applicability = Applicability::MaybeIncorrect;
+            }
+            // TODO: get the argument span.
+            (span, false)
+        },
+        |outer_span| (outer_span, span.ctxt() != outer),
+    );
 
     (
         snippet_with_applicability(cx, span, default, applicability),
@@ -298,6 +298,37 @@ pub fn snippet_with_context(
     )
 }
 
+/// Walks the span up to the target context, thereby returning the macro call site if the span is
+/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
+/// case of the span being in a macro expansion, but the target context is from expanding a macro
+/// argument.
+///
+/// Given the following
+///
+/// ```rust,ignore
+/// macro_rules! m { ($e:expr) => { f($e) }; }
+/// g(m!(0))
+/// ```
+///
+/// If called with a span of the call to `f` and a context of the call to `g` this will return a
+/// span containing `m!(0)`. However, if called with a span of the literal `0` this will give a span
+/// containing `0` as the context is the same as the outer context.
+///
+/// This will traverse through multiple macro calls. Given the following:
+///
+/// ```rust,ignore
+/// macro_rules! m { ($e:expr) => { n!($e, 0) }; }
+/// macro_rules! n { ($e:expr, $f:expr) => { f($e, $f) }; }
+/// g(m!(0))
+/// ```
+///
+/// If called with a span of the call to `f` and a context of the call to `g` this will return a
+/// span containing `m!(0)`.
+pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
+    let outer_span = hygiene::walk_chain(span, outer);
+    (outer_span.ctxt() == outer).then(|| outer_span)
+}
+
 /// Removes block comments from the given `Vec` of lines.
 ///
 /// # Examples
diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs
index 5a8c629e333..d431bdf34ee 100644
--- a/clippy_utils/src/visitors.rs
+++ b/clippy_utils/src/visitors.rs
@@ -1,7 +1,7 @@
 use crate::path_to_local_id;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Arm, Body, Expr, HirId, Stmt};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 
@@ -188,3 +188,54 @@ impl<'v> Visitor<'v> for LocalUsedVisitor<'v> {
         NestedVisitorMap::OnlyBodies(self.hir)
     }
 }
+
+pub trait Visitable<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V);
+}
+impl Visitable<'tcx> for &'tcx Expr<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_expr(self)
+    }
+}
+impl Visitable<'tcx> for &'tcx Block<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_block(self)
+    }
+}
+impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_stmt(self)
+    }
+}
+impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_body(self)
+    }
+}
+impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_arm(self)
+    }
+}
+
+pub fn visit_break_exprs<'tcx>(
+    node: impl Visitable<'tcx>,
+    f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
+) {
+    struct V<F>(F);
+    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V<F> {
+        type Map = ErasedMap<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            if let ExprKind::Break(dest, sub_expr) = e.kind {
+                self.0(e, dest, sub_expr)
+            }
+            walk_expr(self, e);
+        }
+    }
+
+    node.visit(&mut V(f));
+}