]> git.lizzy.rs Git - rust.git/commitdiff
Make needless_range_loop not applicable to structures without iter method
authorbzzzz <evgeny.barbashov@gmail.com>
Wed, 20 Feb 2019 08:10:25 +0000 (00:10 -0800)
committerbzzzz <evgeny.barbashov@gmail.com>
Wed, 20 Feb 2019 08:10:25 +0000 (00:10 -0800)
clippy_lints/src/loops.rs
clippy_lints/src/methods/mod.rs
clippy_lints/src/utils/mod.rs
clippy_lints/src/utils/paths.rs
tests/ui/needless_range_loop.rs

index 058d9adcb514e8ce181522083b3e2710cdba9f02..fcf6a879b91574d1c553f5dcc55e62ed5f533632 100644 (file)
@@ -27,7 +27,7 @@
 
 use crate::utils::paths;
 use crate::utils::{
-    get_enclosing_block, get_parent_expr, higher, is_integer_literal, is_refutable, last_path_segment,
+    get_enclosing_block, get_parent_expr, has_iter_method, higher, is_integer_literal, is_refutable, last_path_segment,
     match_trait_method, match_type, match_var, multispan_sugg, snippet, snippet_opt, snippet_with_applicability,
     span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then, SpanlessEq,
 };
@@ -1118,6 +1118,12 @@ fn check_for_loop_range<'a, 'tcx>(
                     }
                 }
 
+                // don't lint if the container that is indexed does not have .iter() method
+                let has_iter = has_iter_method(cx, indexed_ty);
+                if has_iter.is_none() {
+                    return;
+                }
+
                 // don't lint if the container that is indexed into is also used without
                 // indexing
                 if visitor.referenced.contains(&indexed) {
index c9b27ef161526c2500fa6969cd97ea18aa61fc56..477ad1613afeac785aabddac1d5925f03861428b 100644 (file)
@@ -1,7 +1,7 @@
 use crate::utils::paths;
 use crate::utils::sugg;
 use crate::utils::{
-    get_arg_name, get_parent_expr, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, is_self,
+    get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy, is_expn_of, is_self,
     is_self_ty, iter_input_pats, last_path_segment, match_def_path, match_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,
@@ -2228,47 +2228,23 @@ fn ty_has_iter_method(
     cx: &LateContext<'_, '_>,
     self_ref_ty: ty::Ty<'_>,
 ) -> Option<(&'static Lint, &'static str, &'static str)> {
-    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
-    // exists and has the desired signature. Unfortunately FnCtxt is not exported
-    // so we can't use its `lookup_method` method.
-    static INTO_ITER_COLLECTIONS: [(&Lint, &[&str]); 13] = [
-        (INTO_ITER_ON_REF, &paths::VEC),
-        (INTO_ITER_ON_REF, &paths::OPTION),
-        (INTO_ITER_ON_REF, &paths::RESULT),
-        (INTO_ITER_ON_REF, &paths::BTREESET),
-        (INTO_ITER_ON_REF, &paths::BTREEMAP),
-        (INTO_ITER_ON_REF, &paths::VEC_DEQUE),
-        (INTO_ITER_ON_REF, &paths::LINKED_LIST),
-        (INTO_ITER_ON_REF, &paths::BINARY_HEAP),
-        (INTO_ITER_ON_REF, &paths::HASHSET),
-        (INTO_ITER_ON_REF, &paths::HASHMAP),
-        (INTO_ITER_ON_ARRAY, &["std", "path", "PathBuf"]),
-        (INTO_ITER_ON_REF, &["std", "path", "Path"]),
-        (INTO_ITER_ON_REF, &["std", "sync", "mpsc", "Receiver"]),
-    ];
-
-    let (self_ty, mutbl) = match self_ref_ty.sty {
-        ty::Ref(_, self_ty, mutbl) => (self_ty, mutbl),
-        _ => unreachable!(),
-    };
-    let method_name = match mutbl {
-        hir::MutImmutable => "iter",
-        hir::MutMutable => "iter_mut",
-    };
-
-    let def_id = match self_ty.sty {
-        ty::Array(..) => return Some((INTO_ITER_ON_ARRAY, "array", method_name)),
-        ty::Slice(..) => return Some((INTO_ITER_ON_REF, "slice", method_name)),
-        ty::Adt(adt, _) => adt.did,
-        _ => return None,
-    };
-
-    for (lint, path) in &INTO_ITER_COLLECTIONS {
-        if match_def_path(cx.tcx, def_id, path) {
-            return Some((lint, path.last().unwrap(), method_name));
-        }
+    if let Some(ty_name) = has_iter_method(cx, self_ref_ty) {
+        let lint = match ty_name {
+            "array" | "PathBuf" => INTO_ITER_ON_ARRAY,
+            _ => INTO_ITER_ON_REF,
+        };
+        let mutbl = match self_ref_ty.sty {
+            ty::Ref(_, _, mutbl) => mutbl,
+            _ => unreachable!(),
+        };
+        let method_name = match mutbl {
+            hir::MutImmutable => "iter",
+            hir::MutMutable => "iter_mut",
+        };
+        Some((lint, ty_name, method_name))
+    } else {
+        None
     }
-    None
 }
 
 fn lint_into_iter(cx: &LateContext<'_, '_>, expr: &hir::Expr, self_ref_ty: ty::Ty<'_>, method_span: Span) {
index 0816c209a42db61aaa32e630c2cd78ae351c0422..ce4213327b9298e8569448ed30e3c78087c30b52 100644 (file)
@@ -1155,6 +1155,50 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_, '_, '_>, node: NodeId
     false
 }
 
+/// Returns true if ty has `iter` or `iter_mut` methods
+pub fn has_iter_method(
+    cx: &LateContext<'_, '_>,
+    probably_ref_ty: ty::Ty<'_>,
+) -> Option<&'static str> {
+    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
+    // exists and has the desired signature. Unfortunately FnCtxt is not exported
+    // so we can't use its `lookup_method` method.
+    static INTO_ITER_COLLECTIONS: [&[&str]; 13] = [
+        &paths::VEC,
+        &paths::OPTION,
+        &paths::RESULT,
+        &paths::BTREESET,
+        &paths::BTREEMAP,
+        &paths::VEC_DEQUE,
+        &paths::LINKED_LIST,
+        &paths::BINARY_HEAP,
+        &paths::HASHSET,
+        &paths::HASHMAP,
+        &paths::PATH_BUF,
+        &paths::PATH,
+        &paths::RECIEVER,
+    ];
+
+    let ty_to_check = match probably_ref_ty.sty {
+        ty::Ref(_, ty_to_check, _) => ty_to_check,
+        _ => probably_ref_ty,
+    };
+
+    let def_id = match ty_to_check.sty {
+        ty::Array(..) => return Some("array"),
+        ty::Slice(..) => return Some("slice"),
+        ty::Adt(adt, _) => adt.did,
+        _ => return None,
+    };
+
+    for path in &INTO_ITER_COLLECTIONS {
+        if match_def_path(cx.tcx, def_id, path) {
+            return Some(path.last().unwrap());
+        }
+    }
+    None
+}
+
 #[cfg(test)]
 mod test {
     use super::{trim_multiline, without_block_comments};
index 4108732653ff3619923a246c81f051d9b34a8aa1..a6b3203fbef4355d4239a2720a5c6091d1100d1d 100644 (file)
@@ -62,6 +62,7 @@
 pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
 pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
 pub const PARTIAL_ORD: [&str; 3] = ["core", "cmp", "PartialOrd"];
+pub const PATH: [&str; 3] = ["std", "path", "Path"];
 pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
 pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
 pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
@@ -80,6 +81,7 @@
 pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
 pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
 pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
+pub const RECIEVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
 pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
 pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
 pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
index 5f22e2645d12b4d17b5320679dd17e5c8327e3b1..70ad6eac65f6d59dced91a7ad969c50b4d472adc 100644 (file)
@@ -85,4 +85,23 @@ fn main() {
     for i in 0..vec.len() {
         vec[i] = Some(1).unwrap_or_else(|| panic!("error on {}", i));
     }
+
+    // #3788
+    let test = Test {
+        inner: vec![1, 2, 3, 4],
+    };
+    for i in 0..2 {
+        println!("{}", test[i]);
+    }
+}
+
+struct Test {
+    inner: Vec<usize>,
+}
+
+impl std::ops::Index<usize> for Test {
+    type Output = usize;
+    fn index(&self, index: usize) -> &Self::Output {
+        &self.inner[index]
+    }
 }