Skip to content

Commit 1f7d7d7

Browse files
authored
Merge pull request #1082 from Manishearth/small-fixes
Small fixes
2 parents ad1cd99 + efaed2e commit 1f7d7d7

File tree

7 files changed

+191
-26
lines changed

7 files changed

+191
-26
lines changed

clippy_lints/src/copies.rs

+36-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::collections::hash_map::Entry;
66
use syntax::parse::token::InternedString;
77
use syntax::util::small_vector::SmallVector;
88
use utils::{SpanlessEq, SpanlessHash};
9-
use utils::{get_parent_expr, in_macro, span_note_and_lint};
9+
use utils::{get_parent_expr, in_macro, span_lint_and_then, span_note_and_lint, snippet};
1010

1111
/// **What it does:** This lint checks for consecutive `ifs` with the same condition. This lint is
1212
/// `Warn` by default.
@@ -42,7 +42,8 @@ declare_lint! {
4242
/// purpose, you can factor them
4343
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
4444
///
45-
/// **Known problems:** Hopefully none.
45+
/// **Known problems:** False positive possible with order dependent `match`
46+
/// (see issue [#860](https://github.com/Manishearth/rust-clippy/issues/860)).
4647
///
4748
/// **Example:**
4849
/// ```rust,ignore
@@ -52,6 +53,23 @@ declare_lint! {
5253
/// Baz => bar(), // <= oops
5354
/// }
5455
/// ```
56+
///
57+
/// This should probably be
58+
/// ```rust,ignore
59+
/// match foo {
60+
/// Bar => bar(),
61+
/// Quz => quz(),
62+
/// Baz => baz(), // <= fixed
63+
/// }
64+
/// ```
65+
///
66+
/// or if the original code was not a typo:
67+
/// ```rust,ignore
68+
/// match foo {
69+
/// Bar | Baz => bar(), // <= shows the intent better
70+
/// Quz => quz(),
71+
/// }
72+
/// ```
5573
declare_lint! {
5674
pub MATCH_SAME_ARMS,
5775
Warn,
@@ -143,12 +161,25 @@ fn lint_match_arms(cx: &LateContext, expr: &Expr) {
143161

144162
if let ExprMatch(_, ref arms, MatchSource::Normal) = expr.node {
145163
if let Some((i, j)) = search_same(arms, hash, eq) {
146-
span_note_and_lint(cx,
164+
span_lint_and_then(cx,
147165
MATCH_SAME_ARMS,
148166
j.body.span,
149167
"this `match` has identical arm bodies",
150-
i.body.span,
151-
"same as this");
168+
|db| {
169+
db.span_note(i.body.span, "same as this");
170+
171+
// Note: this does not use `span_suggestion` on purpose: there is no clean way to
172+
// remove the other arm. Building a span and suggest to replace it to "" makes an
173+
// even more confusing error message. Also in order not to make up a span for the
174+
// whole pattern, the suggestion is only shown when there is only one pattern. The
175+
// user should know about `|` if they are already using it…
176+
177+
if i.pats.len() == 1 && j.pats.len() == 1 {
178+
let lhs = snippet(cx, i.pats[0].span, "<pat1>");
179+
let rhs = snippet(cx, j.pats[0].span, "<pat2>");
180+
db.span_note(i.body.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
181+
}
182+
});
152183
}
153184
}
154185
}

clippy_lints/src/loops.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use reexport::*;
22
use rustc::hir::*;
33
use rustc::hir::def::Def;
4+
use rustc::hir::def_id::DefId;
45
use rustc::hir::intravisit::{Visitor, walk_expr, walk_block, walk_decl};
56
use rustc::hir::map::Node::NodeBlock;
67
use rustc::lint::*;
@@ -337,7 +338,7 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
337338
if let PatKind::Binding(_, ref ident, _) = pat.node {
338339
let mut visitor = VarVisitor {
339340
cx: cx,
340-
var: ident.node,
341+
var: cx.tcx.expect_def(pat.id).def_id(),
341342
indexed: HashMap::new(),
342343
nonindex: false,
343344
};
@@ -667,15 +668,15 @@ impl<'a> Visitor<'a> for UsedVisitor {
667668

668669
struct VarVisitor<'v, 't: 'v> {
669670
cx: &'v LateContext<'v, 't>, // context reference
670-
var: Name, // var name to look for as index
671+
var: DefId, // var name to look for as index
671672
indexed: HashMap<Name, Option<CodeExtent>>, // indexed variables, the extent is None for global
672673
nonindex: bool, // has the var been used otherwise?
673674
}
674675

675676
impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
676677
fn visit_expr(&mut self, expr: &'v Expr) {
677678
if let ExprPath(None, ref path) = expr.node {
678-
if path.segments.len() == 1 && path.segments[0].name == self.var {
679+
if path.segments.len() == 1 && self.cx.tcx.expect_def(expr.id).def_id() == self.var {
679680
// we are referencing our variable! now check if it's as an index
680681
if_let_chain! {[
681682
let Some(parexpr) = get_parent_expr(self.cx, expr),

clippy_lints/src/new_without_default.rs

+15-9
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ use syntax::ast;
77
use syntax::codemap::Span;
88
use utils::paths;
99
use utils::{get_trait_def_id, implements_trait, in_external_macro, return_ty, same_tys, span_lint_and_then};
10+
use utils::sugg::DiagnosticBuilderExt;
1011

1112
/// **What it does:** This lints about type with a `fn new() -> Self` method
1213
/// and no implementation of
1314
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
1415
///
1516
/// **Why is this bad?** User might expect to be able to use
1617
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
17-
/// as the type can be
18-
/// constructed without arguments.
18+
/// as the type can be constructed without arguments.
1919
///
2020
/// **Known problems:** Hopefully none.
2121
///
@@ -118,20 +118,26 @@ impl LateLintPass for NewWithoutDefault {
118118
`Default` implementation for `{}`",
119119
self_ty),
120120
|db| {
121-
db.span_suggestion(span, "try this", "#[derive(Default)]".into());
122-
});
121+
db.suggest_item_with_attr(cx, span, "try this", "#[derive(Default)]");
122+
});
123123
} else {
124124
span_lint_and_then(cx,
125125
NEW_WITHOUT_DEFAULT, span,
126126
&format!("you should consider adding a \
127127
`Default` implementation for `{}`",
128128
self_ty),
129129
|db| {
130-
db.span_suggestion(span,
131-
"try this",
132-
format!("impl Default for {} {{ fn default() -> \
133-
Self {{ {}::new() }} }}", self_ty, self_ty));
134-
});
130+
db.suggest_prepend_item(cx,
131+
span,
132+
"try this",
133+
&format!(
134+
"impl Default for {} {{
135+
fn default() -> Self {{
136+
Self::new()
137+
}}
138+
}}",
139+
self_ty));
140+
});
135141
}
136142
}}
137143
}

clippy_lints/src/utils/sugg.rs

+89-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use rustc::hir;
2-
use rustc::lint::{EarlyContext, LateContext};
2+
use rustc::lint::{EarlyContext, LateContext, LintContext};
3+
use rustc_errors;
34
use std::borrow::Cow;
5+
use std::fmt::Display;
46
use std;
5-
use syntax::ast;
7+
use syntax::codemap::{CharPos, Span};
8+
use syntax::print::pprust::binop_to_string;
69
use syntax::util::parser::AssocOp;
10+
use syntax::ast;
711
use utils::{higher, snippet, snippet_opt};
8-
use syntax::print::pprust::binop_to_string;
912

1013
/// A helper type to build suggestion correctly handling parenthesis.
1114
pub enum Sugg<'a> {
@@ -20,7 +23,7 @@ pub enum Sugg<'a> {
2023
/// Literal constant `1`, for convenience.
2124
pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
2225

23-
impl<'a> std::fmt::Display for Sugg<'a> {
26+
impl<'a> Display for Sugg<'a> {
2427
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
2528
match *self {
2629
Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => {
@@ -126,7 +129,7 @@ impl<'a> Sugg<'a> {
126129
}
127130

128131
/// Convenience method to create the `<lhs> as <rhs>` suggestion.
129-
pub fn as_ty<R: std::fmt::Display>(self, rhs: R) -> Sugg<'static> {
132+
pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
130133
make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
131134
}
132135

@@ -198,7 +201,7 @@ impl<T> ParenHelper<T> {
198201
}
199202
}
200203

201-
impl<T: std::fmt::Display> std::fmt::Display for ParenHelper<T> {
204+
impl<T: Display> Display for ParenHelper<T> {
202205
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
203206
if self.paren {
204207
write!(f, "({})", self.wrapped)
@@ -354,3 +357,83 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
354357
And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
355358
})
356359
}
360+
361+
/// Return the indentation before `span` if there are nothing but `[ \t]` before it on its line.
362+
fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
363+
let lo = cx.sess().codemap().lookup_char_pos(span.lo);
364+
if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) {
365+
if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
366+
// we can mix char and byte positions here because we only consider `[ \t]`
367+
if lo.col == CharPos(pos) {
368+
Some(line[..pos].into())
369+
} else {
370+
None
371+
}
372+
} else {
373+
None
374+
}
375+
} else {
376+
None
377+
}
378+
}
379+
380+
pub trait DiagnosticBuilderExt<T: LintContext> {
381+
/// Suggests to add an attribute to an item.
382+
///
383+
/// Correctly handles indentation of the attribute and item.
384+
///
385+
/// # Example
386+
///
387+
/// ```rust
388+
/// db.suggest_item_with_attr(cx, item, "#[derive(Default)]");
389+
/// ```
390+
fn suggest_item_with_attr<D: Display+?Sized>(&mut self, cx: &T, item: Span, msg: &str, attr: &D);
391+
392+
/// Suggest to add an item before another.
393+
///
394+
/// The item should not be indented (expect for inner indentation).
395+
///
396+
/// # Example
397+
///
398+
/// ```rust
399+
/// db.suggest_prepend_item(cx, item,
400+
/// "fn foo() {
401+
/// bar();
402+
/// }");
403+
/// ```
404+
fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str);
405+
}
406+
407+
impl<'a, 'b, T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'b> {
408+
fn suggest_item_with_attr<D: Display+?Sized>(&mut self, cx: &T, item: Span, msg: &str, attr: &D) {
409+
if let Some(indent) = indentation(cx, item) {
410+
let span = Span {
411+
hi: item.lo,
412+
..item
413+
};
414+
415+
self.span_suggestion(span, msg, format!("{}\n{}", attr, indent));
416+
}
417+
}
418+
419+
fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str) {
420+
if let Some(indent) = indentation(cx, item) {
421+
let span = Span {
422+
hi: item.lo,
423+
..item
424+
};
425+
426+
let mut first = true;
427+
let new_item = new_item.lines().map(|l| {
428+
if first {
429+
first = false;
430+
format!("{}\n", l)
431+
} else {
432+
format!("{}{}\n", indent, l)
433+
}
434+
}).collect::<String>();
435+
436+
self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent));
437+
}
438+
}
439+
}

0 commit comments

Comments
 (0)