diff options
| author | flip1995 <philipp.krones@embecosm.com> | 2021-05-06 11:22:33 +0200 |
|---|---|---|
| committer | flip1995 <philipp.krones@embecosm.com> | 2021-05-06 11:32:03 +0200 |
| commit | 767cc7cd894085c67776c96b7e0bfed95031c2fd (patch) | |
| tree | 8a3fcf573f681e908f6466aa1349e2938c75705b /clippy_utils/src | |
| parent | 45d4e61098f9e0593fd0d28ba07a738d7e909212 (diff) | |
| parent | 796a6f00e19ac44b4f75cf47ef3cdcf4fdaed123 (diff) | |
| download | rust-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.rs | 8 | ||||
| -rw-r--r-- | clippy_utils/src/hir_utils.rs | 93 | ||||
| -rw-r--r-- | clippy_utils/src/lib.rs | 50 | ||||
| -rw-r--r-- | clippy_utils/src/paths.rs | 13 | ||||
| -rw-r--r-- | clippy_utils/src/source.rs | 53 | ||||
| -rw-r--r-- | clippy_utils/src/visitors.rs | 55 |
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)); +} |
