]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #7315 - DevinR528:disallowed-ty, r=giraffate
authorbors <bors@rust-lang.org>
Thu, 10 Jun 2021 00:30:36 +0000 (00:30 +0000)
committerbors <bors@rust-lang.org>
Thu, 10 Jun 2021 00:30:36 +0000 (00:30 +0000)
Add disallowed_type lint, this adds a field to the conf struct

Fixes #7277

changelog: Add ``[`disallowed_type`]`` a lint that can enforce banning types specified in the config.

26 files changed:
clippy_lints/src/default_numeric_fallback.rs
clippy_lints/src/lib.rs
clippy_lints/src/loops/while_let_on_iterator.rs
clippy_lints/src/map_identity.rs [deleted file]
clippy_lints/src/methods/filter_map_identity.rs
clippy_lints/src/methods/flat_map_identity.rs
clippy_lints/src/methods/map_identity.rs [new file with mode: 0644]
clippy_lints/src/methods/mod.rs
clippy_lints/src/semicolon_if_nothing_returned.rs
clippy_lints/src/utils/conf.rs
clippy_utils/src/lib.rs
tests/ui/auxiliary/macro_rules.rs
tests/ui/default_numeric_fallback.rs
tests/ui/default_numeric_fallback.stderr
tests/ui/doc.rs
tests/ui/filter_map_identity.fixed
tests/ui/filter_map_identity.rs
tests/ui/filter_map_identity.stderr
tests/ui/flat_map_identity.fixed
tests/ui/flat_map_identity.rs
tests/ui/flat_map_identity.stderr
tests/ui/semicolon_if_nothing_returned.rs
tests/ui/semicolon_if_nothing_returned.stderr
tests/ui/while_let_on_iterator.fixed
tests/ui/while_let_on_iterator.rs
tests/ui/while_let_on_iterator.stderr

index 759f7d4062d448ba64e3218b6435c65841fb864a..a125376bffa9fa405089ba1cc483e3543496aee9 100644 (file)
@@ -7,9 +7,10 @@
     intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
     Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
 };
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::{
     hir::map::Map,
+    lint::in_external_macro,
     ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
 };
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -73,6 +74,7 @@ fn new(cx: &'a LateContext<'tcx>) -> Self {
     /// Check whether a passed literal has potential to cause fallback or not.
     fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
         if_chain! {
+                if !in_external_macro(self.cx.sess(), lit.span);
                 if let Some(ty_bound) = self.ty_bounds.last();
                 if matches!(lit.node,
                             LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
index 41c635d9e9386c954487fb292c1aa67e56a2a916..591bf68f927af65657817a276a0e427ca18c8d71 100644 (file)
@@ -255,7 +255,6 @@ macro_rules! declare_clippy_lint {
 mod manual_unwrap_or;
 mod map_clone;
 mod map_err_ignore;
-mod map_identity;
 mod map_unit_fn;
 mod match_on_vec_items;
 mod matches;
@@ -707,7 +706,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         manual_unwrap_or::MANUAL_UNWRAP_OR,
         map_clone::MAP_CLONE,
         map_err_ignore::MAP_ERR_IGNORE,
-        map_identity::MAP_IDENTITY,
         map_unit_fn::OPTION_MAP_UNIT_FN,
         map_unit_fn::RESULT_MAP_UNIT_FN,
         match_on_vec_items::MATCH_ON_VEC_ITEMS,
@@ -767,6 +765,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         methods::MANUAL_STR_REPEAT,
         methods::MAP_COLLECT_RESULT_UNIT,
         methods::MAP_FLATTEN,
+        methods::MAP_IDENTITY,
         methods::MAP_UNWRAP_OR,
         methods::NEW_RET_NO_SELF,
         methods::OK_EXPECT,
@@ -1262,7 +1261,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(manual_strip::MANUAL_STRIP),
         LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
         LintId::of(map_clone::MAP_CLONE),
-        LintId::of(map_identity::MAP_IDENTITY),
         LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
         LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
         LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
@@ -1303,6 +1301,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
         LintId::of(methods::MANUAL_STR_REPEAT),
         LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+        LintId::of(methods::MAP_IDENTITY),
         LintId::of(methods::NEW_RET_NO_SELF),
         LintId::of(methods::OK_EXPECT),
         LintId::of(methods::OPTION_AS_REF_DEREF),
@@ -1588,7 +1587,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(loops::WHILE_LET_LOOP),
         LintId::of(manual_strip::MANUAL_STRIP),
         LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
-        LintId::of(map_identity::MAP_IDENTITY),
         LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
         LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
         LintId::of(matches::MATCH_AS_REF),
@@ -1603,6 +1601,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(methods::ITER_COUNT),
         LintId::of(methods::MANUAL_FILTER_MAP),
         LintId::of(methods::MANUAL_FIND_MAP),
+        LintId::of(methods::MAP_IDENTITY),
         LintId::of(methods::OPTION_AS_REF_DEREF),
         LintId::of(methods::OPTION_FILTER_MAP),
         LintId::of(methods::SEARCH_IS_SOME),
@@ -2042,7 +2041,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         single_char_binding_names_threshold,
     });
     store.register_late_pass(|| box macro_use::MacroUseImports::default());
-    store.register_late_pass(|| box map_identity::MapIdentity);
     store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
     store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
     store.register_late_pass(|| box repeat_once::RepeatOnce);
index 63560047578a16aa0c39f8996745dbd02ab6c246..d57588716a5bf16c8f4b28d0b75ceb251718837e 100644 (file)
@@ -1,7 +1,9 @@
 use super::WHILE_LET_ON_ITERATOR;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
+use clippy_utils::{
+    get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
+};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
@@ -315,9 +317,10 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
         }
     }
 
-    if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
-        // The iterator expression will be used on the next iteration unless it is declared within the outer
-        // loop.
+    if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
+        // The iterator expression will be used on the next iteration (for loops), or on the next call (for
+        // closures) unless it is declared within the enclosing expression. TODO: Check for closures
+        // used where an `FnOnce` type is expected.
         let local_id = match iter_expr.path {
             Res::Local(id) => id,
             _ => return true,
diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs
deleted file mode 100644 (file)
index 41cda23..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
-    ///
-    /// **Why is this bad?** It can be written more concisely without the call to `map`.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    ///
-    /// ```rust
-    /// let x = [1, 2, 3];
-    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let x = [1, 2, 3];
-    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
-    /// ```
-    pub MAP_IDENTITY,
-    complexity,
-    "using iterator.map(|x| x)"
-}
-
-declare_lint_pass!(MapIdentity => [MAP_IDENTITY]);
-
-impl<'tcx> LateLintPass<'tcx> for MapIdentity {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-
-        if_chain! {
-            if let Some([caller, func]) = get_map_argument(cx, expr);
-            if is_expr_identity_function(cx, func);
-            then {
-                span_lint_and_sugg(
-                    cx,
-                    MAP_IDENTITY,
-                    expr.span.trim_start(caller.span).unwrap(),
-                    "unnecessary map of the identity function",
-                    "remove the call to `map`",
-                    String::new(),
-                    Applicability::MachineApplicable
-                )
-            }
-        }
-    }
-}
-
-/// Returns the arguments passed into map() if the expression is a method call to
-/// map(). Otherwise, returns None.
-fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
-    if_chain! {
-        if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
-        if args.len() == 2 && method.ident.name == sym::map;
-        let caller_ty = cx.typeck_results().expr_ty(&args[0]);
-        if is_trait_method(cx, expr, sym::Iterator)
-            || is_type_diagnostic_item(cx, caller_ty, sym::result_type)
-            || is_type_diagnostic_item(cx, caller_ty, sym::option_type);
-        then {
-            Some(args)
-        } else {
-            None
-        }
-    }
-}
-
-/// Checks if an expression represents the identity function
-/// Only examines closures and `std::convert::identity`
-fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
-        ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
-        _ => false,
-    }
-}
-
-/// Checks if a function's body represents the identity function
-/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
-/// return x; }`
-fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
-    let params = func.params;
-    let body = remove_blocks(&func.value);
-
-    // if there's less/more than one parameter, then it is not the identity function
-    if params.len() != 1 {
-        return false;
-    }
-
-    match body.kind {
-        ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
-        ExprKind::Ret(Some(ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
-        ExprKind::Block(block, _) => {
-            if_chain! {
-                if block.stmts.len() == 1;
-                if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind;
-                if let ExprKind::Ret(Some(ret_val)) = expr.kind;
-                then {
-                    match_expr_param(cx, ret_val, params[0].pat)
-                } else {
-                    false
-                }
-            }
-        },
-        _ => false,
-    }
-}
-
-/// Returns true iff an expression returns the same thing as a parameter's pattern
-fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
-    if let PatKind::Binding(_, _, ident, _) = pat.kind {
-        match_var(expr, ident.name) && !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
-    } else {
-        false
-    }
-}
index 403fe8d35468468db91a9c46a13dd2755b5c9fd7..d1b5e945dfdaa83a85b5ce13d776b0d438b88579 100644 (file)
@@ -1,6 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
-use if_chain::if_chain;
+use clippy_utils::{is_expr_identity_function, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -9,32 +8,15 @@
 use super::FILTER_MAP_IDENTITY;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let apply_lint = |message: &str| {
-            span_lint_and_sugg(
-                cx,
-                FILTER_MAP_IDENTITY,
-                filter_map_span.with_hi(expr.span.hi()),
-                message,
-                "try",
-                "flatten()".to_string(),
-                Applicability::MachineApplicable,
-            );
-        };
-
-        if_chain! {
-            if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind;
-            let body = cx.tcx.hir().body(body_id);
-
-            if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind;
-            if path_to_local_id(&body.value, binding_id);
-            then {
-                apply_lint("called `filter_map(|x| x)` on an `Iterator`");
-            }
-        }
-
-        if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
-            apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
-        }
+    if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
+        span_lint_and_sugg(
+            cx,
+            FILTER_MAP_IDENTITY,
+            filter_map_span.with_hi(expr.span.hi()),
+            "use of `filter_map` with an identity function",
+            "try",
+            "flatten()".to_string(),
+            Applicability::MachineApplicable,
+        );
     }
 }
index 25f8434cb94428f458f36fd442fcf8260e542828..6f911d79d0bc50880628e5de6f2711802601d58d 100644 (file)
@@ -1,6 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
-use if_chain::if_chain;
+use clippy_utils::{is_expr_identity_function, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -15,36 +14,15 @@ pub(super) fn check<'tcx>(
     flat_map_arg: &'tcx hir::Expr<'_>,
     flat_map_span: Span,
 ) {
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let apply_lint = |message: &str| {
-            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, _, _) = flat_map_arg.kind;
-            let body = cx.tcx.hir().body(body_id);
-
-            if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
-            if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
-
-            if path.segments.len() == 1;
-            if path.segments[0].ident.name == binding_ident.name;
-
-            then {
-                apply_lint("called `flat_map(|x| x)` on an `Iterator`");
-            }
-        }
-
-        if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
-            apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
-        }
+    if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
+        span_lint_and_sugg(
+            cx,
+            FLAT_MAP_IDENTITY,
+            flat_map_span.with_hi(expr.span.hi()),
+            "use of `flat_map` with an identity function",
+            "try",
+            "flatten()".to_string(),
+            Applicability::MachineApplicable,
+        );
     }
 }
diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs
new file mode 100644 (file)
index 0000000..538a125
--- /dev/null
@@ -0,0 +1,38 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_expr_identity_function, is_trait_method};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::{source_map::Span, sym};
+
+use super::MAP_IDENTITY;
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    caller: &hir::Expr<'_>,
+    map_arg: &hir::Expr<'_>,
+    _map_span: Span,
+) {
+    let caller_ty = cx.typeck_results().expr_ty(caller);
+
+    if_chain! {
+        if is_trait_method(cx, expr, sym::Iterator)
+            || is_type_diagnostic_item(cx, caller_ty, sym::result_type)
+            || is_type_diagnostic_item(cx, caller_ty, sym::option_type);
+        if is_expr_identity_function(cx, map_arg);
+        if let Some(sugg_span) = expr.span.trim_start(caller.span);
+        then {
+            span_lint_and_sugg(
+                cx,
+                MAP_IDENTITY,
+                sugg_span,
+                "unnecessary map of the identity function",
+                "remove the call to `map`",
+                String::new(),
+                Applicability::MachineApplicable,
+            )
+        }
+    }
+}
index c8ae972f18ca62a5d4ba0f3e19ca803fa4169f7a..d4f8cef4f375d6b8f206e70b0767a5c1c3d4111f 100644 (file)
@@ -35,6 +35,7 @@
 mod manual_str_repeat;
 mod map_collect_result_unit;
 mod map_flatten;
+mod map_identity;
 mod map_unwrap_or;
 mod ok_expect;
 mod option_as_ref_deref;
     "call to `filter_map` where `flatten` is sufficient"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
+    ///
+    /// **Why is this bad?** It can be written more concisely without the call to `map`.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let x = [1, 2, 3];
+    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x = [1, 2, 3];
+    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
+    /// ```
+    pub MAP_IDENTITY,
+    complexity,
+    "using iterator.map(|x| x)"
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for the use of `.bytes().nth()`.
     ///
@@ -1728,6 +1752,7 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
     FILTER_NEXT,
     SKIP_WHILE_NEXT,
     FILTER_MAP_IDENTITY,
+    MAP_IDENTITY,
     MANUAL_FILTER_MAP,
     MANUAL_FIND_MAP,
     OPTION_FILTER_MAP,
@@ -2058,6 +2083,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                         _ => {},
                     }
                 }
+                map_identity::check(cx, expr, recv, m_arg, span);
             },
             ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
             ("next", []) => {
index 16e4d73851fb4e89b67cb68ff184880254d4cee1..da3e30af35ca4ffd7e2a8de07ef16ce6e028e04c 100644 (file)
@@ -1,3 +1,4 @@
+use crate::rustc_lint::LintContext;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_macro_callsite;
 use clippy_utils::{in_macro, sugg};
@@ -45,6 +46,7 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
             if t_expr.is_unit();
             if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
             if !snippet.ends_with('}');
+            if cx.sess().source_map().is_multiline(block.span);
             then {
                 // filter out the desugared `for` loop
                 if let ExprKind::DropTemps(..) = &expr.kind {
index e7d901aa9f9d924601d7a3ebef748262f3060ffb..ad2cb27650ece69e674ba1d48049eb84360977af 100644 (file)
@@ -149,7 +149,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
         "WebGL",
         "TensorFlow",
         "TrueType",
-        "iOS", "macOS",
+        "iOS", "macOS", "FreeBSD",
         "TeX", "LaTeX", "BibTeX", "BibLaTeX",
         "MinGW",
         "CamelCase",
index 769836aaf18ed6c54c3773c5c4e052e8528f96c5..d3efd32d727e5477aa01839a2f079913add98c5b 100644 (file)
@@ -861,14 +861,16 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
     })
 }
 
-/// Gets the loop enclosing the given expression, if any.
-pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+/// Gets the loop or closure enclosing the given expression, if any.
+pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     let map = tcx.hir();
     for (_, node) in map.parent_iter(expr.hir_id) {
         match node {
             Node::Expr(
-                e @ Expr {
-                    kind: ExprKind::Loop(..),
+                e
+                @
+                Expr {
+                    kind: ExprKind::Loop(..) | ExprKind::Closure(..),
                     ..
                 },
             ) => return Some(e),
@@ -1399,6 +1401,60 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
 }
 
+/// Checks if an expression represents the identity function
+/// Only examines closures and `std::convert::identity`
+pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    /// Returns true if the expression is a binding to the given pattern
+    fn is_expr_pat_binding(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
+        if let PatKind::Binding(_, _, ident, _) = pat.kind {
+            if match_var(expr, ident.name) {
+                return !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr));
+            }
+        }
+
+        false
+    }
+
+    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
+    /// * `|x| x`
+    /// * `|x| return x`
+    /// * `|x| { return x }`
+    /// * `|x| { return x; }`
+    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
+        let body = remove_blocks(&func.value);
+
+        let value_pat = if let [value_param] = func.params {
+            value_param.pat
+        } else {
+            return false;
+        };
+
+        match body.kind {
+            ExprKind::Path(QPath::Resolved(None, _)) => is_expr_pat_binding(cx, body, value_pat),
+            ExprKind::Ret(Some(ret_val)) => is_expr_pat_binding(cx, ret_val, value_pat),
+            ExprKind::Block(block, _) => {
+                if_chain! {
+                    if let &[block_stmt] = &block.stmts;
+                    if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block_stmt.kind;
+                    if let ExprKind::Ret(Some(ret_val)) = expr.kind;
+                    then {
+                        is_expr_pat_binding(cx, ret_val, value_pat)
+                    } else {
+                        false
+                    }
+                }
+            },
+            _ => false,
+        }
+    }
+
+    match expr.kind {
+        ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
+        ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
+        _ => false,
+    }
+}
+
 /// Gets the node where an expression is either used, or it's type is unified with another branch.
 pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
     let map = tcx.hir();
index d4470d3f40708517027b62f158c11c692b70ecaf..170955e726cc573853764fbaaf16b187450bada7 100644 (file)
@@ -106,3 +106,10 @@ fn lint() {
         }
     };
 }
+
+#[macro_export]
+macro_rules! default_numeric_fallback {
+    () => {
+        let x = 22;
+    };
+}
index 43468872db0b45f69c61799afc1c6b75521e7100..c0625fd1b75e8af15b1e2af3f9d43f749963974b 100644 (file)
@@ -1,3 +1,5 @@
+// aux-build:macro_rules.rs
+
 #![warn(clippy::default_numeric_fallback)]
 #![allow(unused)]
 #![allow(clippy::never_loop)]
@@ -5,6 +7,9 @@
 #![allow(clippy::unnecessary_operation)]
 #![allow(clippy::branches_sharing_code)]
 
+#[macro_use]
+extern crate macro_rules;
+
 mod basic_expr {
     fn test() {
         // Should lint unsuffixed literals typed `i32`.
@@ -133,4 +138,22 @@ fn test() {
     }
 }
 
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
 fn main() {}
index d1c4c8203dd832a07875d668dd9d7178928f64ca..5862cd936ac104ac03822912e75bed4dd4130c61 100644 (file)
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:11:17
+  --> $DIR/default_numeric_fallback.rs:16:17
    |
 LL |         let x = 22;
    |                 ^^ help: consider adding suffix: `22_i32`
@@ -7,142 +7,153 @@ LL |         let x = 22;
    = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:12:18
+  --> $DIR/default_numeric_fallback.rs:17:18
    |
 LL |         let x = [1, 2, 3];
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:12:21
+  --> $DIR/default_numeric_fallback.rs:17:21
    |
 LL |         let x = [1, 2, 3];
    |                     ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:12:24
+  --> $DIR/default_numeric_fallback.rs:17:24
    |
 LL |         let x = [1, 2, 3];
    |                        ^ help: consider adding suffix: `3_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:13:28
+  --> $DIR/default_numeric_fallback.rs:18:28
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:13:31
+  --> $DIR/default_numeric_fallback.rs:18:31
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                               ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:13:44
+  --> $DIR/default_numeric_fallback.rs:18:44
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                                            ^ help: consider adding suffix: `3_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:13:47
+  --> $DIR/default_numeric_fallback.rs:18:47
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                                               ^ help: consider adding suffix: `4_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:14:23
+  --> $DIR/default_numeric_fallback.rs:19:23
    |
 LL |         let x = match 1 {
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:15:13
+  --> $DIR/default_numeric_fallback.rs:20:13
    |
 LL |             1 => 1,
    |             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:15:18
+  --> $DIR/default_numeric_fallback.rs:20:18
    |
 LL |             1 => 1,
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:16:18
+  --> $DIR/default_numeric_fallback.rs:21:18
    |
 LL |             _ => 2,
    |                  ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:20:17
+  --> $DIR/default_numeric_fallback.rs:25:17
    |
 LL |         let x = 0.12;
    |                 ^^^^ help: consider adding suffix: `0.12_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:38:21
+  --> $DIR/default_numeric_fallback.rs:43:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:46:21
+  --> $DIR/default_numeric_fallback.rs:51:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:52:21
+  --> $DIR/default_numeric_fallback.rs:57:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:64:9
+  --> $DIR/default_numeric_fallback.rs:69:9
    |
 LL |         1
    |         ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:70:27
+  --> $DIR/default_numeric_fallback.rs:75:27
    |
 LL |         let f = || -> _ { 1 };
    |                           ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:74:29
+  --> $DIR/default_numeric_fallback.rs:79:29
    |
 LL |         let f = || -> i32 { 1 };
    |                             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:88:21
+  --> $DIR/default_numeric_fallback.rs:93:21
    |
 LL |         generic_arg(1);
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:91:32
+  --> $DIR/default_numeric_fallback.rs:96:32
    |
 LL |         let x: _ = generic_arg(1);
    |                                ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:109:28
+  --> $DIR/default_numeric_fallback.rs:114:28
    |
 LL |         GenericStruct { x: 1 };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:112:36
+  --> $DIR/default_numeric_fallback.rs:117:36
    |
 LL |         let _ = GenericStruct { x: 1 };
    |                                    ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:132:23
+  --> $DIR/default_numeric_fallback.rs:137:23
    |
 LL |         s.generic_arg(1);
    |                       ^ help: consider adding suffix: `1_i32`
 
-error: aborting due to 24 previous errors
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:144:21
+   |
+LL |             let x = 22;
+   |                     ^^ help: consider adding suffix: `22_i32`
+...
+LL |         internal_macro!();
+   |         ------------------ in this macro invocation
+   |
+   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 25 previous errors
 
index c946a047f1b0a88c56ef98d57428b1d8cd98b20e..8afef6b23d47653c27a9f2d2398ed411fd08667a 100644 (file)
@@ -64,7 +64,7 @@ fn test_units() {
 /// WebGL
 /// TensorFlow
 /// TrueType
-/// iOS macOS
+/// iOS macOS FreeBSD
 /// TeX LaTeX BibTeX BibLaTeX
 /// MinGW
 /// CamelCase (see also #2395)
index 23ce28d8e9be4c2238afe40f3a664419763305d0..a5860aa49b3bb7f909aeee04e130285188fd0ae8 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused_imports)]
+#![allow(unused_imports, clippy::needless_return)]
 #![warn(clippy::filter_map_identity)]
 
 fn main() {
@@ -13,4 +13,7 @@ fn main() {
     use std::convert::identity;
     let iterator = vec![Some(1), None, Some(2)].into_iter();
     let _ = iterator.flatten();
+
+    let iterator = vec![Some(1), None, Some(2)].into_iter();
+    let _ = iterator.flatten();
 }
index e698df13eea47ef4ff3eaca9319a0a0b82b14ad5..7e998b9cdf7010f4cd6e1fa84af582a3450dd6eb 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused_imports)]
+#![allow(unused_imports, clippy::needless_return)]
 #![warn(clippy::filter_map_identity)]
 
 fn main() {
@@ -13,4 +13,7 @@ fn main() {
     use std::convert::identity;
     let iterator = vec![Some(1), None, Some(2)].into_iter();
     let _ = iterator.filter_map(identity);
+
+    let iterator = vec![Some(1), None, Some(2)].into_iter();
+    let _ = iterator.filter_map(|x| return x);
 }
index 596a6320608c7c0acf8725c40c88c82461b20309..43c9fdca4fbe0251b54e6e6c70f4ab2fe3873eb8 100644 (file)
@@ -1,4 +1,4 @@
-error: called `filter_map(|x| x)` on an `Iterator`
+error: use of `filter_map` with an identity function
   --> $DIR/filter_map_identity.rs:8:22
    |
 LL |     let _ = iterator.filter_map(|x| x);
@@ -6,17 +6,23 @@ LL |     let _ = iterator.filter_map(|x| x);
    |
    = note: `-D clippy::filter-map-identity` implied by `-D warnings`
 
-error: called `filter_map(std::convert::identity)` on an `Iterator`
+error: use of `filter_map` with an identity function
   --> $DIR/filter_map_identity.rs:11:22
    |
 LL |     let _ = iterator.filter_map(std::convert::identity);
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
 
-error: called `filter_map(std::convert::identity)` on an `Iterator`
+error: use of `filter_map` with an identity function
   --> $DIR/filter_map_identity.rs:15:22
    |
 LL |     let _ = iterator.filter_map(identity);
    |                      ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
 
-error: aborting due to 3 previous errors
+error: use of `filter_map` with an identity function
+  --> $DIR/filter_map_identity.rs:18:22
+   |
+LL |     let _ = iterator.filter_map(|x| return x);
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+
+error: aborting due to 4 previous errors
 
index dfe3bd47e1394b4517cdbc88ee1b288a86a0c036..1f4b880ef5bcd8fa1cea502a8b9bd5efae1d7ba9 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused_imports)]
+#![allow(unused_imports, clippy::needless_return)]
 #![warn(clippy::flat_map_identity)]
 
 use std::convert;
@@ -11,4 +11,7 @@ fn main() {
 
     let iterator = [[0, 1], [2, 3], [4, 5]].iter();
     let _ = iterator.flatten();
+
+    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
+    let _ = iterator.flatten();
 }
index 393b95692554ca200ab9e5ac37f998e350f37d99..de14a06d4e6b3377a40d277105d073005e68229a 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused_imports)]
+#![allow(unused_imports, clippy::needless_return)]
 #![warn(clippy::flat_map_identity)]
 
 use std::convert;
@@ -11,4 +11,7 @@ fn main() {
 
     let iterator = [[0, 1], [2, 3], [4, 5]].iter();
     let _ = iterator.flat_map(convert::identity);
+
+    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
+    let _ = iterator.flat_map(|x| return x);
 }
index e4686ae5a5493ca2d409d56eebb347d84e0530d8..e776c9fdf512e2bda03316138086e2eabd4e2dcf 100644 (file)
@@ -1,4 +1,4 @@
-error: called `flat_map(|x| x)` on an `Iterator`
+error: use of `flat_map` with an identity function
   --> $DIR/flat_map_identity.rs:10:22
    |
 LL |     let _ = iterator.flat_map(|x| x);
@@ -6,11 +6,17 @@ LL |     let _ = iterator.flat_map(|x| x);
    |
    = note: `-D clippy::flat-map-identity` implied by `-D warnings`
 
-error: called `flat_map(std::convert::identity)` on an `Iterator`
+error: use of `flat_map` with an identity function
   --> $DIR/flat_map_identity.rs:13:22
    |
 LL |     let _ = iterator.flat_map(convert::identity);
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
 
-error: aborting due to 2 previous errors
+error: use of `flat_map` with an identity function
+  --> $DIR/flat_map_identity.rs:16:22
+   |
+LL |     let _ = iterator.flat_map(|x| return x);
+   |                      ^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+
+error: aborting due to 3 previous errors
 
index 0abe2cca26757325fc4f7d16c45d9d07ea5912f2..79ba7402f1f49285a79b0de83952e5f46c272333 100644 (file)
@@ -17,6 +17,24 @@ fn basic101(x: i32) {
     y = x + 1
 }
 
+#[rustfmt::skip]
+fn closure_error() {
+    let _d = || {
+        hello()
+    };
+}
+
+#[rustfmt::skip]
+fn unsafe_checks_error() {
+    use std::mem::MaybeUninit;
+    use std::ptr;
+
+    let mut s = MaybeUninit::<String>::uninit();
+    let _d = || unsafe { 
+        ptr::drop_in_place(s.as_mut_ptr()) 
+    };
+}
+
 // this is fine
 fn print_sum(a: i32, b: i32) {
     println!("{}", a + b);
@@ -53,3 +71,29 @@ fn loop_test(x: i32) {
         println!("{}", ext);
     }
 }
+
+fn closure() {
+    let _d = || hello();
+}
+
+#[rustfmt::skip]
+fn closure_block() {
+    let _d = || { hello() };
+}
+
+unsafe fn some_unsafe_op() {}
+unsafe fn some_other_unsafe_fn() {}
+
+fn do_something() {
+    unsafe { some_unsafe_op() };
+
+    unsafe { some_other_unsafe_fn() };
+}
+
+fn unsafe_checks() {
+    use std::mem::MaybeUninit;
+    use std::ptr;
+
+    let mut s = MaybeUninit::<String>::uninit();
+    let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) };
+}
index b73f89675383f12d53aeb4298898502c443af5b0..e88ebe2ad35f0bcacf64ed5f8ee3bc57e1227ae7 100644 (file)
@@ -18,5 +18,17 @@ error: consider adding a `;` to the last statement for consistent formatting
 LL |     y = x + 1
    |     ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
 
-error: aborting due to 3 previous errors
+error: consider adding a `;` to the last statement for consistent formatting
+  --> $DIR/semicolon_if_nothing_returned.rs:23:9
+   |
+LL |         hello()
+   |         ^^^^^^^ help: add a `;` here: `hello();`
+
+error: consider adding a `;` to the last statement for consistent formatting
+  --> $DIR/semicolon_if_nothing_returned.rs:34:9
+   |
+LL |         ptr::drop_in_place(s.as_mut_ptr()) 
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
+
+error: aborting due to 5 previous errors
 
index c3e2cf0c4a4bd485e8a869e3a1b7f140eacdc9e3..52e80ceee83cf31439c07cfff967fc46a03e09fd 100644 (file)
@@ -320,6 +320,20 @@ fn issue1924() {
     println!("iterator field {}", it.1);
 }
 
+fn issue7249() {
+    let mut it = 0..10;
+    let mut x = || {
+        // Needs &mut, the closure can be called multiple times
+        for x in &mut it {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    };
+    x();
+    x();
+}
+
 fn main() {
     let mut it = 0..20;
     for _ in it {
index 1717006a4490e59884b5e6db16858021ada5e383..5078a3c9028c4762f9d7ea09e3b1baf5c837c7c5 100644 (file)
@@ -320,6 +320,20 @@ fn next(&mut self) -> Option<u32> {
     println!("iterator field {}", it.1);
 }
 
+fn issue7249() {
+    let mut it = 0..10;
+    let mut x = || {
+        // Needs &mut, the closure can be called multiple times
+        while let Some(x) = it.next() {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    };
+    x();
+    x();
+}
+
 fn main() {
     let mut it = 0..20;
     while let Some(..) = it.next() {
index eff559bef7e3b87a3ab9491876558389070393a9..cb0afeae15ee0ead89f0bbe769e7af04660ccd72 100644 (file)
@@ -105,10 +105,16 @@ LL |     while let Some(n) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:325:5
+  --> $DIR/while_let_on_iterator.rs:327:9
+   |
+LL |         while let Some(x) = it.next() {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:339:5
    |
 LL |     while let Some(..) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
 
-error: aborting due to 18 previous errors
+error: aborting due to 19 previous errors