Skip to content

Commit 743e9e3

Browse files
committed
Merge branch 'master' into rustfmt_tests
2 parents 625ca77 + 0fd7fe9 commit 743e9e3

9 files changed

+151
-44
lines changed

CONTRIBUTING.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ All contributors are expected to follow the [Rust Code of Conduct](http://www.ru
2323
* [How Clippy works](#how-clippy-works)
2424
* [Fixing nightly build failures](#fixing-build-failures-caused-by-rust)
2525
* [Issue and PR Triage](#issue-and-pr-triage)
26+
* [Bors and Homu](#bors-and-homu)
2627
* [Contributions](#contributions)
2728

2829
## Getting started
@@ -156,7 +157,7 @@ to style guidelines. The code has to be formatted by `rustfmt` before a PR will
156157

157158
It can be installed via `rustup`:
158159
```bash
159-
rustup component add rustfmt-preview
160+
rustup component add rustfmt
160161
```
161162

162163
Use `cargo fmt --all` to format the whole codebase.
@@ -220,7 +221,7 @@ That's why the `else_if_without_else` example uses the `register_early_lint_pass
220221

221222
### Fixing build failures caused by Rust
222223

223-
Clippy will sometimes break because it still depends on unstable internal Rust features. Most of the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. Fixing build failures caused by Rust updates, can be a good way to learn about Rust internals.
224+
Clippy will sometimes fail to build from source because building it depends on unstable internal Rust features. Most of the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. Fixing build failures caused by Rust updates, can be a good way to learn about Rust internals.
224225

225226
In order to find out why Clippy does not work properly with a new Rust commit, you can use the [rust-toolstate commit history][toolstate_commit_history].
226227
You will then have to look for the last commit that contains `test-pass -> build-fail` or `test-pass` -> `test-fail` for the `clippy-driver` component. [Here][toolstate_commit] is an example.
@@ -257,6 +258,17 @@ Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug]. We don't
257258
want Clippy to crash on your code and we want it to be as reliable as the
258259
suggestions from Rust compiler errors.
259260

261+
## Bors and Homu
262+
263+
We use a bot powered by [Homu][homu] to help automate testing and landing of pull
264+
requests in Clippy. The bot's username is @bors.
265+
266+
You can find the Clippy bors queue [here][homu_queue].
267+
268+
If you have @bors permissions, you can find an overview of the available
269+
commands [here][homu_instructions].
270+
271+
260272
## Contributions
261273

262274
Contributions to Clippy should be made in the form of GitHub pull requests. Each pull request will
@@ -288,3 +300,6 @@ or the [MIT](http://opensource.org/licenses/MIT) license.
288300
[triage]: https://forge.rust-lang.org/triage-procedure.html
289301
[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash%20%3Aboom%3A
290302
[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug%20%3Abeetle%3A
303+
[homu]: https://github.com/servo/homu
304+
[homu_instructions]: https://buildbot2.rust-lang.org/homu/
305+
[homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy

ci/base-tests.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ fi
2020
# build clippy in debug mode and run tests
2121
cargo build --features debugging
2222
cargo test --features debugging
23+
# for faster build, share target dir between subcrates
24+
export CARGO_TARGET_DIR=`pwd`/target/
2325
cd clippy_lints && cargo test && cd ..
2426
cd rustc_tools_util && cargo test && cd ..
2527
cd clippy_dev && cargo test && cd ..

clippy_lints/src/attrs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) {
332332
&format!("unknown clippy lint: clippy::{}", name),
333333
|db| {
334334
if name.as_str().chars().any(|c| c.is_uppercase()) {
335-
let name_lower = name.as_str().to_lowercase().to_string();
335+
let name_lower = name.as_str().to_lowercase();
336336
match lint_store.check_lint_name(
337337
&name_lower,
338338
Some(tool_name.as_str())

clippy_lints/src/excessive_precision.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,9 @@ impl ExcessivePrecision {
109109
}
110110
}
111111

112-
#[allow(clippy::doc_markdown)]
113112
/// Should we exclude the float because it has a `.0` or `.` suffix
114-
/// Ex 1_000_000_000.0
115-
/// Ex 1_000_000_000.
113+
/// Ex `1_000_000_000.0`
114+
/// Ex `1_000_000_000.`
116115
fn dot_zero_exclusion(s: &str) -> bool {
117116
if let Some(after_dec) = s.split('.').nth(1) {
118117
let mut decpart = after_dec.chars().take_while(|c| *c != 'e' || *c != 'E');

clippy_lints/src/no_effect.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
5757
}
5858
match expr.node {
5959
ExprKind::Lit(..) | ExprKind::Closure(.., _) => true,
60-
ExprKind::Path(..) => !has_drop(cx, expr),
60+
ExprKind::Path(..) => !has_drop(cx, cx.tables.expr_ty(expr)),
6161
ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => {
6262
has_no_effect(cx, a) && has_no_effect(cx, b)
6363
},
@@ -70,7 +70,7 @@ fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
7070
| ExprKind::AddrOf(_, ref inner)
7171
| ExprKind::Box(ref inner) => has_no_effect(cx, inner),
7272
ExprKind::Struct(_, ref fields, ref base) => {
73-
!has_drop(cx, expr)
73+
!has_drop(cx, cx.tables.expr_ty(expr))
7474
&& fields.iter().all(|field| has_no_effect(cx, &field.expr))
7575
&& match *base {
7676
Some(ref base) => has_no_effect(cx, base),
@@ -82,7 +82,7 @@ fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
8282
let def = cx.tables.qpath_def(qpath, callee.hir_id);
8383
match def {
8484
Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) => {
85-
!has_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg))
85+
!has_drop(cx, cx.tables.expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg))
8686
},
8787
_ => false,
8888
}
@@ -161,7 +161,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option<Vec
161161
| ExprKind::AddrOf(_, ref inner)
162162
| ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
163163
ExprKind::Struct(_, ref fields, ref base) => {
164-
if has_drop(cx, expr) {
164+
if has_drop(cx, cx.tables.expr_ty(expr)) {
165165
None
166166
} else {
167167
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
@@ -172,7 +172,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option<Vec
172172
let def = cx.tables.qpath_def(qpath, callee.hir_id);
173173
match def {
174174
Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..)
175-
if !has_drop(cx, expr) =>
175+
if !has_drop(cx, cx.tables.expr_ty(expr)) =>
176176
{
177177
Some(args.iter().collect())
178178
},

clippy_lints/src/redundant_clone.rs

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::rustc::hir::{def_id, Body, FnDecl};
1212
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
1313
use crate::rustc::mir::{
1414
self, traversal,
15-
visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor},
15+
visit::{MutatingUseContext, PlaceContext, Visitor},
1616
TerminatorKind,
1717
};
1818
use crate::rustc::ty;
@@ -23,10 +23,11 @@ use crate::syntax::{
2323
source_map::{BytePos, Span},
2424
};
2525
use crate::utils::{
26-
in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node, span_lint_node_and_then,
27-
walk_ptrs_ty_depth,
26+
has_drop, in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node,
27+
span_lint_node_and_then, walk_ptrs_ty_depth,
2828
};
2929
use if_chain::if_chain;
30+
use matches::matches;
3031
use std::convert::TryFrom;
3132

3233
macro_rules! unwrap_or_continue {
@@ -126,7 +127,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
126127
// _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
127128
// In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
128129
// block.
129-
let cloned = unwrap_or_continue!(find_stmt_assigns_to(arg, from_borrow, bbdata.statements.iter().rev()));
130+
let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(
131+
cx,
132+
mir,
133+
arg,
134+
from_borrow,
135+
bbdata.statements.iter()
136+
));
137+
138+
if from_borrow && cannot_move_out {
139+
continue;
140+
}
130141

131142
// _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
132143
let referent = if from_deref {
@@ -150,7 +161,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
150161
}
151162
};
152163

153-
unwrap_or_continue!(find_stmt_assigns_to(pred_arg, true, mir[ps[0]].statements.iter().rev()))
164+
let (local, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(
165+
cx,
166+
mir,
167+
pred_arg,
168+
true,
169+
mir[ps[0]].statements.iter()
170+
));
171+
if cannot_move_out {
172+
continue;
173+
}
174+
local
154175
} else {
155176
cloned
156177
};
@@ -227,27 +248,69 @@ fn is_call_with_ref_arg<'tcx>(
227248
}
228249
}
229250

230-
/// Finds the first `to = (&)from`, and returns `Some(from)`.
251+
type CannotMoveOut = bool;
252+
253+
/// Finds the first `to = (&)from`, and returns
254+
/// ``Some((from, [`true` if `from` cannot be moved out]))``.
231255
fn find_stmt_assigns_to<'a, 'tcx: 'a>(
256+
cx: &LateContext<'_, 'tcx>,
257+
mir: &mir::Mir<'tcx>,
232258
to: mir::Local,
233259
by_ref: bool,
234-
mut stmts: impl Iterator<Item = &'a mir::Statement<'tcx>>,
235-
) -> Option<mir::Local> {
236-
stmts.find_map(|stmt| {
237-
if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
238-
if *local == to {
239-
if by_ref {
240-
if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
241-
return Some(r);
242-
}
243-
} else if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
244-
return Some(r);
260+
stmts: impl DoubleEndedIterator<Item = &'a mir::Statement<'tcx>>,
261+
) -> Option<(mir::Local, CannotMoveOut)> {
262+
stmts
263+
.rev()
264+
.find_map(|stmt| {
265+
if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
266+
if *local == to {
267+
return Some(v);
245268
}
246269
}
247-
}
248270

249-
None
250-
})
271+
None
272+
})
273+
.and_then(|v| {
274+
if by_ref {
275+
if let mir::Rvalue::Ref(_, _, ref place) = **v {
276+
return base_local_and_movability(cx, mir, place);
277+
}
278+
} else if let mir::Rvalue::Use(mir::Operand::Copy(ref place)) = **v {
279+
return base_local_and_movability(cx, mir, place);
280+
}
281+
None
282+
})
283+
}
284+
285+
/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
286+
/// if it is already a `Local`.
287+
///
288+
/// Also reports whether given `place` cannot be moved out.
289+
fn base_local_and_movability<'tcx>(
290+
cx: &LateContext<'_, 'tcx>,
291+
mir: &mir::Mir<'tcx>,
292+
mut place: &mir::Place<'tcx>,
293+
) -> Option<(mir::Local, CannotMoveOut)> {
294+
use rustc::mir::Place::*;
295+
296+
// Dereference. You cannot move things out from a borrowed value.
297+
let mut deref = false;
298+
// Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
299+
let mut field = false;
300+
301+
loop {
302+
match place {
303+
Local(local) => return Some((*local, deref || field)),
304+
Projection(proj) => {
305+
place = &proj.base;
306+
deref = deref || matches!(proj.elem, mir::ProjectionElem::Deref);
307+
if !field && matches!(proj.elem, mir::ProjectionElem::Field(..)) {
308+
field = has_drop(cx, place.ty(&mir.local_decls, cx.tcx).to_ty(cx.tcx));
309+
}
310+
},
311+
_ => return None,
312+
}
313+
}
251314
}
252315

253316
struct LocalUseVisitor {
@@ -279,9 +342,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
279342

280343
fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext<'tcx>, _: mir::Location) {
281344
match ctx {
282-
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(NonUseContext::StorageDead) => {
283-
return;
284-
},
345+
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) => return,
285346
_ => {},
286347
}
287348

clippy_lints/src/utils/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,8 @@ pub fn implements_trait<'a, 'tcx>(
266266
}
267267

268268
/// Check whether this type implements Drop.
269-
pub fn has_drop(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
270-
let struct_ty = cx.tables.expr_ty(expr);
271-
match struct_ty.ty_adt_def() {
269+
pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
270+
match ty.ty_adt_def() {
272271
Some(def) => def.has_dtor(cx.tcx),
273272
_ => false,
274273
}

tests/ui/redundant_clone.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,33 @@ fn main() {
3535
// Check that lint level works
3636
#[allow(clippy::redundant_clone)]
3737
let _ = String::new().to_string();
38+
39+
let tup = (String::from("foo"),);
40+
let _ = tup.0.clone();
41+
42+
let tup_ref = &(String::from("foo"),);
43+
let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
3844
}
3945

4046
#[derive(Clone)]
4147
struct Alpha;
42-
fn double(a: Alpha) -> (Alpha, Alpha) {
43-
if true {
48+
fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
49+
if b {
4450
(a.clone(), a.clone())
4551
} else {
4652
(Alpha, a)
4753
}
4854
}
55+
56+
struct TypeWithDrop {
57+
x: String,
58+
}
59+
60+
impl Drop for TypeWithDrop {
61+
fn drop(&mut self) {}
62+
}
63+
64+
fn cannot_move_from_type_with_drop() -> String {
65+
let s = TypeWithDrop { x: String::new() };
66+
s.x.clone() // removing this `clone()` summons E0509
67+
}

tests/ui/redundant_clone.stderr

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,28 @@ note: this value is dropped without further use
9696
| ^^^^^^^^^^^^^^^
9797

9898
error: redundant clone
99-
--> $DIR/redundant_clone.rs:44:22
99+
--> $DIR/redundant_clone.rs:40:18
100100
|
101-
44 | (a.clone(), a.clone())
101+
40 | let _ = tup.0.clone();
102+
| ^^^^^^^^ help: remove this
103+
|
104+
note: this value is dropped without further use
105+
--> $DIR/redundant_clone.rs:40:13
106+
|
107+
40 | let _ = tup.0.clone();
108+
| ^^^^^
109+
110+
error: redundant clone
111+
--> $DIR/redundant_clone.rs:50:22
112+
|
113+
50 | (a.clone(), a.clone())
102114
| ^^^^^^^^ help: remove this
103115
|
104116
note: this value is dropped without further use
105-
--> $DIR/redundant_clone.rs:44:21
117+
--> $DIR/redundant_clone.rs:50:21
106118
|
107-
44 | (a.clone(), a.clone())
119+
50 | (a.clone(), a.clone())
108120
| ^
109121

110-
error: aborting due to 9 previous errors
122+
error: aborting due to 10 previous errors
111123

0 commit comments

Comments
 (0)