]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/methods/mod.rs
rustup https://github.com/rust-lang/rust/pull/65535
[rust.git] / clippy_lints / src / methods / mod.rs
index cc22d3b9865fb0f3a8abdc34c0a75e35b24bd2ee..e7b07bb1c1b6c488c3defaa8b464187f43567da0 100644 (file)
@@ -1,3 +1,5 @@
+mod inefficient_to_string;
+mod manual_saturating_arithmetic;
 mod option_map_unwrap_or;
 mod unnecessary_filter_map;
 
 use rustc_errors::Applicability;
 use syntax::ast;
 use syntax::source_map::Span;
-use syntax::symbol::LocalInternedString;
+use syntax::symbol::{sym, LocalInternedString, Symbol};
 
-use crate::utils::sugg;
 use crate::utils::usage::mutated_variables;
 use crate::utils::{
     get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy,
-    is_ctor_function, is_expn_of, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method,
-    match_type, match_var, method_calls, method_chain_args, remove_blocks, return_ty, same_tys, single_segment_path,
-    snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_sugg,
-    span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
+    is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
+    match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
+    remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability,
+    snippet_with_macro_callsite, span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then,
+    span_note_and_lint, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
 };
-use crate::utils::{paths, span_help_and_lint};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for `.unwrap()` calls on `Option`s.
@@ -72,7 +73,7 @@
     /// **Known problems:** None.
     ///
     /// **Example:**
-    /// Using unwrap on an `Option`:
+    /// Using unwrap on an `Result`:
     ///
     /// ```rust
     /// let res: Result<usize, ()> = Ok(1);
     "using `Result.unwrap()`, which might be better handled"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for `.expect()` calls on `Option`s.
+    ///
+    /// **Why is this bad?** Usually it is better to handle the `None` case. Still,
+    ///  for a lot of quick-and-dirty code, `expect` is a good choice, which is why
+    ///  this lint is `Allow` by default.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// Using expect on an `Option`:
+    ///
+    /// ```rust
+    /// let opt = Some(1);
+    /// opt.expect("one");
+    /// ```
+    ///
+    /// Better:
+    ///
+    /// ```ignore
+    /// let opt = Some(1);
+    /// opt?;
+    /// # Some::<()>(())
+    /// ```
+    pub OPTION_EXPECT_USED,
+    restriction,
+    "using `Option.expect()`, which might be better handled"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for `.expect()` calls on `Result`s.
+    ///
+    /// **Why is this bad?** `result.expect()` will let the thread panic on `Err`
+    /// values. Normally, you want to implement more sophisticated error handling,
+    /// and propagate errors upwards with `try!`.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// Using expect on an `Result`:
+    ///
+    /// ```rust
+    /// let res: Result<usize, ()> = Ok(1);
+    /// res.expect("one");
+    /// ```
+    ///
+    /// Better:
+    ///
+    /// ```
+    /// let res: Result<usize, ()> = Ok(1);
+    /// res?;
+    /// # Ok::<(), ()>(())
+    /// ```
+    pub RESULT_EXPECT_USED,
+    restriction,
+    "using `Result.expect()`, which might be better handled"
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for methods that should live in a trait
     /// implementation of a `std` trait (see [llogiq's blog
     "using `clone` on `&&T`"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for usage of `.to_string()` on an `&&T` where
+    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
+    ///
+    /// **Why is this bad?** This bypasses the specialized implementation of
+    /// `ToString` and instead goes through the more expensive string formatting
+    /// facilities.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// // Generic implementation for `T: Display` is used (slow)
+    /// ["foo", "bar"].iter().map(|s| s.to_string());
+    ///
+    /// // OK, the specialized impl is used
+    /// ["foo", "bar"].iter().map(|&s| s.to_string());
+    /// ```
+    pub INEFFICIENT_TO_STRING,
+    perf,
+    "using `to_string` on `&&T` where `T: ToString`"
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for `new` not returning `Self`.
     ///
     "suspicious usage of map"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.
+    ///
+    /// **Why is this bad?** For most types, this is undefined behavior.
+    ///
+    /// **Known problems:** For now, we accept empty tuples and tuples / arrays
+    /// of `MaybeUninit`. There may be other types that allow uninitialized
+    /// data, but those are not yet rigorously defined.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// // Beware the UB
+    /// use std::mem::MaybeUninit;
+    ///
+    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+    /// ```
+    ///
+    /// Note that the following is OK:
+    ///
+    /// ```rust
+    /// use std::mem::MaybeUninit;
+    ///
+    /// let _: [MaybeUninit<bool>; 5] = unsafe {
+    ///     MaybeUninit::uninit().assume_init()
+    /// };
+    /// ```
+    pub UNINIT_ASSUMED_INIT,
+    correctness,
+    "`MaybeUninit::uninit().assume_init()`"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
+    ///
+    /// **Why is this bad?** These can be written simply with `saturating_add/sub` methods.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// # let y: u32 = 0;
+    /// # let x: u32 = 100;
+    /// let add = x.checked_add(y).unwrap_or(u32::max_value());
+    /// let sub = x.checked_sub(y).unwrap_or(u32::min_value());
+    /// ```
+    ///
+    /// can be written using dedicated methods for saturating addition/subtraction as:
+    ///
+    /// ```rust
+    /// # let y: u32 = 0;
+    /// # let x: u32 = 100;
+    /// let add = x.saturating_add(y);
+    /// let sub = x.saturating_sub(y);
+    /// ```
+    pub MANUAL_SATURATING_ARITHMETIC,
+    style,
+    "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
+}
+
 declare_lint_pass!(Methods => [
     OPTION_UNWRAP_USED,
     RESULT_UNWRAP_USED,
+    OPTION_EXPECT_USED,
+    RESULT_EXPECT_USED,
     SHOULD_IMPLEMENT_TRAIT,
     WRONG_SELF_CONVENTION,
     WRONG_PUB_SELF_CONVENTION,
     CLONE_ON_COPY,
     CLONE_ON_REF_PTR,
     CLONE_DOUBLE_REF,
+    INEFFICIENT_TO_STRING,
     NEW_RET_NO_SELF,
     SINGLE_CHAR_PATTERN,
     SEARCH_IS_SOME,
     INTO_ITER_ON_ARRAY,
     INTO_ITER_ON_REF,
     SUSPICIOUS_MAP,
+    UNINIT_ASSUMED_INIT,
+    MANUAL_SATURATING_ARITHMETIC,
 ]);
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
@@ -1000,7 +1147,7 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
             return;
         }
 
-        let (method_names, arg_lists) = method_calls(expr, 2);
+        let (method_names, arg_lists, method_spans) = method_calls(expr, 2);
         let method_names: Vec<LocalInternedString> = method_names.iter().map(|s| s.as_str()).collect();
         let method_names: Vec<&str> = method_names.iter().map(std::convert::AsRef::as_ref).collect();
 
@@ -1009,6 +1156,7 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
             ["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true),
             ["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),
             ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
+            ["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
             ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0]),
             ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
             ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
@@ -1020,11 +1168,15 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
             ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
             ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
             ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
-            ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0]),
+            ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]),
             ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]),
-            ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0]),
-            ["is_some", "position"] => lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0]),
-            ["is_some", "rposition"] => lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0]),
+            ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]),
+            ["is_some", "position"] => {
+                lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1])
+            },
+            ["is_some", "rposition"] => {
+                lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
+            },
             ["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
             ["as_ptr", "unwrap"] | ["as_ptr", "expect"] => {
                 lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0])
@@ -1035,13 +1187,19 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
             ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
             ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
             ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
-            ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0]),
+            ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
             ["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
             ["count", "map"] => lint_suspicious_map(cx, expr),
+            ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr),
+            ["unwrap_or", arith @ "checked_add"]
+            | ["unwrap_or", arith @ "checked_sub"]
+            | ["unwrap_or", arith @ "checked_mul"] => {
+                manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..])
+            },
             _ => {},
         }
 
-        match expr.node {
+        match expr.kind {
             hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args) => {
                 lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
                 lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
@@ -1051,9 +1209,12 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
                     lint_clone_on_copy(cx, expr, &args[0], self_ty);
                     lint_clone_on_ref_ptr(cx, expr, &args[0]);
                 }
+                if args.len() == 1 && method_call.ident.name == sym!(to_string) {
+                    inefficient_to_string::lint(cx, expr, &args[0], self_ty);
+                }
 
-                match self_ty.sty {
-                    ty::Ref(_, ty, _) if ty.sty == ty::Str => {
+                match self_ty.kind {
+                    ty::Ref(_, ty, _) if ty.kind == ty::Str => {
                         for &(method, pos) in &PATTERN_METHODS {
                             if method_call.ident.name.as_str() == method && args.len() > pos {
                                 lint_single_char_pattern(cx, expr, &args[pos]);
@@ -1091,9 +1252,9 @@ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::
         let def_id = cx.tcx.hir().local_def_id(item.hir_id);
         let ty = cx.tcx.type_of(def_id);
         if_chain! {
-            if let hir::ImplItemKind::Method(ref sig, id) = impl_item.node;
+            if let hir::ImplItemKind::Method(ref sig, id) = impl_item.kind;
             if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next();
-            if let hir::ItemKind::Impl(_, _, _, _, None, _, _) = item.node;
+            if let hir::ItemKind::Impl(_, _, _, _, None, _, _) = item.kind;
 
             let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
             let method_sig = cx.tcx.fn_sig(method_def_id);
@@ -1150,7 +1311,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::
             }
         }
 
-        if let hir::ImplItemKind::Method(_, _) = impl_item.node {
+        if let hir::ImplItemKind::Method(_, _) = impl_item.kind {
             let ret_ty = return_ty(cx, impl_item.hir_id);
 
             // walk the return type and check for Self (this does not check associated types)
@@ -1159,9 +1320,9 @@ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::
             }
 
             // if return type is impl trait, check the associated types
-            if let ty::Opaque(def_id, _) = ret_ty.sty {
+            if let ty::Opaque(def_id, _) = ret_ty.kind {
                 // one of the associated types must be Self
-                for predicate in &cx.tcx.predicates_of(def_id).predicates {
+                for predicate in cx.tcx.predicates_of(def_id).predicates {
                     match predicate {
                         (Predicate::Projection(poly_projection_predicate), _) => {
                             let binder = poly_projection_predicate.ty();
@@ -1208,24 +1369,15 @@ struct FunCallFinder<'a, 'tcx> {
 
     impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
         fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
-            let call_found = match &expr.node {
+            let call_found = match &expr.kind {
                 // ignore enum and struct constructors
-                hir::ExprKind::Call(..) => !is_ctor_function(self.cx, expr),
+                hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
                 hir::ExprKind::MethodCall(..) => true,
                 _ => false,
             };
 
             if call_found {
-                // don't lint for constant values
-                let owner_def = self.cx.tcx.hir().get_parent_did(expr.hir_id);
-                let promotable = self
-                    .cx
-                    .tcx
-                    .rvalue_promotable_map(owner_def)
-                    .contains(&expr.hir_id.local_id);
-                if !promotable {
-                    self.found |= true;
-                }
+                self.found |= true;
             }
 
             if !self.found {
@@ -1251,7 +1403,7 @@ fn check_unwrap_or_default(
         if_chain! {
             if !or_has_args;
             if name == "unwrap_or";
-            if let hir::ExprKind::Path(ref qpath) = fun.node;
+            if let hir::ExprKind::Path(ref qpath) = fun.kind;
             let path = &*last_path_segment(qpath).ident.as_str();
             if ["default", "new"].contains(&path);
             let arg_ty = cx.tables.expr_ty(arg);
@@ -1305,6 +1457,7 @@ fn check_general_case<'a, 'tcx>(
 
             let mut finder = FunCallFinder { cx: &cx, found: false };
             if { finder.visit_expr(&arg); finder.found };
+            if !contains_return(&arg);
 
             let self_ty = cx.tables.expr_ty(self_expr);
 
@@ -1334,7 +1487,7 @@ fn check_general_case<'a, 'tcx>(
     }
 
     if args.len() == 2 {
-        match args[1].node {
+        match args[1].kind {
             hir::ExprKind::Call(ref fun, ref or_args) => {
                 let or_has_args = !or_args.is_empty();
                 if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
@@ -1373,7 +1526,7 @@ fn lint_expect_fun_call(cx: &LateContext<'_, '_>, expr: &hir::Expr, method_span:
     fn get_arg_root<'a>(cx: &LateContext<'_, '_>, arg: &'a hir::Expr) -> &'a hir::Expr {
         let mut arg_root = arg;
         loop {
-            arg_root = match &arg_root.node {
+            arg_root = match &arg_root.kind {
                 hir::ExprKind::AddrOf(_, expr) => expr,
                 hir::ExprKind::MethodCall(method_name, _, call_args) => {
                     if call_args.len() == 1
@@ -1381,7 +1534,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_, '_>, arg: &'a hir::Expr) -> &'a hir::Ex
                         && {
                             let arg_type = cx.tables.expr_ty(&call_args[0]);
                             let base_type = walk_ptrs_ty(arg_type);
-                            base_type.sty == ty::Str || match_type(cx, base_type, &paths::STRING)
+                            base_type.kind == ty::Str || match_type(cx, base_type, &paths::STRING)
                         }
                     {
                         &call_args[0]
@@ -1402,8 +1555,8 @@ fn requires_to_string(cx: &LateContext<'_, '_>, arg: &hir::Expr) -> bool {
         if match_type(cx, arg_ty, &paths::STRING) {
             return false;
         }
-        if let ty::Ref(ty::ReStatic, ty, ..) = arg_ty.sty {
-            if ty.sty == ty::Str {
+        if let ty::Ref(ty::ReStatic, ty, ..) = arg_ty.kind {
+            if ty.kind == ty::Str {
                 return false;
             }
         };
@@ -1416,9 +1569,9 @@ fn generate_format_arg_snippet(
         applicability: &mut Applicability,
     ) -> Vec<String> {
         if_chain! {
-            if let hir::ExprKind::AddrOf(_, ref format_arg) = a.node;
-            if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.node;
-            if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.node;
+            if let hir::ExprKind::AddrOf(_, ref format_arg) = a.kind;
+            if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind;
+            if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind;
 
             then {
                 format_arg_expr_tup
@@ -1434,7 +1587,7 @@ fn generate_format_arg_snippet(
     fn is_call(node: &hir::ExprKind) -> bool {
         match node {
             hir::ExprKind::AddrOf(_, expr) => {
-                is_call(&expr.node)
+                is_call(&expr.kind)
             },
             hir::ExprKind::Call(..)
             | hir::ExprKind::MethodCall(..)
@@ -1445,7 +1598,7 @@ fn is_call(node: &hir::ExprKind) -> bool {
         }
     }
 
-    if args.len() != 2 || name != "expect" || !is_call(&args[1].node) {
+    if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) {
         return;
     }
 
@@ -1465,9 +1618,9 @@ fn is_call(node: &hir::ExprKind) -> bool {
     let mut applicability = Applicability::MachineApplicable;
 
     //Special handling for `format!` as arg_root
-    if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.node {
+    if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind {
         if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1 {
-            if let hir::ExprKind::Call(_, format_args) = &inner_args[0].node {
+            if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind {
                 let fmt_spec = &format_args[0];
                 let fmt_args = &format_args[1];
 
@@ -1511,8 +1664,8 @@ fn is_call(node: &hir::ExprKind) -> bool {
 /// Checks for the `CLONE_ON_COPY` lint.
 fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr, arg: &hir::Expr, arg_ty: Ty<'_>) {
     let ty = cx.tables.expr_ty(expr);
-    if let ty::Ref(_, inner, _) = arg_ty.sty {
-        if let ty::Ref(_, innermost, _) = inner.sty {
+    if let ty::Ref(_, inner, _) = arg_ty.kind {
+        if let ty::Ref(_, innermost, _) = inner.kind {
             span_lint_and_then(
                 cx,
                 CLONE_DOUBLE_REF,
@@ -1523,7 +1676,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr, arg: &hir::Exp
                     if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
                         let mut ty = innermost;
                         let mut n = 0;
-                        while let ty::Ref(_, inner, _) = ty.sty {
+                        while let ty::Ref(_, inner, _) = ty.kind {
                             ty = inner;
                             n += 1;
                         }
@@ -1554,7 +1707,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr, arg: &hir::Exp
         if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) {
             let parent = cx.tcx.hir().get_parent_node(expr.hir_id);
             match &cx.tcx.hir().get(parent) {
-                hir::Node::Expr(parent) => match parent.node {
+                hir::Node::Expr(parent) => match parent.kind {
                     // &*x is a nop, &x.clone() is not
                     hir::ExprKind::AddrOf(..) |
                     // (*x).func() is useless, x.clone().func() can work in case func borrows mutably
@@ -1562,8 +1715,8 @@ fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr, arg: &hir::Exp
                     _ => {},
                 },
                 hir::Node::Stmt(stmt) => {
-                    if let hir::StmtKind::Local(ref loc) = stmt.node {
-                        if let hir::PatKind::Ref(..) = loc.pat.node {
+                    if let hir::StmtKind::Local(ref loc) = stmt.kind {
+                        if let hir::PatKind::Ref(..) = loc.pat.kind {
                             // let ref y = *x borrows x, let ref y = x.clone() does not
                             return;
                         }
@@ -1605,7 +1758,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr, arg: &hir::Exp
 fn lint_clone_on_ref_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr, arg: &hir::Expr) {
     let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(arg));
 
-    if let ty::Adt(_, subst) = obj_ty.sty {
+    if let ty::Adt(_, subst) = obj_ty.kind {
         let caller_type = if match_type(cx, obj_ty, &paths::RC) {
             "Rc"
         } else if match_type(cx, obj_ty, &paths::ARC) {
@@ -1638,7 +1791,7 @@ fn lint_string_extend(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::E
     if let Some(arglists) = method_chain_args(arg, &["chars"]) {
         let target = &arglists[0][0];
         let self_ty = walk_ptrs_ty(cx.tables.expr_ty(target));
-        let ref_str = if self_ty.sty == ty::Str {
+        let ref_str = if self_ty.kind == ty::Str {
             ""
         } else if match_type(cx, self_ty, &paths::STRING) {
             "&"
@@ -1674,7 +1827,7 @@ fn lint_extend(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr]) {
 fn lint_cstring_as_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr, source: &hir::Expr, unwrap: &hir::Expr) {
     if_chain! {
         let source_type = cx.tables.expr_ty(source);
-        if let ty::Adt(def, substs) = source_type.sty;
+        if let ty::Adt(def, substs) = source_type.kind;
         if match_def_path(cx, def.did, &paths::RESULT);
         if match_type(cx, substs.type_at(0), &paths::CSTRING);
         then {
@@ -1693,7 +1846,7 @@ fn lint_cstring_as_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr, source: &hir:
 
 fn lint_iter_cloned_collect<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, iter_args: &'tcx [hir::Expr]) {
     if_chain! {
-        if match_type(cx, cx.tables.expr_ty(expr), &paths::VEC);
+        if is_type_diagnostic_item(cx, cx.tables.expr_ty(expr), Symbol::intern("vec_type"));
         if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0]));
         if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite());
 
@@ -1712,34 +1865,33 @@ fn lint_iter_cloned_collect<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Ex
     }
 }
 
-fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr, fold_args: &[hir::Expr]) {
+fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr, fold_args: &[hir::Expr], fold_span: Span) {
     fn check_fold_with_op(
         cx: &LateContext<'_, '_>,
         expr: &hir::Expr,
         fold_args: &[hir::Expr],
+        fold_span: Span,
         op: hir::BinOpKind,
         replacement_method_name: &str,
         replacement_has_args: bool,
     ) {
         if_chain! {
             // Extract the body of the closure passed to fold
-            if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].node;
+            if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind;
             let closure_body = cx.tcx.hir().body(body_id);
             let closure_expr = remove_blocks(&closure_body.value);
 
             // Check if the closure body is of the form `acc <op> some_expr(x)`
-            if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.node;
+            if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind;
             if bin_op.node == op;
 
             // Extract the names of the two arguments to the closure
-            if let Some(first_arg_ident) = get_arg_name(&closure_body.arguments[0].pat);
-            if let Some(second_arg_ident) = get_arg_name(&closure_body.arguments[1].pat);
+            if let Some(first_arg_ident) = get_arg_name(&closure_body.params[0].pat);
+            if let Some(second_arg_ident) = get_arg_name(&closure_body.params[1].pat);
 
             if match_var(&*left_expr, first_arg_ident);
             if replacement_has_args || match_var(&*right_expr, second_arg_ident);
 
-            if let hir::ExprKind::MethodCall(_, span, _) = &expr.node;
-
             then {
                 let mut applicability = Applicability::MachineApplicable;
                 let sugg = if replacement_has_args {
@@ -1759,7 +1911,7 @@ fn check_fold_with_op(
                 span_lint_and_sugg(
                     cx,
                     UNNECESSARY_FOLD,
-                    span.with_hi(expr.span.hi()),
+                    fold_span.with_hi(expr.span.hi()),
                     // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
                     "this `.fold` can be written more succinctly using another method",
                     "try",
@@ -1781,12 +1933,20 @@ fn check_fold_with_op(
     );
 
     // Check if the first argument to .fold is a suitable literal
-    if let hir::ExprKind::Lit(ref lit) = fold_args[1].node {
+    if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind {
         match lit.node {
-            ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, fold_args, hir::BinOpKind::Or, "any", true),
-            ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, fold_args, hir::BinOpKind::And, "all", true),
-            ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, fold_args, hir::BinOpKind::Add, "sum", false),
-            ast::LitKind::Int(1, _) => check_fold_with_op(cx, expr, fold_args, hir::BinOpKind::Mul, "product", false),
+            ast::LitKind::Bool(false) => {
+                check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true)
+            },
+            ast::LitKind::Bool(true) => {
+                check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true)
+            },
+            ast::LitKind::Int(0, _) => {
+                check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false)
+            },
+            ast::LitKind::Int(1, _) => {
+                check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false)
+            },
             _ => (),
         }
     }
@@ -1796,7 +1956,7 @@ fn lint_iter_nth<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, iter_ar
     let mut_str = if is_mut { "_mut" } else { "" };
     let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() {
         "slice"
-    } else if match_type(cx, cx.tables.expr_ty(&iter_args[0]), &paths::VEC) {
+    } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(&iter_args[0]), Symbol::intern("vec_type")) {
         "Vec"
     } else if match_type(cx, cx.tables.expr_ty(&iter_args[0]), &paths::VEC_DEQUE) {
         "VecDeque"
@@ -1816,7 +1976,7 @@ fn lint_iter_nth<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, iter_ar
 }
 
 fn lint_get_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, get_args: &'tcx [hir::Expr], is_mut: bool) {
-    // Note: we don't want to lint `get_mut().unwrap` for HashMap or BTreeMap,
+    // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`,
     // because they do not implement `IndexMut`
     let mut applicability = Applicability::MachineApplicable;
     let expr_ty = cx.tables.expr_ty(&get_args[0]);
@@ -1829,7 +1989,7 @@ fn lint_get_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, get_a
     let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() {
         needs_ref = get_args_str.parse::<usize>().is_ok();
         "slice"
-    } else if match_type(cx, expr_ty, &paths::VEC) {
+    } else if is_type_diagnostic_item(cx, expr_ty, Symbol::intern("vec_type")) {
         needs_ref = get_args_str.parse::<usize>().is_ok();
         "Vec"
     } else if match_type(cx, expr_ty, &paths::VEC_DEQUE) {
@@ -1853,7 +2013,7 @@ fn lint_get_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, get_a
     if_chain! {
         if needs_ref;
         if let Some(parent) = get_parent_expr(cx, expr);
-        if let hir::ExprKind::Unary(hir::UnOp::UnDeref, _) = parent.node;
+        if let hir::ExprKind::Unary(hir::UnOp::UnDeref, _) = parent.kind;
         then {
             needs_ref = false;
             span = parent.span;
@@ -1906,24 +2066,24 @@ fn derefs_to_slice<'a, 'tcx>(
     ty: Ty<'tcx>,
 ) -> Option<&'tcx hir::Expr> {
     fn may_slice<'a>(cx: &LateContext<'_, 'a>, ty: Ty<'a>) -> bool {
-        match ty.sty {
+        match ty.kind {
             ty::Slice(_) => true,
             ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
-            ty::Adt(..) => match_type(cx, ty, &paths::VEC),
+            ty::Adt(..) => is_type_diagnostic_item(cx, ty, Symbol::intern("vec_type")),
             ty::Array(_, size) => size.eval_usize(cx.tcx, cx.param_env) < 32,
             ty::Ref(_, inner, _) => may_slice(cx, inner),
             _ => false,
         }
     }
 
-    if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.node {
+    if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.kind {
         if path.ident.name == sym!(iter) && may_slice(cx, cx.tables.expr_ty(&args[0])) {
             Some(&args[0])
         } else {
             None
         }
     } else {
-        match ty.sty {
+        match ty.kind {
             ty::Slice(_) => Some(expr),
             ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
             ty::Ref(_, inner, _) => {
@@ -1965,6 +2125,31 @@ fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr, unwrap_args: &[hir::E
     }
 }
 
+/// lint use of `expect()` for `Option`s and `Result`s
+fn lint_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr, expect_args: &[hir::Expr]) {
+    let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&expect_args[0]));
+
+    let mess = if match_type(cx, obj_ty, &paths::OPTION) {
+        Some((OPTION_EXPECT_USED, "an Option", "None"))
+    } else if match_type(cx, obj_ty, &paths::RESULT) {
+        Some((RESULT_EXPECT_USED, "a Result", "Err"))
+    } else {
+        None
+    };
+
+    if let Some((lint, kind, none_value)) = mess {
+        span_lint(
+            cx,
+            lint,
+            expr.span,
+            &format!(
+                "used expect() on {} value. If this value is an {} it will panic",
+                kind, none_value
+            ),
+        );
+    }
+}
+
 /// lint use of `ok().expect()` for `Result`s
 fn lint_ok_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr, ok_args: &[hir::Expr]) {
     if_chain! {
@@ -2081,7 +2266,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>(
 fn lint_map_or_none<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr, map_or_args: &'tcx [hir::Expr]) {
     if match_type(cx, cx.tables.expr_ty(&map_or_args[0]), &paths::OPTION) {
         // check if the first non-self argument to map_or() is None
-        let map_or_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].node {
+        let map_or_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind {
             match_qpath(qpath, &paths::OPTION_NONE)
         } else {
             false
@@ -2111,48 +2296,24 @@ fn lint_option_and_then_some(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &
     const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`";
     const NO_OP_MSG: &str = "using `Option.and_then(Some)`, which is a no-op";
 
-    // Searches an return expressions in `y` in `_.and_then(|x| Some(y))`, which we don't lint
-    struct RetCallFinder {
-        found: bool,
-    }
-
-    impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder {
-        fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
-            if self.found {
-                return;
-            }
-            if let hir::ExprKind::Ret(..) = &expr.node {
-                self.found = true;
-            } else {
-                intravisit::walk_expr(self, expr);
-            }
-        }
-
-        fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
-            intravisit::NestedVisitorMap::None
-        }
-    }
-
     let ty = cx.tables.expr_ty(&args[0]);
     if !match_type(cx, ty, &paths::OPTION) {
         return;
     }
 
-    match args[1].node {
+    match args[1].kind {
         hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
             let closure_body = cx.tcx.hir().body(body_id);
             let closure_expr = remove_blocks(&closure_body.value);
             if_chain! {
-                if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.node;
-                if let hir::ExprKind::Path(ref qpath) = some_expr.node;
+                if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind;
+                if let hir::ExprKind::Path(ref qpath) = some_expr.kind;
                 if match_qpath(qpath, &paths::OPTION_SOME);
                 if some_args.len() == 1;
                 then {
                     let inner_expr = &some_args[0];
 
-                    let mut finder = RetCallFinder { found: false };
-                    finder.visit_expr(inner_expr);
-                    if finder.found {
+                    if contains_return(inner_expr) {
                         return;
                     }
 
@@ -2323,30 +2484,29 @@ fn lint_flat_map_identity<'a, 'tcx>(
     cx: &LateContext<'a, 'tcx>,
     expr: &'tcx hir::Expr,
     flat_map_args: &'tcx [hir::Expr],
+    flat_map_span: Span,
 ) {
     if match_trait_method(cx, expr, &paths::ITERATOR) {
-        let arg_node = &flat_map_args[1].node;
+        let arg_node = &flat_map_args[1].kind;
 
         let apply_lint = |message: &str| {
-            if let hir::ExprKind::MethodCall(_, span, _) = &expr.node {
-                span_lint_and_sugg(
-                    cx,
-                    FLAT_MAP_IDENTITY,
-                    span.with_hi(expr.span.hi()),
-                    message,
-                    "try",
-                    "flatten()".to_string(),
-                    Applicability::MachineApplicable,
-                );
-            }
+            span_lint_and_sugg(
+                cx,
+                FLAT_MAP_IDENTITY,
+                flat_map_span.with_hi(expr.span.hi()),
+                message,
+                "try",
+                "flatten()".to_string(),
+                Applicability::MachineApplicable,
+            );
         };
 
         if_chain! {
             if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
             let body = cx.tcx.hir().body(*body_id);
 
-            if let hir::PatKind::Binding(_, _, binding_ident, _) = body.arguments[0].pat.node;
-            if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.node;
+            if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
+            if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind;
 
             if path.segments.len() == 1;
             if path.segments[0].ident.as_str() == binding_ident.as_str();
@@ -2375,6 +2535,7 @@ fn lint_search_is_some<'a, 'tcx>(
     search_method: &str,
     search_args: &'tcx [hir::Expr],
     is_some_args: &'tcx [hir::Expr],
+    method_span: Span,
 ) {
     // lint if caller of search is an Iterator
     if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) {
@@ -2386,31 +2547,36 @@ fn lint_search_is_some<'a, 'tcx>(
         let search_snippet = snippet(cx, search_args[1].span, "..");
         if search_snippet.lines().count() <= 1 {
             // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
+            // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
             let any_search_snippet = if_chain! {
                 if search_method == "find";
-                if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].node;
+                if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind;
                 let closure_body = cx.tcx.hir().body(body_id);
-                if let Some(closure_arg) = closure_body.arguments.get(0);
-                if let hir::PatKind::Ref(..) = closure_arg.pat.node;
+                if let Some(closure_arg) = closure_body.params.get(0);
                 then {
-                    Some(search_snippet.replacen('&', "", 1))
+                    if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
+                        Some(search_snippet.replacen('&', "", 1))
+                    } else if let Some(name) = get_arg_name(&closure_arg.pat) {
+                        Some(search_snippet.replace(&format!("*{}", name), &name.as_str()))
+                    } else {
+                        None
+                    }
                 } else {
                     None
                 }
             };
             // add note if not multi-line
-            span_note_and_lint(
+            span_lint_and_sugg(
                 cx,
                 SEARCH_IS_SOME,
-                expr.span,
+                method_span.with_hi(expr.span.hi()),
                 &msg,
-                expr.span,
-                &format!(
-                    "replace `{0}({1}).is_some()` with `any({2})`",
-                    search_method,
-                    search_snippet,
+                "try this",
+                format!(
+                    "any({})",
                     any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
                 ),
+                Applicability::MachineApplicable,
             );
         } else {
             span_lint(cx, SEARCH_IS_SOME, expr.span, &msg);
@@ -2456,16 +2622,16 @@ fn lint_chars_cmp(
 ) -> bool {
     if_chain! {
         if let Some(args) = method_chain_args(info.chain, chain_methods);
-        if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.node;
+        if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
         if arg_char.len() == 1;
-        if let hir::ExprKind::Path(ref qpath) = fun.node;
+        if let hir::ExprKind::Path(ref qpath) = fun.kind;
         if let Some(segment) = single_segment_path(qpath);
         if segment.ident.name == sym!(Some);
         then {
             let mut applicability = Applicability::MachineApplicable;
             let self_ty = walk_ptrs_ty(cx.tables.expr_ty_adjusted(&args[0][0]));
 
-            if self_ty.sty != ty::Str {
+            if self_ty.kind != ty::Str {
                 return false;
             }
 
@@ -2514,7 +2680,7 @@ fn lint_chars_cmp_with_unwrap<'a, 'tcx>(
 ) -> bool {
     if_chain! {
         if let Some(args) = method_chain_args(info.chain, chain_methods);
-        if let hir::ExprKind::Lit(ref lit) = info.other.node;
+        if let hir::ExprKind::Lit(ref lit) = info.other.kind;
         if let ast::LitKind::Char(c) = lit.node;
         then {
             let mut applicability = Applicability::MachineApplicable;
@@ -2556,7 +2722,7 @@ fn lint_chars_last_cmp_with_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &
 /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
 fn lint_single_char_pattern<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, _expr: &'tcx hir::Expr, arg: &'tcx hir::Expr) {
     if_chain! {
-        if let hir::ExprKind::Lit(lit) = &arg.node;
+        if let hir::ExprKind::Lit(lit) = &arg.kind;
         if let ast::LitKind::Str(r, style) = lit.node;
         if r.as_str().len() == 1;
         then {
@@ -2599,7 +2765,7 @@ fn lint_asref(cx: &LateContext<'_, '_>, expr: &hir::Expr, call_name: &str, as_re
             // allow the `as_ref` or `as_mut` if it is followed by another method call
             if_chain! {
                 if let Some(parent) = get_parent_expr(cx, expr);
-                if let hir::ExprKind::MethodCall(_, ref span, _) = parent.node;
+                if let hir::ExprKind::MethodCall(_, ref span, _) = parent.kind;
                 if span != &expr.span;
                 then {
                     return;
@@ -2630,7 +2796,7 @@ fn ty_has_iter_method(
         } else {
             INTO_ITER_ON_REF
         };
-        let mutbl = match self_ref_ty.sty {
+        let mutbl = match self_ref_ty.kind {
             ty::Ref(_, _, mutbl) => mutbl,
             _ => unreachable!(),
         };
@@ -2662,6 +2828,37 @@ fn lint_into_iter(cx: &LateContext<'_, '_>, expr: &hir::Expr, self_ref_ty: Ty<'_
     }
 }
 
+/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
+fn lint_maybe_uninit(cx: &LateContext<'_, '_>, expr: &hir::Expr, outer: &hir::Expr) {
+    if_chain! {
+        if let hir::ExprKind::Call(ref callee, ref args) = expr.kind;
+        if args.is_empty();
+        if let hir::ExprKind::Path(ref path) = callee.kind;
+        if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
+        if !is_maybe_uninit_ty_valid(cx, cx.tables.expr_ty_adjusted(outer));
+        then {
+            span_lint(
+                cx,
+                UNINIT_ASSUMED_INIT,
+                outer.span,
+                "this call for this type may be undefined behavior"
+            );
+        }
+    }
+}
+
+fn is_maybe_uninit_ty_valid(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
+    match ty.kind {
+        ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component),
+        ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)),
+        ty::Adt(ref adt, _) => {
+            // needs to be a MaybeUninit
+            match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT)
+        },
+        _ => false,
+    }
+}
+
 fn lint_suspicious_map(cx: &LateContext<'_, '_>, expr: &hir::Expr) {
     span_help_and_lint(
         cx,
@@ -2674,7 +2871,7 @@ fn lint_suspicious_map(cx: &LateContext<'_, '_>, expr: &hir::Expr) {
 
 /// Given a `Result<T, E>` type, return its error type (`E`).
 fn get_error_type<'a>(cx: &LateContext<'_, '_>, ty: Ty<'a>) -> Option<Ty<'a>> {
-    match ty.sty {
+    match ty.kind {
         ty::Adt(_, substs) if match_type(cx, ty, &paths::RESULT) => substs.types().nth(1),
         _ => None,
     }
@@ -2682,10 +2879,9 @@ fn get_error_type<'a>(cx: &LateContext<'_, '_>, ty: Ty<'a>) -> Option<Ty<'a>> {
 
 /// This checks whether a given type is known to implement Debug.
 fn has_debug_impl<'a, 'b>(ty: Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
-    match cx.tcx.lang_items().debug_trait() {
-        Some(debug) => implements_trait(cx, ty, debug, &[]),
-        None => false,
-    }
+    cx.tcx
+        .get_diagnostic_item(sym::debug_trait)
+        .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
 }
 
 enum Convention {
@@ -2775,7 +2971,7 @@ fn matches_value(parent_ty: Ty<'_>, ty: Ty<'_>) -> bool {
             } else if ty.is_box() {
                 ty.boxed_ty() == parent_ty
             } else if ty.is_rc() || ty.is_arc() {
-                if let ty::Adt(_, substs) = ty.sty {
+                if let ty::Adt(_, substs) = ty.kind {
                     substs.types().next().map_or(false, |t| t == parent_ty)
                 } else {
                     false
@@ -2791,7 +2987,7 @@ fn matches_ref<'a>(
             parent_ty: Ty<'a>,
             ty: Ty<'a>,
         ) -> bool {
-            if let ty::Ref(_, t, m) = ty.sty {
+            if let ty::Ref(_, t, m) = ty.kind {
                 return m == mutability && t == parent_ty;
             }
 
@@ -2817,6 +3013,7 @@ fn matches_ref<'a>(
         }
     }
 
+    #[must_use]
     fn description(self) -> &'static str {
         match self {
             Self::Value => "self by value",
@@ -2828,6 +3025,7 @@ fn description(self) -> &'static str {
 }
 
 impl Convention {
+    #[must_use]
     fn check(&self, other: &str) -> bool {
         match *self {
             Self::Eq(this) => this == other,
@@ -2855,22 +3053,50 @@ enum OutType {
 
 impl OutType {
     fn matches(self, cx: &LateContext<'_, '_>, ty: &hir::FunctionRetTy) -> bool {
-        let is_unit = |ty: &hir::Ty| SpanlessEq::new(cx).eq_ty_kind(&ty.node, &hir::TyKind::Tup(vec![].into()));
+        let is_unit = |ty: &hir::Ty| SpanlessEq::new(cx).eq_ty_kind(&ty.kind, &hir::TyKind::Tup(vec![].into()));
         match (self, ty) {
             (Self::Unit, &hir::DefaultReturn(_)) => true,
             (Self::Unit, &hir::Return(ref ty)) if is_unit(ty) => true,
             (Self::Bool, &hir::Return(ref ty)) if is_bool(ty) => true,
             (Self::Any, &hir::Return(ref ty)) if !is_unit(ty) => true,
-            (Self::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyKind::Rptr(_, _)),
+            (Self::Ref, &hir::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
             _ => false,
         }
     }
 }
 
 fn is_bool(ty: &hir::Ty) -> bool {
-    if let hir::TyKind::Path(ref p) = ty.node {
+    if let hir::TyKind::Path(ref p) = ty.kind {
         match_qpath(p, &["bool"])
     } else {
         false
     }
 }
+
+// Returns `true` if `expr` contains a return expression
+fn contains_return(expr: &hir::Expr) -> bool {
+    struct RetCallFinder {
+        found: bool,
+    }
+
+    impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder {
+        fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
+            if self.found {
+                return;
+            }
+            if let hir::ExprKind::Ret(..) = &expr.kind {
+                self.found = true;
+            } else {
+                intravisit::walk_expr(self, expr);
+            }
+        }
+
+        fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+            intravisit::NestedVisitorMap::None
+        }
+    }
+
+    let mut visitor = RetCallFinder { found: false };
+    visitor.visit_expr(expr);
+    visitor.found
+}