Skip to content

Commit f9c6682

Browse files
committed
Make needless_range_loop not applicable to structures without iter method
1 parent 075c212 commit f9c6682

File tree

5 files changed

+89
-42
lines changed

5 files changed

+89
-42
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: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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,
4+
get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy, is_expn_of, is_self,
55
is_self_ty, iter_input_pats, last_path_segment, match_def_path, match_path, match_qpath, match_trait_method,
66
match_type, match_var, method_calls, method_chain_args, remove_blocks, return_ty, same_tys, single_segment_path,
77
snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_sugg,
@@ -2228,47 +2228,23 @@ fn ty_has_iter_method(
22282228
cx: &LateContext<'_, '_>,
22292229
self_ref_ty: ty::Ty<'_>,
22302230
) -> Option<(&'static Lint, &'static str, &'static str)> {
2231-
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
2232-
// exists and has the desired signature. Unfortunately FnCtxt is not exported
2233-
// so we can't use its `lookup_method` method.
2234-
static INTO_ITER_COLLECTIONS: [(&Lint, &[&str]); 13] = [
2235-
(INTO_ITER_ON_REF, &paths::VEC),
2236-
(INTO_ITER_ON_REF, &paths::OPTION),
2237-
(INTO_ITER_ON_REF, &paths::RESULT),
2238-
(INTO_ITER_ON_REF, &paths::BTREESET),
2239-
(INTO_ITER_ON_REF, &paths::BTREEMAP),
2240-
(INTO_ITER_ON_REF, &paths::VEC_DEQUE),
2241-
(INTO_ITER_ON_REF, &paths::LINKED_LIST),
2242-
(INTO_ITER_ON_REF, &paths::BINARY_HEAP),
2243-
(INTO_ITER_ON_REF, &paths::HASHSET),
2244-
(INTO_ITER_ON_REF, &paths::HASHMAP),
2245-
(INTO_ITER_ON_ARRAY, &["std", "path", "PathBuf"]),
2246-
(INTO_ITER_ON_REF, &["std", "path", "Path"]),
2247-
(INTO_ITER_ON_REF, &["std", "sync", "mpsc", "Receiver"]),
2248-
];
2249-
2250-
let (self_ty, mutbl) = match self_ref_ty.sty {
2251-
ty::Ref(_, self_ty, mutbl) => (self_ty, mutbl),
2252-
_ => unreachable!(),
2253-
};
2254-
let method_name = match mutbl {
2255-
hir::MutImmutable => "iter",
2256-
hir::MutMutable => "iter_mut",
2257-
};
2258-
2259-
let def_id = match self_ty.sty {
2260-
ty::Array(..) => return Some((INTO_ITER_ON_ARRAY, "array", method_name)),
2261-
ty::Slice(..) => return Some((INTO_ITER_ON_REF, "slice", method_name)),
2262-
ty::Adt(adt, _) => adt.did,
2263-
_ => return None,
2264-
};
2265-
2266-
for (lint, path) in &INTO_ITER_COLLECTIONS {
2267-
if match_def_path(cx.tcx, def_id, path) {
2268-
return Some((lint, path.last().unwrap(), method_name));
2269-
}
2231+
if let Some(ty_name) = has_iter_method(cx, self_ref_ty) {
2232+
let lint = match ty_name {
2233+
"array" | "PathBuf" => INTO_ITER_ON_ARRAY,
2234+
_ => INTO_ITER_ON_REF,
2235+
};
2236+
let mutbl = match self_ref_ty.sty {
2237+
ty::Ref(_, _, mutbl) => mutbl,
2238+
_ => unreachable!(),
2239+
};
2240+
let method_name = match mutbl {
2241+
hir::MutImmutable => "iter",
2242+
hir::MutMutable => "iter_mut",
2243+
};
2244+
Some((lint, ty_name, method_name))
2245+
} else {
2246+
None
22702247
}
2271-
None
22722248
}
22732249

22742250
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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,50 @@ 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(
1160+
cx: &LateContext<'_, '_>,
1161+
probably_ref_ty: ty::Ty<'_>,
1162+
) -> Option<&'static str> {
1163+
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
1164+
// exists and has the desired signature. Unfortunately FnCtxt is not exported
1165+
// so we can't use its `lookup_method` method.
1166+
static INTO_ITER_COLLECTIONS: [&[&str]; 13] = [
1167+
&paths::VEC,
1168+
&paths::OPTION,
1169+
&paths::RESULT,
1170+
&paths::BTREESET,
1171+
&paths::BTREEMAP,
1172+
&paths::VEC_DEQUE,
1173+
&paths::LINKED_LIST,
1174+
&paths::BINARY_HEAP,
1175+
&paths::HASHSET,
1176+
&paths::HASHMAP,
1177+
&paths::PATH_BUF,
1178+
&paths::PATH,
1179+
&paths::RECIEVER,
1180+
];
1181+
1182+
let ty_to_check = match probably_ref_ty.sty {
1183+
ty::Ref(_, ty_to_check, _) => ty_to_check,
1184+
_ => probably_ref_ty,
1185+
};
1186+
1187+
let def_id = match ty_to_check.sty {
1188+
ty::Array(..) => return Some("array"),
1189+
ty::Slice(..) => return Some("slice"),
1190+
ty::Adt(adt, _) => adt.did,
1191+
_ => return None,
1192+
};
1193+
1194+
for path in &INTO_ITER_COLLECTIONS {
1195+
if match_def_path(cx.tcx, def_id, path) {
1196+
return Some(path.last().unwrap());
1197+
}
1198+
}
1199+
None
1200+
}
1201+
11581202
#[cfg(test)]
11591203
mod test {
11601204
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 RECIEVER: [&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)