]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/utils/mod.rs
Update needless_continue stderr
[rust.git] / clippy_lints / src / utils / mod.rs
index 3c806cc7ea587fcd06b368621f8ccfa0524baa58..4b3c84505aa5f4c6a7b692865f253c6958486364 100644 (file)
 
 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<T: LintContext>(cx: &T, span: Span) -> bool {
     true
 }
 
+/// Checks if given pattern is a wildcard (`_`)
+pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> 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<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 ///
 /// # 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<Span>,
+) -> 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<Span>,
     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<T: LintContext>(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<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
+    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<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
+    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<T: LintContext>(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<T: LintContext>(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<String>,
     default: &'a str,
+    indent_relative_to: Option<Span>,
 ) -> 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<usize>) -> 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<usize>, 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<Span> {
     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<Span> {
 pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
     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
+    }
+}