X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;ds=sidebyside;f=clippy_lints%2Fsrc%2Futils%2Fmod.rs;h=4b3c84505aa5f4c6a7b692865f253c6958486364;hb=f1a72e992d75983e1a2503b4e577971d7e018f1d;hp=3c806cc7ea587fcd06b368621f8ccfa0524baa58;hpb=e8642c7a2900bed28003a98d4db8b62290ac802f;p=rust.git diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3c806cc7ea5..4b3c84505aa 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -25,27 +25,30 @@ use if_chain::if_chain; use matches::matches; -use rustc::hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc::lint::{LateContext, Level, Lint, LintContext}; +use rustc::hir::map::Map; use rustc::traits; +use rustc::traits::predicate_for_trait_def; use rustc::ty::{ self, layout::{self, IntegerExt}, subst::GenericArg, Binder, Ty, TyCtxt, }; +use rustc_attr as attr; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::*; -use rustc_span::hygiene::ExpnKind; -use rustc_span::source_map::{Span, DUMMY_SP}; -use rustc_span::symbol::{kw, Symbol}; +use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_span::hygiene::{ExpnKind, MacroKind}; +use rustc_span::source_map::original_sp; +use rustc_span::symbol::{self, kw, Symbol}; +use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use smallvec::SmallVec; use syntax::ast::{self, Attribute, LitKind}; -use syntax::attr; use crate::consts::{constant, Constant}; use crate::reexport::*; @@ -125,6 +128,14 @@ pub fn is_present_in_source(cx: &T, span: Span) -> bool { true } +/// Checks if given pattern is a wildcard (`_`) +pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { + match pat.kind { + PatKind::Wild => true, + _ => false, + } +} + /// Checks if type is struct, enum or union type with the given def path. pub fn match_type(cx: &LateContext<'_, '_>, ty: Ty<'_>, path: &[&str]) -> bool { match ty.kind { @@ -312,7 +323,8 @@ pub fn implements_trait<'a, 'tcx>( ty_params: &[GenericArg<'tcx>], ) -> bool { let ty = cx.tcx.erase_regions(&ty); - let obligation = cx.tcx.predicate_for_trait_def( + let obligation = predicate_for_trait_def( + cx.tcx, cx.param_env, traits::ObligationCause::dummy(), trait_id, @@ -346,7 +358,7 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> O if_chain! { if parent_impl != hir::CRATE_HIR_ID; if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl); - if let hir::ItemKind::Impl(_, _, _, _, trait_ref, _, _) = &item.kind; + if let hir::ItemKind::Impl{ of_trait: trait_ref, .. } = &item.kind; then { return trait_ref.as_ref(); } } None @@ -453,12 +465,14 @@ struct ContainsName { } impl<'tcx> Visitor<'tcx> for ContainsName { + type Map = Map<'tcx>; + fn visit_name(&mut self, _: Span, name: Name) { if self.name == name { self.result = true; } } - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { NestedVisitorMap::None } } @@ -528,11 +542,17 @@ pub fn snippet_opt(cx: &T, span: Span) -> Option { /// /// # Example /// ```rust,ignore -/// snippet_block(cx, expr.span, "..") +/// snippet_block(cx, expr.span, "..", None) /// ``` -pub fn snippet_block<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { +pub fn snippet_block<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + indent_relative_to: Option, +) -> Cow<'a, str> { let snip = snippet(cx, span, default); - trim_multiline(snip, true) + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + trim_multiline(snip, true, indent) } /// Same as `snippet_block`, but adapts the applicability level by the rules of @@ -541,18 +561,73 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( cx: &T, span: Span, default: &'a str, + indent_relative_to: Option, applicability: &mut Applicability, ) -> Cow<'a, str> { let snip = snippet_with_applicability(cx, span, default, applicability); - trim_multiline(snip, true) + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + trim_multiline(snip, true, indent) +} + +/// Returns a new Span that extends the original Span to the first non-whitespace char of the first +/// line. +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ +/// // will be converted to +/// let x = (); +/// // ^^^^^^^^^^ +/// ``` +pub fn first_line_of_span(cx: &T, span: Span) -> Span { + if let Some(first_char_pos) = first_char_in_first_line(cx, span) { + span.with_lo(first_char_pos) + } else { + span + } +} + +fn first_char_in_first_line(cx: &T, span: Span) -> Option { + let line_span = line_span(cx, span); + if let Some(snip) = snippet_opt(cx, line_span) { + snip.find(|c: char| !c.is_whitespace()) + .map(|pos| line_span.lo() + BytePos::from_usize(pos)) + } else { + None + } +} + +/// Returns the indentation of the line of a span +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ -- will return 0 +/// let x = (); +/// // ^^ -- will return 4 +/// ``` +pub fn indent_of(cx: &T, span: Span) -> Option { + if let Some(snip) = snippet_opt(cx, line_span(cx, span)) { + snip.find(|c: char| !c.is_whitespace()) + } else { + None + } } -/// Returns a new Span that covers the full last line of the given Span -pub fn last_line_of_span(cx: &T, span: Span) -> Span { +/// Extends the span to the beginning of the spans line, incl. whitespaces. +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ +/// // will be converted to +/// let x = (); +/// // ^^^^^^^^^^^^^^ +/// ``` +fn line_span(cx: &T, span: Span) -> Span { + let span = original_sp(span, DUMMY_SP); let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); let line_no = source_map_and_line.line; - let line_start = &source_map_and_line.sf.lines[line_no]; - Span::new(*line_start, span.hi(), span.ctxt()) + let line_start = source_map_and_line.sf.lines[line_no]; + Span::new(line_start, span.hi(), span.ctxt()) } /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. @@ -562,8 +637,9 @@ pub fn expr_block<'a, T: LintContext>( expr: &Expr<'_>, option: Option, default: &'a str, + indent_relative_to: Option, ) -> Cow<'a, str> { - let code = snippet_block(cx, expr.span, default); + let code = snippet_block(cx, expr.span, default, indent_relative_to); let string = option.unwrap_or_default(); if expr.span.from_expansion() { Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) @@ -578,14 +654,14 @@ pub fn expr_block<'a, T: LintContext>( /// Trim indentation from a multiline string with possibility of ignoring the /// first line. -pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool) -> Cow<'_, str> { - let s_space = trim_multiline_inner(s, ignore_first, ' '); - let s_tab = trim_multiline_inner(s_space, ignore_first, '\t'); - trim_multiline_inner(s_tab, ignore_first, ' ') +fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { + let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); + let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); + trim_multiline_inner(s_tab, ignore_first, indent, ' ') } -fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, ch: char) -> Cow<'_, str> { - let x = s +fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option, ch: char) -> Cow<'_, str> { + let mut x = s .lines() .skip(ignore_first as usize) .filter_map(|l| { @@ -598,6 +674,9 @@ fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, ch: char) -> Cow<'_ }) .min() .unwrap_or(0); + if let Some(indent) = indent { + x = x.saturating_sub(indent); + } if x > 0 { Cow::Owned( s.lines() @@ -736,14 +815,15 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { loop { if span.from_expansion() { let data = span.ctxt().outer_expn_data(); - let mac_name = data.kind.descr(); let new_span = data.call_site; - if mac_name.as_str() == name { - return Some(new_span); - } else { - span = new_span; + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { + if mac_name.as_str() == name { + return Some(new_span); + } } + + span = new_span; } else { return None; } @@ -763,17 +843,16 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { pub fn is_direct_expn_of(span: Span, name: &str) -> Option { if span.from_expansion() { let data = span.ctxt().outer_expn_data(); - let mac_name = data.kind.descr(); let new_span = data.call_site; - if mac_name.as_str() == name { - Some(new_span) - } else { - None + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { + if mac_name.as_str() == name { + return Some(new_span); + } } - } else { - None } + + None } /// Convenience function to get the return type of a function. @@ -873,12 +952,11 @@ pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { /// /// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return /// themselves. -pub fn remove_blocks<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { - if let ExprKind::Block(ref block, _) = expr.kind { - if block.stmts.is_empty() { - if let Some(ref expr) = block.expr { - return remove_blocks(expr); - } +pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + while let ExprKind::Block(ref block, ..) = expr.kind { + match (block.stmts.is_empty(), block.expr.as_ref()) { + (true, Some(e)) => expr = e, + _ => break, } } expr @@ -1279,7 +1357,7 @@ pub fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> boo Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), Opaque(ref def_id, _) => { for (predicate, _) in cx.tcx.predicates_of(*def_id).predicates { - if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate { + if let ty::Predicate::Trait(ref poly_trait_predicate, _) = predicate { if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() { return true; } @@ -1323,3 +1401,28 @@ pub fn is_must_use_func_call(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool false } } + +pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { + krate.attrs.iter().any(|attr| { + if let ast::AttrKind::Normal(ref attr) = attr.kind { + attr.path == symbol::sym::no_std + } else { + false + } + }) +} + +/// Check if parent of a hir node is a trait implementation block. +/// For example, `f` in +/// ```rust,ignore +/// impl Trait for S { +/// fn f() {} +/// } +/// ``` +pub fn is_trait_impl_item(cx: &LateContext<'_, '_>, hir_id: HirId) -> bool { + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. }) + } else { + false + } +}