Skip to content

Commit a5c16e5

Browse files
committed
Auto merge of #3789 - bzzzzzz:needless_range_loop_bugfix, r=oli-obk
Make needless_range_loop not applicable to structures without iter method Fixes #3788 Now we will start lint indexed structure only if it has known iter or iter_mut method implemented.
2 parents 027dde9 + 7767b3a commit a5c16e5

File tree

5 files changed

+90
-46
lines changed

5 files changed

+90
-46
lines changed

clippy_lints/src/loops.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use syntax_pos::BytePos;
2727

2828
use crate::utils::paths;
2929
use crate::utils::{
30-
get_enclosing_block, get_parent_expr, higher, is_integer_literal, is_refutable, last_path_segment,
30+
get_enclosing_block, get_parent_expr, has_iter_method, higher, is_integer_literal, is_refutable, last_path_segment,
3131
match_trait_method, match_type, match_var, multispan_sugg, snippet, snippet_opt, snippet_with_applicability,
3232
span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then, SpanlessEq,
3333
};
@@ -1118,6 +1118,12 @@ fn check_for_loop_range<'a, 'tcx>(
11181118
}
11191119
}
11201120

1121+
// don't lint if the container that is indexed does not have .iter() method
1122+
let has_iter = has_iter_method(cx, indexed_ty);
1123+
if has_iter.is_none() {
1124+
return;
1125+
}
1126+
11211127
// don't lint if the container that is indexed into is also used without
11221128
// indexing
11231129
if visitor.referenced.contains(&indexed) {

clippy_lints/src/methods/mod.rs

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::utils::paths;
22
use crate::utils::sugg;
33
use crate::utils::{
4-
get_arg_name, get_parent_expr, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, is_self,
5-
is_self_ty, iter_input_pats, last_path_segment, match_def_path, match_path, match_qpath, match_trait_method,
6-
match_type, match_var, method_calls, method_chain_args, remove_blocks, return_ty, same_tys, single_segment_path,
7-
snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_sugg,
8-
span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
4+
get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy, is_expn_of,
5+
is_self, is_self_ty, iter_input_pats, last_path_segment, match_def_path, match_path, match_qpath,
6+
match_trait_method, match_type, match_var, method_calls, method_chain_args, remove_blocks, return_ty, same_tys,
7+
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
8+
span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
99
};
1010
use if_chain::if_chain;
1111
use matches::matches;
@@ -2237,47 +2237,23 @@ fn ty_has_iter_method(
22372237
cx: &LateContext<'_, '_>,
22382238
self_ref_ty: ty::Ty<'_>,
22392239
) -> Option<(&'static Lint, &'static str, &'static str)> {
2240-
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
2241-
// exists and has the desired signature. Unfortunately FnCtxt is not exported
2242-
// so we can't use its `lookup_method` method.
2243-
static INTO_ITER_COLLECTIONS: [(&Lint, &[&str]); 13] = [
2244-
(INTO_ITER_ON_REF, &paths::VEC),
2245-
(INTO_ITER_ON_REF, &paths::OPTION),
2246-
(INTO_ITER_ON_REF, &paths::RESULT),
2247-
(INTO_ITER_ON_REF, &paths::BTREESET),
2248-
(INTO_ITER_ON_REF, &paths::BTREEMAP),
2249-
(INTO_ITER_ON_REF, &paths::VEC_DEQUE),
2250-
(INTO_ITER_ON_REF, &paths::LINKED_LIST),
2251-
(INTO_ITER_ON_REF, &paths::BINARY_HEAP),
2252-
(INTO_ITER_ON_REF, &paths::HASHSET),
2253-
(INTO_ITER_ON_REF, &paths::HASHMAP),
2254-
(INTO_ITER_ON_ARRAY, &["std", "path", "PathBuf"]),
2255-
(INTO_ITER_ON_REF, &["std", "path", "Path"]),
2256-
(INTO_ITER_ON_REF, &["std", "sync", "mpsc", "Receiver"]),
2257-
];
2258-
2259-
let (self_ty, mutbl) = match self_ref_ty.sty {
2260-
ty::Ref(_, self_ty, mutbl) => (self_ty, mutbl),
2261-
_ => unreachable!(),
2262-
};
2263-
let method_name = match mutbl {
2264-
hir::MutImmutable => "iter",
2265-
hir::MutMutable => "iter_mut",
2266-
};
2267-
2268-
let def_id = match self_ty.sty {
2269-
ty::Array(..) => return Some((INTO_ITER_ON_ARRAY, "array", method_name)),
2270-
ty::Slice(..) => return Some((INTO_ITER_ON_REF, "slice", method_name)),
2271-
ty::Adt(adt, _) => adt.did,
2272-
_ => return None,
2273-
};
2274-
2275-
for (lint, path) in &INTO_ITER_COLLECTIONS {
2276-
if match_def_path(cx.tcx, def_id, path) {
2277-
return Some((lint, path.last().unwrap(), method_name));
2278-
}
2240+
if let Some(ty_name) = has_iter_method(cx, self_ref_ty) {
2241+
let lint = match ty_name {
2242+
"array" | "PathBuf" => INTO_ITER_ON_ARRAY,
2243+
_ => INTO_ITER_ON_REF,
2244+
};
2245+
let mutbl = match self_ref_ty.sty {
2246+
ty::Ref(_, _, mutbl) => mutbl,
2247+
_ => unreachable!(),
2248+
};
2249+
let method_name = match mutbl {
2250+
hir::MutImmutable => "iter",
2251+
hir::MutMutable => "iter_mut",
2252+
};
2253+
Some((lint, ty_name, method_name))
2254+
} else {
2255+
None
22792256
}
2280-
None
22812257
}
22822258

22832259
fn lint_into_iter(cx: &LateContext<'_, '_>, expr: &hir::Expr, self_ref_ty: ty::Ty<'_>, method_span: Span) {

clippy_lints/src/utils/mod.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,47 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_, '_, '_>, node: NodeId
11551155
false
11561156
}
11571157

1158+
/// Returns true if ty has `iter` or `iter_mut` methods
1159+
pub fn has_iter_method(cx: &LateContext<'_, '_>, probably_ref_ty: ty::Ty<'_>) -> Option<&'static str> {
1160+
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
1161+
// exists and has the desired signature. Unfortunately FnCtxt is not exported
1162+
// so we can't use its `lookup_method` method.
1163+
static INTO_ITER_COLLECTIONS: [&[&str]; 13] = [
1164+
&paths::VEC,
1165+
&paths::OPTION,
1166+
&paths::RESULT,
1167+
&paths::BTREESET,
1168+
&paths::BTREEMAP,
1169+
&paths::VEC_DEQUE,
1170+
&paths::LINKED_LIST,
1171+
&paths::BINARY_HEAP,
1172+
&paths::HASHSET,
1173+
&paths::HASHMAP,
1174+
&paths::PATH_BUF,
1175+
&paths::PATH,
1176+
&paths::RECEIVER,
1177+
];
1178+
1179+
let ty_to_check = match probably_ref_ty.sty {
1180+
ty::Ref(_, ty_to_check, _) => ty_to_check,
1181+
_ => probably_ref_ty,
1182+
};
1183+
1184+
let def_id = match ty_to_check.sty {
1185+
ty::Array(..) => return Some("array"),
1186+
ty::Slice(..) => return Some("slice"),
1187+
ty::Adt(adt, _) => adt.did,
1188+
_ => return None,
1189+
};
1190+
1191+
for path in &INTO_ITER_COLLECTIONS {
1192+
if match_def_path(cx.tcx, def_id, path) {
1193+
return Some(path.last().unwrap());
1194+
}
1195+
}
1196+
None
1197+
}
1198+
11581199
#[cfg(test)]
11591200
mod test {
11601201
use super::{trim_multiline, without_block_comments};

clippy_lints/src/utils/paths.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
6262
pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
6363
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
6464
pub const PARTIAL_ORD: [&str; 3] = ["core", "cmp", "PartialOrd"];
65+
pub const PATH: [&str; 3] = ["std", "path", "Path"];
6566
pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
6667
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
6768
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
@@ -80,6 +81,7 @@ pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
8081
pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
8182
pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
8283
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
84+
pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
8385
pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
8486
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
8587
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];

tests/ui/needless_range_loop.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,23 @@ fn main() {
8585
for i in 0..vec.len() {
8686
vec[i] = Some(1).unwrap_or_else(|| panic!("error on {}", i));
8787
}
88+
89+
// #3788
90+
let test = Test {
91+
inner: vec![1, 2, 3, 4],
92+
};
93+
for i in 0..2 {
94+
println!("{}", test[i]);
95+
}
96+
}
97+
98+
struct Test {
99+
inner: Vec<usize>,
100+
}
101+
102+
impl std::ops::Index<usize> for Test {
103+
type Output = usize;
104+
fn index(&self, index: usize) -> &Self::Output {
105+
&self.inner[index]
106+
}
88107
}

0 commit comments

Comments
 (0)