diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index c0c231bb2..10f2bef26 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -98,10 +98,16 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc // Method call on `id` in a statement ignores any return value, so it's not a read access: // // id.foo(...); // Not reading `id`. + // + // Only assuming this for "official" methods defined on the type. For methods defined in extension + // traits (identified as local, based on the orphan rule), pessimistically assume that they might + // have side effects, so consider them a read. if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id) && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind && path_to_local_id(receiver, id) && let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id) + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && !method_def_id.is_local() { return ControlFlow::Continue(()); } diff --git a/tests/ui/collection_is_never_read.rs b/tests/ui/collection_is_never_read.rs index 8f5ceb06b..49c72e7ee 100644 --- a/tests/ui/collection_is_never_read.rs +++ b/tests/ui/collection_is_never_read.rs @@ -133,3 +133,23 @@ fn not_read_if_return_value_not_used() { let x = vec![1, 2, 3]; // WARNING x.is_empty(); } + +fn extension_traits() { + trait VecExt { + fn method_with_side_effect(&self); + fn method_without_side_effect(&self); + } + + impl VecExt for Vec { + fn method_with_side_effect(&self) { + println!("my length: {}", self.len()); + } + fn method_without_side_effect(&self) {} + } + + let x = vec![1, 2, 3]; // Ok + x.method_with_side_effect(); + + let y = vec![1, 2, 3]; // Ok (false negative) + y.method_without_side_effect(); +}