commit
1f7d7d76e7
@ -6,7 +6,7 @@ use std::collections::hash_map::Entry;
|
||||
use syntax::parse::token::InternedString;
|
||||
use syntax::util::small_vector::SmallVector;
|
||||
use utils::{SpanlessEq, SpanlessHash};
|
||||
use utils::{get_parent_expr, in_macro, span_note_and_lint};
|
||||
use utils::{get_parent_expr, in_macro, span_lint_and_then, span_note_and_lint, snippet};
|
||||
|
||||
/// **What it does:** This lint checks for consecutive `ifs` with the same condition. This lint is
|
||||
/// `Warn` by default.
|
||||
@ -42,7 +42,8 @@ declare_lint! {
|
||||
/// purpose, you can factor them
|
||||
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
/// **Known problems:** False positive possible with order dependent `match`
|
||||
/// (see issue [#860](https://github.com/Manishearth/rust-clippy/issues/860)).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
@ -52,6 +53,23 @@ declare_lint! {
|
||||
/// Baz => bar(), // <= oops
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This should probably be
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => baz(), // <= fixed
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// or if the original code was not a typo:
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar | Baz => bar(), // <= shows the intent better
|
||||
/// Quz => quz(),
|
||||
/// }
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub MATCH_SAME_ARMS,
|
||||
Warn,
|
||||
@ -143,12 +161,25 @@ fn lint_match_arms(cx: &LateContext, expr: &Expr) {
|
||||
|
||||
if let ExprMatch(_, ref arms, MatchSource::Normal) = expr.node {
|
||||
if let Some((i, j)) = search_same(arms, hash, eq) {
|
||||
span_note_and_lint(cx,
|
||||
span_lint_and_then(cx,
|
||||
MATCH_SAME_ARMS,
|
||||
j.body.span,
|
||||
"this `match` has identical arm bodies",
|
||||
i.body.span,
|
||||
"same as this");
|
||||
|db| {
|
||||
db.span_note(i.body.span, "same as this");
|
||||
|
||||
// Note: this does not use `span_suggestion` on purpose: there is no clean way to
|
||||
// remove the other arm. Building a span and suggest to replace it to "" makes an
|
||||
// even more confusing error message. Also in order not to make up a span for the
|
||||
// whole pattern, the suggestion is only shown when there is only one pattern. The
|
||||
// user should know about `|` if they are already using it…
|
||||
|
||||
if i.pats.len() == 1 && j.pats.len() == 1 {
|
||||
let lhs = snippet(cx, i.pats[0].span, "<pat1>");
|
||||
let rhs = snippet(cx, j.pats[0].span, "<pat2>");
|
||||
db.span_note(i.body.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use reexport::*;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, walk_block, walk_decl};
|
||||
use rustc::hir::map::Node::NodeBlock;
|
||||
use rustc::lint::*;
|
||||
@ -337,7 +338,7 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
|
||||
if let PatKind::Binding(_, ref ident, _) = pat.node {
|
||||
let mut visitor = VarVisitor {
|
||||
cx: cx,
|
||||
var: ident.node,
|
||||
var: cx.tcx.expect_def(pat.id).def_id(),
|
||||
indexed: HashMap::new(),
|
||||
nonindex: false,
|
||||
};
|
||||
@ -667,7 +668,7 @@ impl<'a> Visitor<'a> for UsedVisitor {
|
||||
|
||||
struct VarVisitor<'v, 't: 'v> {
|
||||
cx: &'v LateContext<'v, 't>, // context reference
|
||||
var: Name, // var name to look for as index
|
||||
var: DefId, // var name to look for as index
|
||||
indexed: HashMap<Name, Option<CodeExtent>>, // indexed variables, the extent is None for global
|
||||
nonindex: bool, // has the var been used otherwise?
|
||||
}
|
||||
@ -675,7 +676,7 @@ struct VarVisitor<'v, 't: 'v> {
|
||||
impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
if let ExprPath(None, ref path) = expr.node {
|
||||
if path.segments.len() == 1 && path.segments[0].name == self.var {
|
||||
if path.segments.len() == 1 && self.cx.tcx.expect_def(expr.id).def_id() == self.var {
|
||||
// we are referencing our variable! now check if it's as an index
|
||||
if_let_chain! {[
|
||||
let Some(parexpr) = get_parent_expr(self.cx, expr),
|
||||
|
@ -7,6 +7,7 @@ use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
use utils::paths;
|
||||
use utils::{get_trait_def_id, implements_trait, in_external_macro, return_ty, same_tys, span_lint_and_then};
|
||||
use utils::sugg::DiagnosticBuilderExt;
|
||||
|
||||
/// **What it does:** This lints about type with a `fn new() -> Self` method
|
||||
/// and no implementation of
|
||||
@ -14,8 +15,7 @@ use utils::{get_trait_def_id, implements_trait, in_external_macro, return_ty, sa
|
||||
///
|
||||
/// **Why is this bad?** User might expect to be able to use
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
|
||||
/// as the type can be
|
||||
/// constructed without arguments.
|
||||
/// as the type can be constructed without arguments.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
@ -118,8 +118,8 @@ impl LateLintPass for NewWithoutDefault {
|
||||
`Default` implementation for `{}`",
|
||||
self_ty),
|
||||
|db| {
|
||||
db.span_suggestion(span, "try this", "#[derive(Default)]".into());
|
||||
});
|
||||
db.suggest_item_with_attr(cx, span, "try this", "#[derive(Default)]");
|
||||
});
|
||||
} else {
|
||||
span_lint_and_then(cx,
|
||||
NEW_WITHOUT_DEFAULT, span,
|
||||
@ -127,11 +127,17 @@ impl LateLintPass for NewWithoutDefault {
|
||||
`Default` implementation for `{}`",
|
||||
self_ty),
|
||||
|db| {
|
||||
db.span_suggestion(span,
|
||||
"try this",
|
||||
format!("impl Default for {} {{ fn default() -> \
|
||||
Self {{ {}::new() }} }}", self_ty, self_ty));
|
||||
});
|
||||
db.suggest_prepend_item(cx,
|
||||
span,
|
||||
"try this",
|
||||
&format!(
|
||||
"impl Default for {} {{
|
||||
fn default() -> Self {{
|
||||
Self::new()
|
||||
}}
|
||||
}}",
|
||||
self_ty));
|
||||
});
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
use rustc::hir;
|
||||
use rustc::lint::{EarlyContext, LateContext};
|
||||
use rustc::lint::{EarlyContext, LateContext, LintContext};
|
||||
use rustc_errors;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Display;
|
||||
use std;
|
||||
use syntax::ast;
|
||||
use syntax::util::parser::AssocOp;
|
||||
use utils::{higher, snippet, snippet_opt};
|
||||
use syntax::codemap::{CharPos, Span};
|
||||
use syntax::print::pprust::binop_to_string;
|
||||
use syntax::util::parser::AssocOp;
|
||||
use syntax::ast;
|
||||
use utils::{higher, snippet, snippet_opt};
|
||||
|
||||
/// A helper type to build suggestion correctly handling parenthesis.
|
||||
pub enum Sugg<'a> {
|
||||
@ -20,7 +23,7 @@ pub enum Sugg<'a> {
|
||||
/// Literal constant `1`, for convenience.
|
||||
pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
|
||||
|
||||
impl<'a> std::fmt::Display for Sugg<'a> {
|
||||
impl<'a> Display for Sugg<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
match *self {
|
||||
Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => {
|
||||
@ -126,7 +129,7 @@ impl<'a> Sugg<'a> {
|
||||
}
|
||||
|
||||
/// Convenience method to create the `<lhs> as <rhs>` suggestion.
|
||||
pub fn as_ty<R: std::fmt::Display>(self, rhs: R) -> Sugg<'static> {
|
||||
pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
|
||||
make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
|
||||
}
|
||||
|
||||
@ -198,7 +201,7 @@ impl<T> ParenHelper<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Display> std::fmt::Display for ParenHelper<T> {
|
||||
impl<T: Display> Display for ParenHelper<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
if self.paren {
|
||||
write!(f, "({})", self.wrapped)
|
||||
@ -354,3 +357,83 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
|
||||
And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the indentation before `span` if there are nothing but `[ \t]` before it on its line.
|
||||
fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
||||
let lo = cx.sess().codemap().lookup_char_pos(span.lo);
|
||||
if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) {
|
||||
if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
|
||||
// we can mix char and byte positions here because we only consider `[ \t]`
|
||||
if lo.col == CharPos(pos) {
|
||||
Some(line[..pos].into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DiagnosticBuilderExt<T: LintContext> {
|
||||
/// Suggests to add an attribute to an item.
|
||||
///
|
||||
/// Correctly handles indentation of the attribute and item.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// db.suggest_item_with_attr(cx, item, "#[derive(Default)]");
|
||||
/// ```
|
||||
fn suggest_item_with_attr<D: Display+?Sized>(&mut self, cx: &T, item: Span, msg: &str, attr: &D);
|
||||
|
||||
/// Suggest to add an item before another.
|
||||
///
|
||||
/// The item should not be indented (expect for inner indentation).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// db.suggest_prepend_item(cx, item,
|
||||
/// "fn foo() {
|
||||
/// bar();
|
||||
/// }");
|
||||
/// ```
|
||||
fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str);
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'b> {
|
||||
fn suggest_item_with_attr<D: Display+?Sized>(&mut self, cx: &T, item: Span, msg: &str, attr: &D) {
|
||||
if let Some(indent) = indentation(cx, item) {
|
||||
let span = Span {
|
||||
hi: item.lo,
|
||||
..item
|
||||
};
|
||||
|
||||
self.span_suggestion(span, msg, format!("{}\n{}", attr, indent));
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str) {
|
||||
if let Some(indent) = indentation(cx, item) {
|
||||
let span = Span {
|
||||
hi: item.lo,
|
||||
..item
|
||||
};
|
||||
|
||||
let mut first = true;
|
||||
let new_item = new_item.lines().map(|l| {
|
||||
if first {
|
||||
first = false;
|
||||
format!("{}\n", l)
|
||||
} else {
|
||||
format!("{}{}\n", indent, l)
|
||||
}
|
||||
}).collect::<String>();
|
||||
|
||||
self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ struct Foo {
|
||||
#[deny(match_same_arms)]
|
||||
fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
if true {
|
||||
//~^NOTE same as this
|
||||
Foo { bar: 42 };
|
||||
0..10;
|
||||
..;
|
||||
@ -62,6 +63,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
}
|
||||
|
||||
let _ = if true {
|
||||
//~^NOTE same as this
|
||||
foo();
|
||||
42
|
||||
}
|
||||
@ -75,6 +77,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
}
|
||||
|
||||
let _ = if true {
|
||||
//~^NOTE same as this
|
||||
42
|
||||
}
|
||||
else { //~ERROR this `if` has identical blocks
|
||||
@ -82,6 +85,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
};
|
||||
|
||||
if true {
|
||||
//~^NOTE same as this
|
||||
let bar = if true {
|
||||
42
|
||||
}
|
||||
@ -105,6 +109,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
}
|
||||
|
||||
if true {
|
||||
//~^NOTE same as this
|
||||
let _ = match 42 {
|
||||
42 => 1,
|
||||
a if a > 0 => 2,
|
||||
@ -125,6 +130,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
}
|
||||
|
||||
if true {
|
||||
//~^NOTE same as this
|
||||
if let Some(a) = Some(42) {}
|
||||
}
|
||||
else { //~ERROR this `if` has identical blocks
|
||||
@ -132,6 +138,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
}
|
||||
|
||||
if true {
|
||||
//~^NOTE same as this
|
||||
if let (1, .., 3) = (1, 2, 3) {}
|
||||
}
|
||||
else { //~ERROR this `if` has identical blocks
|
||||
@ -168,12 +175,16 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
|
||||
let _ = match 42 {
|
||||
42 => foo(),
|
||||
//~^NOTE same as this
|
||||
//~|NOTE `42 | 51`
|
||||
51 => foo(), //~ERROR this `match` has identical arm bodies
|
||||
_ => true,
|
||||
};
|
||||
|
||||
let _ = match Some(42) {
|
||||
Some(_) => 24,
|
||||
//~^NOTE same as this
|
||||
//~|NOTE `Some(_) | None`
|
||||
None => 24, //~ERROR this `match` has identical arm bodies
|
||||
};
|
||||
|
||||
@ -196,18 +207,24 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
|
||||
match (Some(42), Some(42)) {
|
||||
(Some(a), None) => bar(a),
|
||||
//~^NOTE same as this
|
||||
//~|NOTE `(Some(a), None) | (None, Some(a))`
|
||||
(None, Some(a)) => bar(a), //~ERROR this `match` has identical arm bodies
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match (Some(42), Some(42)) {
|
||||
(Some(a), ..) => bar(a),
|
||||
//~^NOTE same as this
|
||||
//~|NOTE `(Some(a), ..) | (.., Some(a))`
|
||||
(.., Some(a)) => bar(a), //~ERROR this `match` has identical arm bodies
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match (1, 2, 3) {
|
||||
(1, .., 3) => 42,
|
||||
//~^NOTE same as this
|
||||
//~|NOTE `(1, .., 3) | (.., 3)`
|
||||
(.., 3) => 42, //~ERROR this `match` has identical arm bodies
|
||||
_ => 0,
|
||||
};
|
||||
@ -219,6 +236,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
}
|
||||
|
||||
if true {
|
||||
//~^NOTE same as this
|
||||
try!(Ok("foo"));
|
||||
}
|
||||
else { //~ERROR this `if` has identical blocks
|
||||
@ -226,6 +244,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
|
||||
}
|
||||
|
||||
if true {
|
||||
//~^NOTE same as this
|
||||
let foo = "";
|
||||
return Ok(&foo[0..]);
|
||||
}
|
||||
@ -246,16 +265,19 @@ fn ifs_same_cond() {
|
||||
let b = false;
|
||||
|
||||
if b {
|
||||
//~^NOTE same as this
|
||||
}
|
||||
else if b { //~ERROR this `if` has the same condition as a previous if
|
||||
}
|
||||
|
||||
if a == 1 {
|
||||
//~^NOTE same as this
|
||||
}
|
||||
else if a == 1 { //~ERROR this `if` has the same condition as a previous if
|
||||
}
|
||||
|
||||
if 2*a == 1 {
|
||||
//~^NOTE same as this
|
||||
}
|
||||
else if 2*a == 2 {
|
||||
}
|
||||
|
@ -104,6 +104,12 @@ fn main() {
|
||||
println!("{}", vec[i]);
|
||||
}
|
||||
|
||||
for i in 0..vec.len() {
|
||||
//~^ WARNING unused variable
|
||||
let i = 42; // make a different `i`
|
||||
println!("{}", vec[i]); // ok, not the `i` of the for-loop
|
||||
}
|
||||
|
||||
for i in 0..vec.len() { let _ = vec[i]; }
|
||||
//~^ ERROR `i` is only used to index `vec`
|
||||
//~| HELP consider
|
||||
|
@ -7,13 +7,21 @@
|
||||
pub struct Foo;
|
||||
|
||||
impl Foo {
|
||||
pub fn new() -> Foo { Foo } //~ERROR: you should consider deriving a `Default` implementation for `Foo`
|
||||
pub fn new() -> Foo { Foo }
|
||||
//~^ERROR: you should consider deriving a `Default` implementation for `Foo`
|
||||
//~|HELP try this
|
||||
//~^^^SUGGESTION #[derive(Default)]
|
||||
//~^^^SUGGESTION pub fn new
|
||||
}
|
||||
|
||||
pub struct Bar;
|
||||
|
||||
impl Bar {
|
||||
pub fn new() -> Self { Bar } //~ERROR: you should consider deriving a `Default` implementation for `Bar`
|
||||
pub fn new() -> Self { Bar }
|
||||
//~^ERROR: you should consider deriving a `Default` implementation for `Bar`
|
||||
//~|HELP try this
|
||||
//~^^^SUGGESTION #[derive(Default)]
|
||||
//~^^^SUGGESTION pub fn new
|
||||
}
|
||||
|
||||
pub struct Ok;
|
||||
@ -61,7 +69,15 @@ pub struct LtKo<'a> {
|
||||
}
|
||||
|
||||
impl<'c> LtKo<'c> {
|
||||
pub fn new() -> LtKo<'c> { unimplemented!() } //~ERROR: you should consider adding a `Default` implementation for
|
||||
pub fn new() -> LtKo<'c> { unimplemented!() }
|
||||
//~^ERROR: you should consider adding a `Default` implementation for
|
||||
//~^^HELP try
|
||||
//~^^^SUGGESTION impl Default for LtKo<'c> {
|
||||
//~^^^SUGGESTION fn default() -> Self {
|
||||
//~^^^SUGGESTION Self::new()
|
||||
//~^^^SUGGESTION }
|
||||
//~^^^SUGGESTION }
|
||||
// FIXME: that suggestion is missing lifetimes
|
||||
}
|
||||
|
||||
struct Private;
|
||||
|
Loading…
x
Reference in New Issue
Block a user