X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_lints%2Fsrc%2Futils%2Fmod.rs;h=49d9de35e182160155f06edf5bae6864c7142e2c;hb=52408f5b7d0392b008d647c5414e45dad4c4f5fd;hp=e5569647ff0cea93a20381cf64aead4e3b71ab95;hpb=c47a7e4ef2bdbec142e6dd59b65ef58d689f7c7d;p=rust.git diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e5569647ff0..49d9de35e18 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -43,10 +43,11 @@ use smallvec::SmallVec; use syntax::ast::{self, LitKind}; use syntax::attr; -use syntax::ext::hygiene::ExpnFormat; +use syntax::ext::hygiene::ExpnKind; use syntax::source_map::{Span, DUMMY_SP}; use syntax::symbol::{kw, Symbol}; +use crate::consts::{constant, Constant}; use crate::reexport::*; /// Returns `true` if the two spans come from differing expansions (i.e., one is @@ -88,19 +89,18 @@ pub fn in_constant(cx: &LateContext<'_, '_>, id: HirId) -> bool { node: ItemKind::Fn(_, header, ..), .. }) => header.constness == Constness::Const, + Node::ImplItem(&ImplItem { + node: ImplItemKind::Method(ref sig, _), + .. + }) => sig.header.constness == Constness::Const, _ => false, } } -/// Returns `true` if this `expn_info` was expanded by any macro or desugaring -pub fn in_macro_or_desugar(span: Span) -> bool { - span.ctxt().outer_expn_info().is_some() -} - -/// Returns `true` if this `expn_info` was expanded by any macro. +/// Returns `true` if this `span` was expanded by any macro. pub fn in_macro(span: Span) -> bool { - if let Some(info) = span.ctxt().outer_expn_info() { - if let ExpnFormat::CompilerDesugaring(..) = info.format { + if span.from_expansion() { + if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind { false } else { true @@ -131,6 +131,14 @@ pub fn match_type(cx: &LateContext<'_, '_>, ty: Ty<'_>, path: &[&str]) -> bool { } } +/// Checks if the type is equal to a diagnostic item +pub fn is_type_diagnostic_item(cx: &LateContext<'_, '_>, ty: Ty<'_>, diag_item: Symbol) -> bool { + match ty.sty { + ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), + _ => false, + } +} + /// Checks if the method call given in `expr` belongs to the given trait. pub fn match_trait_method(cx: &LateContext<'_, '_>, expr: &Expr, path: &[&str]) -> bool { let def_id = cx.tables.type_dependent_def_id(expr.hir_id).unwrap(); @@ -265,7 +273,21 @@ pub fn path_to_res(cx: &LateContext<'_, '_>, path: &[&str]) -> Option<(def::Res) } } +pub fn qpath_res(cx: &LateContext<'_, '_>, qpath: &hir::QPath, id: hir::HirId) -> Res { + match qpath { + hir::QPath::Resolved(_, path) => path.res, + hir::QPath::TypeRelative(..) => { + if cx.tcx.has_typeck_tables(id.owner_def_id()) { + cx.tcx.typeck_tables_of(id.owner_def_id()).qpath_res(qpath, id) + } else { + Res::Err + } + }, + } +} + /// Convenience function to get the `DefId` of a trait by path. +/// It could be a trait or trait alias. pub fn get_trait_def_id(cx: &LateContext<'_, '_>, path: &[&str]) -> Option { let res = match path_to_res(cx, path) { Some(res) => res, @@ -273,7 +295,8 @@ pub fn get_trait_def_id(cx: &LateContext<'_, '_>, path: &[&str]) -> Option Some(trait_id), + Res::Def(DefKind::Trait, trait_id) | Res::Def(DefKind::TraitAlias, trait_id) => Some(trait_id), + Res::Err => unreachable!("this trait resolution is impossible: {:?}", &path), _ => None, } } @@ -341,26 +364,28 @@ pub fn resolve_node(cx: &LateContext<'_, '_>, qpath: &QPath, id: HirId) -> Res { } /// Returns the method names and argument list of nested method call expressions that make up -/// `expr`. -pub fn method_calls(expr: &Expr, max_depth: usize) -> (Vec, Vec<&[Expr]>) { +/// `expr`. method/span lists are sorted with the most recent call first. +pub fn method_calls(expr: &Expr, max_depth: usize) -> (Vec, Vec<&[Expr]>, Vec) { let mut method_names = Vec::with_capacity(max_depth); let mut arg_lists = Vec::with_capacity(max_depth); + let mut spans = Vec::with_capacity(max_depth); let mut current = expr; for _ in 0..max_depth { - if let ExprKind::MethodCall(path, _, args) = ¤t.node { - if args.iter().any(|e| in_macro_or_desugar(e.span)) { + if let ExprKind::MethodCall(path, span, args) = ¤t.node { + if args.iter().any(|e| e.span.from_expansion()) { break; } method_names.push(path.ident.name); arg_lists.push(&**args); + spans.push(*span); current = &args[0]; } else { break; } } - (method_names, arg_lists) + (method_names, arg_lists, spans) } /// Matches an `Expr` against a chain of methods, and return the matched `Expr`s. @@ -376,7 +401,7 @@ pub fn method_chain_args<'a>(expr: &'a Expr, methods: &[&str]) -> Option first if let ExprKind::MethodCall(ref path, _, ref args) = current.node { if path.ident.name.as_str() == *method_name { - if args.iter().any(|e| in_macro_or_desugar(e.span)) { + if args.iter().any(|e| e.span.from_expansion()) { return None; } matched.push(&**args); // build up `matched` backwards @@ -395,16 +420,15 @@ pub fn method_chain_args<'a>(expr: &'a Expr, methods: &[&str]) -> Option, def_id: DefId) -> bool { - if let Some((entry_fn_def_id, _)) = cx.tcx.entry_fn(LOCAL_CRATE) { - return def_id == entry_fn_def_id; - } - false + cx.tcx + .entry_fn(LOCAL_CRATE) + .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id) } /// Gets the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); - match cx.tcx.hir().find_by_hir_id(parent_id) { + match cx.tcx.hir().find(parent_id) { Some(Node::Item(&Item { ref ident, .. })) => Some(ident.name), Some(Node::TraitItem(&TraitItem { ident, .. })) | Some(Node::ImplItem(&ImplItem { ident, .. })) => { Some(ident.name) @@ -471,7 +495,7 @@ pub fn snippet_with_applicability<'a, T: LintContext>( default: &'a str, applicability: &mut Applicability, ) -> Cow<'a, str> { - if *applicability != Applicability::Unspecified && in_macro_or_desugar(span) { + if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } snippet_opt(cx, span).map_or_else( @@ -536,7 +560,7 @@ pub fn last_line_of_span(cx: &T, span: Span) -> Span { pub fn expr_block<'a, T: LintContext>(cx: &T, expr: &Expr, option: Option, default: &'a str) -> Cow<'a, str> { let code = snippet_block(cx, expr.span, default); let string = option.unwrap_or_default(); - if in_macro_or_desugar(expr.span) { + if expr.span.from_expansion() { Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) } else if let ExprKind::Block(_, _) = expr.node { Cow::Owned(format!("{}{}", code, string)) @@ -596,7 +620,7 @@ pub fn get_parent_expr<'c>(cx: &'c LateContext<'_, '_>, e: &Expr) -> Option<&'c if hir_id == parent_id { return None; } - map.find_by_hir_id(parent_id).and_then(|node| { + map.find(parent_id).and_then(|node| { if let Node::Expr(parent) = node { Some(parent) } else { @@ -609,7 +633,7 @@ pub fn get_enclosing_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, hir_id: HirId) let map = &cx.tcx.hir(); let enclosing_node = map .get_enclosing_scope(hir_id) - .and_then(|enclosing_id| map.find_by_hir_id(enclosing_id)); + .and_then(|enclosing_id| map.find(enclosing_id)); if let Some(node) = enclosing_node { match node { Node::Block(block) => Some(block), @@ -659,6 +683,24 @@ fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { inner(ty, 0) } +/// Checks whether the given expression is a constant integer of the given value. +/// unlike `is_integer_literal`, this version does const folding +pub fn is_integer_const(cx: &LateContext<'_, '_>, e: &Expr, value: u128) -> bool { + if is_integer_literal(e, value) { + return true; + } + let map = cx.tcx.hir(); + let parent_item = map.get_parent_item(e.hir_id); + if let Some((Constant::Int(v), _)) = map + .maybe_body_owned_by(parent_item) + .and_then(|body_id| constant(cx, cx.tcx.body_tables(body_id), e)) + { + value == v + } else { + false + } +} + /// Checks whether the given expression is a constant literal of the given value. pub fn is_integer_literal(expr: &Expr, value: u128) -> bool { // FIXME: use constant folding @@ -686,12 +728,18 @@ pub fn is_adjusted(cx: &LateContext<'_, '_>, e: &Expr) -> bool { /// See also `is_direct_expn_of`. pub fn is_expn_of(mut span: Span, name: &str) -> Option { loop { - let span_name_span = span.ctxt().outer_expn_info().map(|ei| (ei.format.name(), ei.call_site)); + if span.from_expansion() { + let data = span.ctxt().outer_expn_data(); + let mac_name = data.kind.descr(); + let new_span = data.call_site; - match span_name_span { - Some((mac_name, new_span)) if mac_name.as_str() == name => return Some(new_span), - None => return None, - Some((_, new_span)) => span = new_span, + if mac_name.as_str() == name { + return Some(new_span); + } else { + span = new_span; + } + } else { + return None; } } } @@ -706,17 +754,24 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { /// `bar!` by /// `is_direct_expn_of`. pub fn is_direct_expn_of(span: Span, name: &str) -> Option { - let span_name_span = span.ctxt().outer_expn_info().map(|ei| (ei.format.name(), ei.call_site)); + if span.from_expansion() { + let data = span.ctxt().outer_expn_data(); + let mac_name = data.kind.descr(); + let new_span = data.call_site; - match span_name_span { - Some((mac_name, new_span)) if mac_name.as_str() == name => Some(new_span), - _ => None, + if mac_name.as_str() == name { + Some(new_span) + } else { + None + } + } else { + None } } /// Convenience function to get the return type of a function. pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: hir::HirId) -> Ty<'tcx> { - let fn_def_id = cx.tcx.hir().local_def_id_from_hir_id(fn_item); + let fn_def_id = cx.tcx.hir().local_def_id(fn_item); let ret_ty = cx.tcx.fn_sig(fn_def_id).output(); cx.tcx.erase_late_bound_regions(&ret_ty) } @@ -778,12 +833,12 @@ fn are_refutable<'a, I: Iterator>(cx: &LateContext<'_, '_>, mut PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat), PatKind::Lit(..) | PatKind::Range(..) => true, PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), - PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)), + PatKind::Or(ref pats) | PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)), PatKind::Struct(ref qpath, ref fields, _) => { if is_enum_variant(cx, qpath, pat.hir_id) { true } else { - are_refutable(cx, fields.iter().map(|field| &*field.node.pat)) + are_refutable(cx, fields.iter().map(|field| &*field.pat)) } }, PatKind::TupleStruct(ref qpath, ref pats, _) => { @@ -825,7 +880,7 @@ pub fn remove_blocks(expr: &Expr) -> &Expr { } } -pub fn is_self(slf: &Arg) -> bool { +pub fn is_self(slf: &Param) -> bool { if let PatKind::Binding(.., name, _) = slf.pat.node { name.name == kw::SelfLower } else { @@ -845,8 +900,8 @@ pub fn is_self_ty(slf: &hir::Ty) -> bool { false } -pub fn iter_input_pats<'tcx>(decl: &FnDecl, body: &'tcx Body) -> impl Iterator { - (0..decl.inputs.len()).map(move |i| &body.arguments[i]) +pub fn iter_input_pats<'tcx>(decl: &FnDecl, body: &'tcx Body) -> impl Iterator { + (0..decl.inputs.len()).map(move |i| &body.params[i]) } /// Checks if a given expression is a match expression expanded from the `?` @@ -1110,7 +1165,50 @@ fn test_without_block_comments_lines_without_block_comments() { } pub fn match_def_path<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, did: DefId, syms: &[&str]) -> bool { - // HACK: find a way to use symbols from clippy or just go fully to diagnostic items - let syms: Vec<_> = syms.iter().map(|sym| Symbol::intern(sym)).collect(); - cx.match_def_path(did, &syms) + let path = cx.get_def_path(did); + path.len() == syms.len() && path.into_iter().zip(syms.iter()).all(|(a, &b)| a.as_str() == b) +} + +/// Returns the list of condition expressions and the list of blocks in a +/// sequence of `if/else`. +/// E.g., this returns `([a, b], [c, d, e])` for the expression +/// `if a { c } else if b { d } else { e }`. +pub fn if_sequence(mut expr: &Expr) -> (SmallVec<[&Expr; 1]>, SmallVec<[&Block; 1]>) { + let mut conds = SmallVec::new(); + let mut blocks: SmallVec<[&Block; 1]> = SmallVec::new(); + + while let Some((ref cond, ref then_expr, ref else_expr)) = higher::if_block(&expr) { + conds.push(&**cond); + if let ExprKind::Block(ref block, _) = then_expr.node { + blocks.push(block); + } else { + panic!("ExprKind::If node is not an ExprKind::Block"); + } + + if let Some(ref else_expr) = *else_expr { + expr = else_expr; + } else { + break; + } + } + + // final `else {..}` + if !blocks.is_empty() { + if let ExprKind::Block(ref block, _) = expr.node { + blocks.push(&**block); + } + } + + (conds, blocks) +} + +pub fn parent_node_is_if_expr<'a, 'b>(expr: &Expr, cx: &LateContext<'a, 'b>) -> bool { + let parent_id = cx.tcx.hir().get_parent_node(expr.hir_id); + let parent_node = cx.tcx.hir().get(parent_id); + + match parent_node { + rustc::hir::Node::Expr(e) => higher::if_block(&e).is_some(), + rustc::hir::Node::Arm(e) => higher::if_block(&e.body).is_some(), + _ => false, + } }