Skip to content

Commit 8213d25

Browse files
committed
Auto merge of #3861 - flip1995:rollup, r=flip1995
Rollup of 3 pull requests Successful merges: - #3851 (Refactor: Extract `trait_ref_of_method` function) - #3852 (Refactor: Cleanup one part of assign_ops lint) - #3857 (Document match_path, improve match_qpath docs) Failed merges: r? @ghost
2 parents bd6b5a1 + c32135a commit 8213d25

File tree

4 files changed

+107
-74
lines changed

4 files changed

+107
-74
lines changed

clippy_lints/src/assign_ops.rs

Lines changed: 56 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::utils::{get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, SpanlessEq};
1+
use crate::utils::{
2+
get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
3+
};
24
use crate::utils::{higher, sugg};
35
use if_chain::if_chain;
46
use rustc::hir;
@@ -68,52 +70,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
6870
match &expr.node {
6971
hir::ExprKind::AssignOp(op, lhs, rhs) => {
7072
if let hir::ExprKind::Binary(binop, l, r) = &rhs.node {
71-
if op.node == binop.node {
72-
let lint = |assignee: &hir::Expr, rhs_other: &hir::Expr| {
73-
span_lint_and_then(
74-
cx,
75-
MISREFACTORED_ASSIGN_OP,
76-
expr.span,
77-
"variable appears on both sides of an assignment operation",
78-
|db| {
79-
if let (Some(snip_a), Some(snip_r)) =
80-
(snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span))
81-
{
82-
let a = &sugg::Sugg::hir(cx, assignee, "..");
83-
let r = &sugg::Sugg::hir(cx, rhs, "..");
84-
let long =
85-
format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
86-
db.span_suggestion(
87-
expr.span,
88-
&format!(
89-
"Did you mean {} = {} {} {} or {}? Consider replacing it with",
90-
snip_a,
91-
snip_a,
92-
op.node.as_str(),
93-
snip_r,
94-
long
95-
),
96-
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
97-
Applicability::MachineApplicable,
98-
);
99-
db.span_suggestion(
100-
expr.span,
101-
"or",
102-
long,
103-
Applicability::MachineApplicable, // snippet
104-
);
105-
}
106-
},
107-
);
108-
};
109-
// lhs op= l op r
110-
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
111-
lint(lhs, r);
112-
}
113-
// lhs op= l commutative_op r
114-
if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
115-
lint(lhs, l);
116-
}
73+
if op.node != binop.node {
74+
return;
75+
}
76+
// lhs op= l op r
77+
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
78+
lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
79+
}
80+
// lhs op= l commutative_op r
81+
if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
82+
lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
11783
}
11884
}
11985
},
@@ -140,13 +106,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
140106
};
141107
// check that we are not inside an `impl AssignOp` of this exact operation
142108
let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
143-
let parent_impl = cx.tcx.hir().get_parent_item(parent_fn);
144-
// the crate node is the only one that is not in the map
145109
if_chain! {
146-
if parent_impl != hir::CRATE_HIR_ID;
147-
if let hir::Node::Item(item) = cx.tcx.hir().get_by_hir_id(parent_impl);
148-
if let hir::ItemKind::Impl(_, _, _, _, Some(trait_ref), _, _) =
149-
&item.node;
110+
if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
150111
if trait_ref.path.def.def_id() == trait_id;
151112
then { return; }
152113
}
@@ -234,6 +195,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
234195
}
235196
}
236197

198+
fn lint_misrefactored_assign_op(
199+
cx: &LateContext<'_, '_>,
200+
expr: &hir::Expr,
201+
op: hir::BinOp,
202+
rhs: &hir::Expr,
203+
assignee: &hir::Expr,
204+
rhs_other: &hir::Expr,
205+
) {
206+
span_lint_and_then(
207+
cx,
208+
MISREFACTORED_ASSIGN_OP,
209+
expr.span,
210+
"variable appears on both sides of an assignment operation",
211+
|db| {
212+
if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
213+
let a = &sugg::Sugg::hir(cx, assignee, "..");
214+
let r = &sugg::Sugg::hir(cx, rhs, "..");
215+
let long = format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
216+
db.span_suggestion(
217+
expr.span,
218+
&format!(
219+
"Did you mean {} = {} {} {} or {}? Consider replacing it with",
220+
snip_a,
221+
snip_a,
222+
op.node.as_str(),
223+
snip_r,
224+
long
225+
),
226+
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
227+
Applicability::MachineApplicable,
228+
);
229+
db.span_suggestion(
230+
expr.span,
231+
"or",
232+
long,
233+
Applicability::MachineApplicable, // snippet
234+
);
235+
}
236+
},
237+
);
238+
}
239+
237240
fn is_commutative(op: hir::BinOpKind) -> bool {
238241
use rustc::hir::BinOpKind::*;
239242
match op {

clippy_lints/src/missing_const_for_fn.rs

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use crate::utils::{is_entrypoint_fn, span_lint};
2-
use if_chain::if_chain;
1+
use crate::utils::{is_entrypoint_fn, span_lint, trait_ref_of_method};
32
use rustc::hir;
43
use rustc::hir::intravisit::FnKind;
54
use rustc::hir::{Body, Constness, FnDecl, HirId};
@@ -96,7 +95,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
9695
}
9796
},
9897
FnKind::Method(_, sig, ..) => {
99-
if is_trait_method(cx, hir_id) || already_const(sig.header) {
98+
if trait_ref_of_method(cx, hir_id).is_some() || already_const(sig.header) {
10099
return;
101100
}
102101
},
@@ -115,18 +114,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
115114
}
116115
}
117116

118-
fn is_trait_method(cx: &LateContext<'_, '_>, hir_id: HirId) -> bool {
119-
// Get the implemented trait for the current function
120-
let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
121-
if_chain! {
122-
if parent_impl != hir::CRATE_HIR_ID;
123-
if let hir::Node::Item(item) = cx.tcx.hir().get_by_hir_id(parent_impl);
124-
if let hir::ItemKind::Impl(_, _, _, _, Some(_trait_ref), _, _) = &item.node;
125-
then { return true; }
126-
}
127-
false
128-
}
129-
130117
// We don't have to lint on something that's already `const`
131118
fn already_const(header: hir::FnHeader) -> bool {
132119
header.constness == Constness::Const

clippy_lints/src/suspicious_trait_impl.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::utils::{get_trait_def_id, span_lint};
1+
use crate::utils::{get_trait_def_id, span_lint, trait_ref_of_method};
22
use if_chain::if_chain;
33
use rustc::hir;
44
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
@@ -177,12 +177,9 @@ fn check_binop<'a>(
177177

178178
// Get the actually implemented trait
179179
let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
180-
let parent_impl = cx.tcx.hir().get_parent_item(parent_fn);
181180

182181
if_chain! {
183-
if parent_impl != hir::CRATE_HIR_ID;
184-
if let hir::Node::Item(item) = cx.tcx.hir().get_by_hir_id(parent_impl);
185-
if let hir::ItemKind::Impl(_, _, _, _, Some(ref trait_ref), _, _) = item.node;
182+
if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
186183
if let Some(idx) = trait_ids.iter().position(|&tid| tid == trait_ref.path.def.def_id());
187184
if binop != expected_ops[idx];
188185
then{

clippy_lints/src/utils/mod.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ pub fn single_segment_path(path: &QPath) -> Option<&PathSegment> {
190190
}
191191
}
192192

193-
/// Match a `Path` against a slice of segment string literals.
193+
/// Match a `QPath` against a slice of segment string literals.
194+
///
195+
/// There is also `match_path` if you are dealing with a `rustc::hir::Path` instead of a
196+
/// `rustc::hir::QPath`.
194197
///
195198
/// # Examples
196199
/// ```rust,ignore
@@ -210,6 +213,22 @@ pub fn match_qpath(path: &QPath, segments: &[&str]) -> bool {
210213
}
211214
}
212215

216+
/// Match a `Path` against a slice of segment string literals.
217+
///
218+
/// There is also `match_qpath` if you are dealing with a `rustc::hir::QPath` instead of a
219+
/// `rustc::hir::Path`.
220+
///
221+
/// # Examples
222+
///
223+
/// ```rust,ignore
224+
/// if match_path(&trait_ref.path, &paths::HASH) {
225+
/// // This is the `std::hash::Hash` trait.
226+
/// }
227+
///
228+
/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
229+
/// // This is a `rustc::lint::Lint`.
230+
/// }
231+
/// ```
213232
pub fn match_path(path: &Path, segments: &[&str]) -> bool {
214233
path.segments
215234
.iter()
@@ -301,6 +320,33 @@ pub fn implements_trait<'a, 'tcx>(
301320
.enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
302321
}
303322

323+
/// Get the `hir::TraitRef` of the trait the given method is implemented for
324+
///
325+
/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
326+
///
327+
/// ```rust
328+
/// struct Point(isize, isize);
329+
///
330+
/// impl std::ops::Add for Point {
331+
/// type Output = Self;
332+
///
333+
/// fn add(self, other: Self) -> Self {
334+
/// Point(0, 0)
335+
/// }
336+
/// }
337+
/// ```
338+
pub fn trait_ref_of_method(cx: &LateContext<'_, '_>, hir_id: HirId) -> Option<TraitRef> {
339+
// Get the implemented trait for the current function
340+
let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
341+
if_chain! {
342+
if parent_impl != hir::CRATE_HIR_ID;
343+
if let hir::Node::Item(item) = cx.tcx.hir().get_by_hir_id(parent_impl);
344+
if let hir::ItemKind::Impl(_, _, _, _, trait_ref, _, _) = &item.node;
345+
then { return trait_ref.clone(); }
346+
}
347+
None
348+
}
349+
304350
/// Check whether this type implements Drop.
305351
pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
306352
match ty.ty_adt_def() {

0 commit comments

Comments
 (0)