[almost_swapped](https://github.com/Manishearth/rust-clippy/wiki#almost_swapped) | warn | `foo = bar; bar = foo` sequence
[approx_constant](https://github.com/Manishearth/rust-clippy/wiki#approx_constant) | warn | the approximate of a known float constant (in `std::f64::consts` or `std::f32::consts`) is found; suggests to use the constant
[assign_op_pattern](https://github.com/Manishearth/rust-clippy/wiki#assign_op_pattern) | warn | assigning the result of an operation on a variable to that same variable
-[assign_ops](https://github.com/Manishearth/rust-clippy/wiki#assign_ops) | allow | Any assignment operation
+[assign_ops](https://github.com/Manishearth/rust-clippy/wiki#assign_ops) | allow | any assignment operation
[bad_bit_mask](https://github.com/Manishearth/rust-clippy/wiki#bad_bit_mask) | warn | expressions of the form `_ & mask == select` that will only ever return `true` or `false` (because in the example `select` containing bits that `mask` doesn't have)
[blacklisted_name](https://github.com/Manishearth/rust-clippy/wiki#blacklisted_name) | warn | usage of a blacklisted/placeholder name
[block_in_if_condition_expr](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_expr) | warn | braces can be eliminated in conditions that are expressions, e.g `if { true } ...`
use rustc_const_math::ConstInt;
use rustc::hir::*;
use syntax::ast::RangeLimits;
-use utils;
+use utils::{self, higher};
/// **What it does:** Check for out of bounds array indexing with a constant index.
///
}
// Index is a constant range
- if let Some(range) = utils::unsugar_range(index) {
+ if let Some(range) = higher::range(index) {
let start = range.start
.map(|start| eval_const_expr_partial(cx.tcx, start, ExprTypeChecked, None))
.map(|v| v.ok());
}
}
- if let Some(range) = utils::unsugar_range(index) {
+ if let Some(range) = higher::range(index) {
// Full ranges are always valid
if range.start.is_none() && range.end.is_none() {
return;
use rustc::hir;
use rustc::lint::*;
use utils::{span_lint_and_then, span_lint, snippet_opt, SpanlessEq, get_trait_def_id, implements_trait};
+use utils::{higher, sugg};
-/// **What it does:** This lint checks for `+=` operations and similar
+/// **What it does:** This lint checks for `+=` operations and similar.
///
-/// **Why is this bad?** Projects with many developers from languages without those operations
-/// may find them unreadable and not worth their weight
+/// **Why is this bad?** Projects with many developers from languages without those operations may
+/// find them unreadable and not worth their weight.
///
-/// **Known problems:** Types implementing `OpAssign` don't necessarily implement `Op`
+/// **Known problems:** Types implementing `OpAssign` don't necessarily implement `Op`.
///
/// **Example:**
/// ```
/// ```
declare_restriction_lint! {
pub ASSIGN_OPS,
- "Any assignment operation"
+ "any assignment operation"
}
-/// **What it does:** Check for `a = a op b` or `a = b commutative_op a` patterns
+/// **What it does:** Check for `a = a op b` or `a = b commutative_op a` patterns.
///
-/// **Why is this bad?** These can be written as the shorter `a op= b`
+/// **Why is this bad?** These can be written as the shorter `a op= b`.
///
-/// **Known problems:** While forbidden by the spec, `OpAssign` traits may have implementations that differ from the regular `Op` impl
+/// **Known problems:** While forbidden by the spec, `OpAssign` traits may have implementations that differ from the regular `Op` impl.
///
/// **Example:**
///
fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
match expr.node {
hir::ExprAssignOp(op, ref lhs, ref rhs) => {
- if let (Some(l), Some(r)) = (snippet_opt(cx, lhs.span), snippet_opt(cx, rhs.span)) {
- span_lint_and_then(cx, ASSIGN_OPS, expr.span, "assign operation detected", |db| {
- match rhs.node {
- hir::ExprBinary(op2, _, _) if op2 != op => {
- db.span_suggestion(expr.span,
- "replace it with",
- format!("{} = {} {} ({})", l, l, op.node.as_str(), r));
- }
- _ => {
- db.span_suggestion(expr.span,
- "replace it with",
- format!("{} = {} {} {}", l, l, op.node.as_str(), r));
- }
- }
- });
- } else {
- span_lint(cx, ASSIGN_OPS, expr.span, "assign operation detected");
- }
+ span_lint_and_then(cx, ASSIGN_OPS, expr.span, "assign operation detected", |db| {
+ let lhs = &sugg::Sugg::hir(cx, lhs, "..");
+ let rhs = &sugg::Sugg::hir(cx, rhs, "..");
+
+ db.span_suggestion(expr.span,
+ "replace it with",
+ format!("{} = {}", lhs, sugg::make_binop(higher::binop(op.node), lhs, rhs)));
+ });
}
hir::ExprAssign(ref assignee, ref e) => {
if let hir::ExprBinary(op, ref l, ref r) = e.node {
//! This lint is **warn** by default
use rustc::lint::*;
-use std::borrow::Cow;
-use syntax::codemap::Spanned;
use syntax::ast;
-use utils::{in_macro, snippet, snippet_block, span_lint_and_then};
+use utils::{in_macro, snippet_block, span_lint_and_then};
+use utils::sugg::Sugg;
/// **What it does:** This lint checks for nested `if`-statements which can be collapsed by
/// `&&`-combining their conditions and for `else { if .. }` expressions that can be collapsed to
return;
}
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this if statement can be collapsed", |db| {
+ let lhs = Sugg::ast(cx, check, "..");
+ let rhs = Sugg::ast(cx, check_inner, "..");
db.span_suggestion(expr.span,
"try",
- format!("if {} && {} {}",
- check_to_string(cx, check),
- check_to_string(cx, check_inner),
+ format!("if {} {}",
+ lhs.and(rhs),
snippet_block(cx, content.span, "..")));
});
}}
}
-fn requires_brackets(e: &ast::Expr) -> bool {
- match e.node {
- ast::ExprKind::Binary(Spanned { node: n, .. }, _, _) if n == ast::BinOpKind::Eq => false,
- _ => true,
- }
-}
-
-fn check_to_string(cx: &EarlyContext, e: &ast::Expr) -> Cow<'static, str> {
- if requires_brackets(e) {
- format!("({})", snippet(cx, e.span, "..")).into()
- } else {
- snippet(cx, e.span, "..")
- }
-}
-
/// If the block contains only one expression, returns it.
fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
let mut it = block.stmts.iter();
SpanlessEq::new(self.cx).eq_expr(self.key, ¶ms[1])
], {
span_lint_and_then(self.cx, MAP_ENTRY, self.span,
- &format!("usage of `contains_key` followed by `insert` on `{}`", self.ty), |db| {
+ &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |db| {
if self.sole_expr {
let help = format!("{}.entry({}).or_insert({})",
snippet(self.cx, self.map.span, "map"),
snippet(self.cx, params[1].span, ".."),
snippet(self.cx, params[2].span, ".."));
- db.span_suggestion(self.span, "Consider using", help);
+ db.span_suggestion(self.span, "consider using", help);
}
else {
- let help = format!("Consider using `{}.entry({})`",
+ let help = format!("{}.entry({})",
snippet(self.cx, self.map.span, "map"),
snippet(self.cx, params[1].span, ".."));
- db.span_note(self.span, &help);
+ db.span_suggestion(self.span, "consider using", help);
}
});
}}
}
}
-/// Match `if` or `else if` expressions and return the `then` and `else` block.
+/// Match `if` or `if let` expressions and return the `then` and `else` block.
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
match expr.node {
ast::ExprKind::If(_, ref then, ref else_) |
#![feature(box_syntax)]
#![feature(collections)]
#![feature(custom_attribute)]
+#![feature(dotdot_in_tuple_patterns)]
#![feature(iter_arith)]
#![feature(question_mark)]
#![feature(rustc_private)]
extern crate rustc_serialize;
+extern crate rustc_errors;
extern crate rustc_plugin;
extern crate rustc_const_eval;
extern crate rustc_const_math;
use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::eval_const_expr_partial;
-use std::borrow::Cow;
use std::collections::HashMap;
use syntax::ast;
+use utils::sugg;
-use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, in_external_macro,
- span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, unsugar_range,
- walk_ptrs_ty, recover_for_loop};
+use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, multispan_sugg, in_external_macro,
+ span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, higher,
+ walk_ptrs_ty};
use utils::paths;
-use utils::UnsugaredRange;
/// **What it does:** This lint checks for looping over the range of `0..len` of some collection just to get the values by index.
///
impl LateLintPass for Pass {
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
- if let Some((pat, arg, body)) = recover_for_loop(expr) {
+ if let Some((pat, arg, body)) = higher::for_loop(expr) {
check_for_loop(cx, pat, arg, body, expr);
}
// check for `loop { if let {} else break }` that could be `while let`
/// Check for looping over a range and then indexing a sequence with it.
/// The iteratee must be a range literal.
fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
- if let Some(UnsugaredRange { start: Some(ref start), ref end, .. }) = unsugar_range(arg) {
+ if let Some(higher::Range { start: Some(ref start), ref end, limits }) = higher::range(arg) {
// the var must be a single name
if let PatKind::Binding(_, ref ident, _) = pat.node {
let mut visitor = VarVisitor {
let starts_at_zero = is_integer_literal(start, 0);
- let skip: Cow<_> = if starts_at_zero {
- "".into()
+ let skip = if starts_at_zero {
+ "".to_owned()
} else {
- format!(".skip({})", snippet(cx, start.span, "..")).into()
+ format!(".skip({})", snippet(cx, start.span, ".."))
};
- let take: Cow<_> = if let Some(ref end) = *end {
+ let take = if let Some(ref end) = *end {
if is_len_call(end, &indexed) {
- "".into()
+ "".to_owned()
} else {
- format!(".take({})", snippet(cx, end.span, "..")).into()
+ match limits {
+ ast::RangeLimits::Closed => {
+ let end = sugg::Sugg::hir(cx, end, "<count>");
+ format!(".take({})", end + sugg::ONE)
+ }
+ ast::RangeLimits::HalfOpen => {
+ format!(".take({})", snippet(cx, end.span, ".."))
+ }
+ }
}
} else {
- "".into()
+ "".to_owned()
};
if visitor.nonindex {
- span_lint(cx,
- NEEDLESS_RANGE_LOOP,
- expr.span,
- &format!("the loop variable `{}` is used to index `{}`. Consider using `for ({}, \
- item) in {}.iter().enumerate(){}{}` or similar iterators",
- ident.node,
- indexed,
- ident.node,
- indexed,
- take,
- skip));
+ span_lint_and_then(cx,
+ NEEDLESS_RANGE_LOOP,
+ expr.span,
+ &format!("the loop variable `{}` is used to index `{}`", ident.node, indexed),
+ |db| {
+ multispan_sugg(db, "consider using an iterator".to_string(), &[
+ (pat.span, &format!("({}, <item>)", ident.node)),
+ (arg.span, &format!("{}.iter().enumerate(){}{}", indexed, take, skip)),
+ ]);
+ });
} else {
let repl = if starts_at_zero && take.is_empty() {
format!("&{}", indexed)
format!("{}.iter(){}{}", indexed, take, skip)
};
- span_lint(cx,
- NEEDLESS_RANGE_LOOP,
- expr.span,
- &format!("the loop variable `{}` is only used to index `{}`. \
- Consider using `for item in {}` or similar iterators",
- ident.node,
- indexed,
- repl));
+ span_lint_and_then(cx,
+ NEEDLESS_RANGE_LOOP,
+ expr.span,
+ &format!("the loop variable `{}` is only used to index `{}`.", ident.node, indexed),
+ |db| {
+ multispan_sugg(db, "consider using an iterator".to_string(), &[
+ (pat.span, "<item>"),
+ (arg.span, &repl),
+ ]);
+ });
}
}
}
fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
// if this for loop is iterating over a two-sided range...
- if let Some(UnsugaredRange { start: Some(ref start), end: Some(ref end), limits }) = unsugar_range(arg) {
+ if let Some(higher::Range { start: Some(ref start), end: Some(ref end), limits }) = higher::range(arg) {
// ...and both sides are compile-time constant integers...
if let Ok(start_idx) = eval_const_expr_partial(cx.tcx, start, ExprTypeChecked, None) {
if let Ok(end_idx) = eval_const_expr_partial(cx.tcx, end, ExprTypeChecked, None) {
/// Check for the `FOR_KV_MAP` lint.
fn check_for_loop_over_map_kv(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
+ let pat_span = pat.span;
+
if let PatKind::Tuple(ref pat, _) = pat.node {
if pat.len() == 2 {
- let (pat_span, kind) = match (&pat[0].node, &pat[1].node) {
- (key, _) if pat_is_wild(key, body) => (&pat[1].span, "values"),
- (_, value) if pat_is_wild(value, body) => (&pat[0].span, "keys"),
+ let (new_pat_span, kind) = match (&pat[0].node, &pat[1].node) {
+ (key, _) if pat_is_wild(key, body) => (pat[1].span, "value"),
+ (_, value) if pat_is_wild(value, body) => (pat[0].span, "key"),
_ => return,
};
- let arg_span = match arg.node {
- ExprAddrOf(MutImmutable, ref expr) => expr.span,
+ let (arg_span, arg) = match arg.node {
+ ExprAddrOf(MutImmutable, ref expr) => (arg.span, &**expr),
ExprAddrOf(MutMutable, _) => return, // for _ in &mut _, there is no {values,keys}_mut method
- _ => arg.span,
+ _ => (arg.span, arg),
};
let ty = walk_ptrs_ty(cx.tcx.expr_ty(arg));
span_lint_and_then(cx,
FOR_KV_MAP,
expr.span,
- &format!("you seem to want to iterate on a map's {}", kind),
+ &format!("you seem to want to iterate on a map's {}s", kind),
|db| {
- db.span_suggestion(expr.span,
- "use the corresponding method",
- format!("for {} in {}.{}() {{ .. }}",
- snippet(cx, *pat_span, ".."),
- snippet(cx, arg_span, ".."),
- kind));
+ let map = sugg::Sugg::hir(cx, arg, "map");
+ multispan_sugg(db, "use the corresponding method".into(), &[
+ (pat_span, &snippet(cx, new_pat_span, kind)),
+ (arg_span, &format!("{}.{}s()", map.maybe_par(), kind)),
+ ]);
});
}
}
use syntax::codemap::Span;
use utils::paths;
use utils::{match_type, snippet, span_note_and_lint, span_lint_and_then, in_external_macro, expr_block};
+use utils::sugg::Sugg;
/// **What it does:** This lint checks for matches with a single arm where an `if let` will usually suffice.
///
fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
// type of expression == bool
if cx.tcx.expr_ty(ex).sty == ty::TyBool {
- let sugg = if arms.len() == 2 && arms[0].pats.len() == 1 {
- // no guards
- let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pats[0].node {
- if let ExprLit(ref lit) = arm_bool.node {
- match lit.node {
- LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
- LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
- _ => None,
+ span_lint_and_then(cx,
+ MATCH_BOOL,
+ expr.span,
+ "you seem to be trying to match on a boolean expression",
+ move |db| {
+ if arms.len() == 2 && arms[0].pats.len() == 1 {
+ // no guards
+ let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pats[0].node {
+ if let ExprLit(ref lit) = arm_bool.node {
+ match lit.node {
+ LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
+ LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
+ _ => None,
+ }
+ } else {
+ None
}
} else {
None
- }
- } else {
- None
- };
-
- if let Some((ref true_expr, ref false_expr)) = exprs {
- match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
- (false, false) => {
- Some(format!("if {} {} else {}",
- snippet(cx, ex.span, "b"),
- expr_block(cx, true_expr, None, ".."),
- expr_block(cx, false_expr, None, "..")))
+ };
+
+ if let Some((ref true_expr, ref false_expr)) = exprs {
+ let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
+ (false, false) => {
+ Some(format!("if {} {} else {}",
+ snippet(cx, ex.span, "b"),
+ expr_block(cx, true_expr, None, ".."),
+ expr_block(cx, false_expr, None, "..")))
+ }
+ (false, true) => {
+ Some(format!("if {} {}", snippet(cx, ex.span, "b"), expr_block(cx, true_expr, None, "..")))
+ }
+ (true, false) => {
+ let test = Sugg::hir(cx, ex, "..");
+ Some(format!("if {} {}",
+ !test,
+ expr_block(cx, false_expr, None, "..")))
+ }
+ (true, true) => None,
+ };
+
+ if let Some(sugg) = sugg {
+ db.span_suggestion(expr.span, "consider using an if/else expression", sugg);
}
- (false, true) => {
- Some(format!("if {} {}", snippet(cx, ex.span, "b"), expr_block(cx, true_expr, None, "..")))
- }
- (true, false) => {
- Some(format!("try\nif !{} {}",
- snippet(cx, ex.span, "b"),
- expr_block(cx, false_expr, None, "..")))
- }
- (true, true) => None,
}
- } else {
- None
}
- } else {
- None
- };
- span_lint_and_then(cx,
- MATCH_BOOL,
- expr.span,
- "you seem to be trying to match on a boolean expression. Consider using an if..else block:",
- move |db| {
- if let Some(sugg) = sugg {
- db.span_suggestion(expr.span, "try this", sugg);
- }
- });
+ });
}
}
fn check_match_ref_pats(cx: &LateContext, ex: &Expr, arms: &[Arm], source: MatchSource, expr: &Expr) {
if has_only_ref_pats(arms) {
if let ExprAddrOf(Mutability::MutImmutable, ref inner) = ex.node {
- let template = match_template(cx, expr.span, source, "", inner);
span_lint_and_then(cx,
MATCH_REF_PATS,
expr.span,
"you don't need to add `&` to both the expression and the patterns",
|db| {
- db.span_suggestion(expr.span, "try", template);
- });
+ let inner = Sugg::hir(cx, inner, "..");
+ let template = match_template(expr.span, source, inner);
+ db.span_suggestion(expr.span, "try", template);
+ });
} else {
- let template = match_template(cx, expr.span, source, "*", ex);
span_lint_and_then(cx,
MATCH_REF_PATS,
expr.span,
"you don't need to add `&` to all patterns",
|db| {
- db.span_suggestion(expr.span,
- "instead of prefixing all patterns with `&`, you can \
- dereference the expression",
- template);
- });
+ let ex = Sugg::hir(cx, ex, "..");
+ let template = match_template(expr.span, source, ex.deref());
+ db.span_suggestion(expr.span,
+ "instead of prefixing all patterns with `&`, you can \
+ dereference the expression",
+ template);
+ });
}
}
}
mapped.map_or(false, |v| v.iter().any(|el| *el))
}
-fn match_template(cx: &LateContext, span: Span, source: MatchSource, op: &str, expr: &Expr) -> String {
- let expr_snippet = snippet(cx, expr.span, "..");
+fn match_template(span: Span, source: MatchSource, expr: Sugg) -> String {
match source {
- MatchSource::Normal => format!("match {}{} {{ .. }}", op, expr_snippet),
- MatchSource::IfLetDesugar { .. } => format!("if let .. = {}{} {{ .. }}", op, expr_snippet),
- MatchSource::WhileLetDesugar => format!("while let .. = {}{} {{ .. }}", op, expr_snippet),
+ MatchSource::Normal => format!("match {} {{ .. }}", expr),
+ MatchSource::IfLetDesugar { .. } => format!("if let .. = {} {{ .. }}", expr),
+ MatchSource::WhileLetDesugar => format!("while let .. = {} {{ .. }}", expr),
MatchSource::ForLoopDesugar => span_bug!(span, "for loop desugared to match with &-patterns!"),
MatchSource::TryDesugar => span_bug!(span, "`?` operator desugared to match with &-patterns!"),
}
#[allow(ptr_arg)]
// Type of MethodArgs is potentially a Vec
fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &MethodArgs, is_mut: bool){
- let caller_type;
let mut_str = if is_mut { "_mut" } else {""};
- if let Some(_) = derefs_to_slice(cx, &iter_args[0], &cx.tcx.expr_ty(&iter_args[0])) {
- caller_type = "slice";
+ let caller_type = if let Some(_) = derefs_to_slice(cx, &iter_args[0], &cx.tcx.expr_ty(&iter_args[0])) {
+ "slice"
}
else if match_type(cx, cx.tcx.expr_ty(&iter_args[0]), &paths::VEC) {
- caller_type = "Vec";
+ "Vec"
}
else if match_type(cx, cx.tcx.expr_ty(&iter_args[0]), &paths::VEC_DEQUE) {
- caller_type = "VecDeque";
+ "VecDeque"
}
else {
return; // caller is not a type that we want to lint
- }
+ };
+
span_lint(
cx,
ITER_NTH,
get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path,
snippet, span_lint, span_lint_and_then, walk_ptrs_ty
};
+use utils::sugg::Sugg;
/// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`.
///
span_lint_and_then(cx,
TOPLEVEL_REF_ARG,
l.pat.span,
- "`ref` on an entire `let` pattern is discouraged, take a reference with & instead",
+ "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
|db| {
+ let init = Sugg::hir(cx, init, "..");
db.span_suggestion(s.span,
"try",
- format!("let {}{} = &{};",
+ format!("let {}{} = {};",
snippet(cx, i.span, "_"),
tyopt,
- snippet(cx, init.span, "_")));
+ init.addr()));
}
);
}}
return;
}
}
- span_lint(cx,
- FLOAT_CMP,
- expr.span,
- &format!("{}-comparison of f32 or f64 detected. Consider changing this to `({} - {}).abs() < \
- epsilon` for some suitable value of epsilon. \
- std::f32::EPSILON and std::f64::EPSILON are available.",
- op.as_str(),
- snippet(cx, left.span, ".."),
- snippet(cx, right.span, "..")));
+ span_lint_and_then(cx,
+ FLOAT_CMP,
+ expr.span,
+ "strict comparison of f32 or f64",
+ |db| {
+ let lhs = Sugg::hir(cx, left, "..");
+ let rhs = Sugg::hir(cx, right, "..");
+
+ db.span_suggestion(expr.span,
+ "consider comparing them within some error",
+ format!("({}).abs() < error", lhs - rhs));
+ db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
+ });
}
}
}
"Try not to call a closure in the expression where it is declared.",
|db| {
if decl.inputs.is_empty() {
- let hint = format!("{}", snippet(cx, block.span, ".."));
+ let hint = snippet(cx, block.span, "..").into_owned();
db.span_suggestion(expr.span, "Try doing something like: ", hint);
}
});
use rustc::hir::intravisit;
use rustc::lint::*;
use rustc::ty::{TypeAndMut, TyRef};
-use utils::{in_external_macro, recover_for_loop, span_lint};
+use utils::{higher, in_external_macro, span_lint};
/// **What it does:** This lint checks for instances of `mut mut` references.
///
return;
}
- if let Some((_, arg, body)) = recover_for_loop(expr) {
+ if let Some((_, arg, body)) = higher::for_loop(expr) {
// A `for` loop lowers to:
// ```rust
// match ::std::iter::Iterator::next(&mut iter) {
use rustc::lint::*;
use rustc::hir::*;
use syntax::codemap::Spanned;
-use utils::{is_integer_literal, match_type, paths, snippet, span_lint, unsugar_range, UnsugaredRange};
+use utils::{is_integer_literal, match_type, paths, snippet, span_lint};
+use utils::higher;
/// **What it does:** This lint checks for iterating over ranges with a `.step_by(0)`, which never terminates.
///
let ExprMethodCall( Spanned { node: ref iter_name, .. }, _, ref iter_args ) = *iter,
iter_name.as_str() == "iter",
// range expression in .zip() call: 0..x.len()
- let Some(UnsugaredRange { start: Some(ref start), end: Some(ref end), .. }) = unsugar_range(zip_arg),
+ let Some(higher::Range { start: Some(ref start), end: Some(ref end), .. }) = higher::range(zip_arg),
is_integer_literal(start, 0),
// .len() call
let ExprMethodCall(Spanned { node: ref len_name, .. }, _, ref len_args) = end.node,
use rustc::hir::intravisit::{Visitor, FnKind};
use std::ops::Deref;
use syntax::codemap::Span;
-use utils::{is_from_for_desugar, in_external_macro, snippet, span_lint_and_then};
+use utils::{higher, in_external_macro, snippet, span_lint_and_then};
/// **What it does:** This lint checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.
///
if in_external_macro(cx, decl.span) {
return;
}
- if is_from_for_desugar(decl) {
+ if higher::is_from_for_desugar(decl) {
return;
}
if let DeclLocal(ref local) = decl.node {
use rustc::lint::*;
use rustc::ty;
use syntax::codemap::mk_sp;
-use utils::{differing_macro_contexts, match_type, paths, snippet, snippet_opt, span_lint_and_then, walk_ptrs_ty, SpanlessEq};
+use utils::{differing_macro_contexts, match_type, paths, snippet, span_lint_and_then, walk_ptrs_ty, SpanlessEq};
+use utils::sugg::Sugg;
/// **What it does:** This lints manual swapping.
///
}
let (replace, what, sugg) = if let Some((slice, idx1, idx2)) = check_for_slice(cx, lhs1, lhs2) {
- if let Some(slice) = snippet_opt(cx, slice.span) {
+ if let Some(slice) = Sugg::hir_opt(cx, slice) {
(false,
format!(" elements of `{}`", slice),
- format!("{}.swap({}, {})",slice, snippet(cx, idx1.span, ".."), snippet(cx, idx2.span, "..")))
+ format!("{}.swap({}, {})", slice.maybe_par(), snippet(cx, idx1.span, ".."), snippet(cx, idx2.span, "..")))
} else {
(false, "".to_owned(), "".to_owned())
}
} else {
- if let (Some(first), Some(second)) = (snippet_opt(cx, lhs1.span), snippet_opt(cx, rhs1.span)) {
+ if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) {
(true, format!(" `{}` and `{}`", first, second),
- format!("std::mem::swap(&mut {}, &mut {})", first, second))
+ format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()))
} else {
(true, "".to_owned(), "".to_owned())
}
SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1),
SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0)
], {
- let (what, lhs, rhs) = if let (Some(first), Some(second)) = (snippet_opt(cx, lhs0.span), snippet_opt(cx, rhs0.span)) {
- (format!(" `{}` and `{}`", first, second), first, second)
+ let (what, lhs, rhs) = if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs0), Sugg::hir_opt(cx, rhs0)) {
+ (format!(" `{}` and `{}`", first, second), first.mut_addr().to_string(), second.mut_addr().to_string())
} else {
("".to_owned(), "".to_owned(), "".to_owned())
};
|db| {
if !what.is_empty() {
db.span_suggestion(span, "try",
- format!("std::mem::swap(&mut {}, &mut {})", lhs, rhs));
+ format!("std::mem::swap({}, {})", lhs, rhs));
db.note("or maybe you should use `std::mem::replace`?");
}
});
use rustc::ty;
use rustc::hir::*;
use utils::{match_def_path, paths, snippet_opt, span_lint, span_lint_and_then};
+use utils::sugg;
/// **What it does:** This lint checks for transmutes that can't ever be correct on any architecture
///
from_ty,
to_ty),
|db| {
- if let Some(arg) = snippet_opt(cx, args[0].span) {
- let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
- ("&mut *", "*mut")
- } else {
- ("&*", "*const")
- };
+ let arg = sugg::Sugg::hir(cx, &args[0], "..");
+ let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
+ ("&mut *", "*mut")
+ } else {
+ ("&*", "*const")
+ };
- let sugg = if from_pty.ty == to_rty.ty {
- // Put things in parentheses if they are more complex
- match args[0].node {
- ExprPath(..) | ExprCall(..) | ExprMethodCall(..) | ExprBlock(..) => {
- format!("{}{}", deref, arg)
- }
- _ => format!("{}({})", deref, arg)
- }
- } else {
- format!("{}({} as {} {})", deref, arg, cast, to_rty.ty)
- };
+ let sugg = if from_pty.ty == to_rty.ty {
+ sugg::make_unop(deref, arg).to_string()
+ } else {
+ format!("{}({} as {} {})", deref, arg, cast, to_rty.ty)
+ };
- db.span_suggestion(e.span, "try", sugg);
- }
+ db.span_suggestion(e.span, "try", sugg);
},
),
_ => return,
use std::cmp::Ordering;
use syntax::ast::{IntTy, UintTy, FloatTy};
use syntax::codemap::Span;
-use utils::{comparisons, in_external_macro, in_macro, is_from_for_desugar, match_def_path, snippet,
+use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet,
span_help_and_lint, span_lint};
use utils::paths;
if in_external_macro(cx, decl.span) || in_macro(cx, local.pat.span) {
return;
}
- if is_from_for_desugar(decl) {
+ if higher::is_from_for_desugar(decl) {
return;
}
span_lint(cx,
--- /dev/null
+//! This module contains functions for retrieve the original AST from lowered `hir`.
+
+use rustc::hir;
+use rustc::lint::LateContext;
+use syntax::ast;
+use syntax::ptr::P;
+use utils::{is_expn_of, match_path, paths};
+
+/// Convert a hir binary operator to the corresponding `ast` type.
+pub fn binop(op: hir::BinOp_) -> ast::BinOpKind {
+ match op {
+ hir::BiEq => ast::BinOpKind::Eq,
+ hir::BiGe => ast::BinOpKind::Ge,
+ hir::BiGt => ast::BinOpKind::Gt,
+ hir::BiLe => ast::BinOpKind::Le,
+ hir::BiLt => ast::BinOpKind::Lt,
+ hir::BiNe => ast::BinOpKind::Ne,
+ hir::BiOr => ast::BinOpKind::Or,
+ hir::BiAdd => ast::BinOpKind::Add,
+ hir::BiAnd => ast::BinOpKind::And,
+ hir::BiBitAnd => ast::BinOpKind::BitAnd,
+ hir::BiBitOr => ast::BinOpKind::BitOr,
+ hir::BiBitXor => ast::BinOpKind::BitXor,
+ hir::BiDiv => ast::BinOpKind::Div,
+ hir::BiMul => ast::BinOpKind::Mul,
+ hir::BiRem => ast::BinOpKind::Rem,
+ hir::BiShl => ast::BinOpKind::Shl,
+ hir::BiShr => ast::BinOpKind::Shr,
+ hir::BiSub => ast::BinOpKind::Sub,
+ }
+}
+
+/// Represent a range akin to `ast::ExprKind::Range`.
+#[derive(Debug, Copy, Clone)]
+pub struct Range<'a> {
+ pub start: Option<&'a hir::Expr>,
+ pub end: Option<&'a hir::Expr>,
+ pub limits: ast::RangeLimits,
+}
+
+/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
+pub fn range(expr: &hir::Expr) -> Option<Range> {
+ // To be removed when ranges get stable.
+ fn unwrap_unstable(expr: &hir::Expr) -> &hir::Expr {
+ if let hir::ExprBlock(ref block) = expr.node {
+ if block.rules == hir::BlockCheckMode::PushUnstableBlock || block.rules == hir::BlockCheckMode::PopUnstableBlock {
+ if let Some(ref expr) = block.expr {
+ return expr;
+ }
+ }
+ }
+
+ expr
+ }
+
+ fn get_field<'a>(name: &str, fields: &'a [hir::Field]) -> Option<&'a hir::Expr> {
+ let expr = &fields.iter()
+ .find(|field| field.name.node.as_str() == name)
+ .unwrap_or_else(|| panic!("missing {} field for range", name))
+ .expr;
+
+ Some(unwrap_unstable(expr))
+ }
+
+ // The range syntax is expanded to literal paths starting with `core` or `std` depending on
+ // `#[no_std]`. Testing both instead of resolving the paths.
+
+ match unwrap_unstable(expr).node {
+ hir::ExprPath(None, ref path) => {
+ if match_path(path, &paths::RANGE_FULL_STD) || match_path(path, &paths::RANGE_FULL) {
+ Some(Range {
+ start: None,
+ end: None,
+ limits: ast::RangeLimits::HalfOpen,
+ })
+ } else {
+ None
+ }
+ }
+ hir::ExprStruct(ref path, ref fields, None) => {
+ if match_path(path, &paths::RANGE_FROM_STD) || match_path(path, &paths::RANGE_FROM) {
+ Some(Range {
+ start: get_field("start", fields),
+ end: None,
+ limits: ast::RangeLimits::HalfOpen,
+ })
+ } else if match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY_STD) ||
+ match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY) {
+ Some(Range {
+ start: get_field("start", fields),
+ end: get_field("end", fields),
+ limits: ast::RangeLimits::Closed,
+ })
+ } else if match_path(path, &paths::RANGE_STD) || match_path(path, &paths::RANGE) {
+ Some(Range {
+ start: get_field("start", fields),
+ end: get_field("end", fields),
+ limits: ast::RangeLimits::HalfOpen,
+ })
+ } else if match_path(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_path(path, &paths::RANGE_TO_INCLUSIVE) {
+ Some(Range {
+ start: None,
+ end: get_field("end", fields),
+ limits: ast::RangeLimits::Closed,
+ })
+ } else if match_path(path, &paths::RANGE_TO_STD) || match_path(path, &paths::RANGE_TO) {
+ Some(Range {
+ start: None,
+ end: get_field("end", fields),
+ limits: ast::RangeLimits::HalfOpen,
+ })
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+}
+
+/// Checks if a `let` decl is from a `for` loop desugaring.
+pub fn is_from_for_desugar(decl: &hir::Decl) -> bool {
+ if_let_chain! {[
+ let hir::DeclLocal(ref loc) = decl.node,
+ let Some(ref expr) = loc.init,
+ let hir::ExprMatch(_, _, hir::MatchSource::ForLoopDesugar) = expr.node,
+ ], {
+ return true;
+ }}
+ false
+}
+
+/// Recover the essential nodes of a desugared for loop:
+/// `for pat in arg { body }` becomes `(pat, arg, body)`.
+pub fn for_loop(expr: &hir::Expr) -> Option<(&hir::Pat, &hir::Expr, &hir::Expr)> {
+ if_let_chain! {[
+ let hir::ExprMatch(ref iterexpr, ref arms, _) = expr.node,
+ let hir::ExprCall(_, ref iterargs) = iterexpr.node,
+ iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
+ let hir::ExprLoop(ref block, _) = arms[0].body.node,
+ block.stmts.is_empty(),
+ let Some(ref loopexpr) = block.expr,
+ let hir::ExprMatch(_, ref innerarms, hir::MatchSource::ForLoopDesugar) = loopexpr.node,
+ innerarms.len() == 2 && innerarms[0].pats.len() == 1,
+ let hir::PatKind::TupleStruct(_, ref somepats, _) = innerarms[0].pats[0].node,
+ somepats.len() == 1
+ ], {
+ return Some((&somepats[0],
+ &iterargs[0],
+ &innerarms[0].body));
+ }}
+ None
+}
+
+/// Represent the pre-expansion arguments of a `vec!` invocation.
+pub enum VecArgs<'a> {
+ /// `vec![elem; len]`
+ Repeat(&'a P<hir::Expr>, &'a P<hir::Expr>),
+ /// `vec![a, b, c]`
+ Vec(&'a [P<hir::Expr>]),
+}
+
+/// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
+pub fn vec_macro<'e>(cx: &LateContext, expr: &'e hir::Expr) -> Option<VecArgs<'e>> {
+ if_let_chain!{[
+ let hir::ExprCall(ref fun, ref args) = expr.node,
+ let hir::ExprPath(_, ref path) = fun.node,
+ is_expn_of(cx, fun.span, "vec").is_some()
+ ], {
+ return if match_path(path, &paths::VEC_FROM_ELEM) && args.len() == 2 {
+ // `vec![elem; size]` case
+ Some(VecArgs::Repeat(&args[0], &args[1]))
+ }
+ else if match_path(path, &["into_vec"]) && args.len() == 1 {
+ // `vec![a, b, c]` case
+ if_let_chain!{[
+ let hir::ExprBox(ref boxed) = args[0].node,
+ let hir::ExprVec(ref args) = boxed.node
+ ], {
+ return Some(VecArgs::Vec(&*args));
+ }}
+
+ None
+ }
+ else {
+ None
+ };
+ }}
+
+ None
+}
use rustc::traits;
use rustc::ty::subst::Subst;
use rustc::ty;
+use rustc_errors;
use std::borrow::Cow;
use std::env;
use std::mem;
use std::str::FromStr;
-use syntax::ast::{self, LitKind, RangeLimits};
-use syntax::codemap::{ExpnInfo, Span, ExpnFormat};
+use syntax::ast::{self, LitKind};
+use syntax::codemap::{ExpnFormat, ExpnInfo, MultiSpan, Span};
use syntax::errors::DiagnosticBuilder;
use syntax::ptr::P;
+pub mod cargo;
pub mod comparisons;
pub mod conf;
mod hir;
pub mod paths;
+pub mod sugg;
pub use self::hir::{SpanlessEq, SpanlessHash};
-pub mod cargo;
pub type MethodArgs = HirVec<P<Expr>>;
};
}
+pub mod higher;
+
/// Returns true if the two spans come from differing expansions (i.e. one is from a macro and one
/// isn't).
pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
}
}
-/// Checks if a `let` decl is from a `for` loop desugaring.
-pub fn is_from_for_desugar(decl: &Decl) -> bool {
- if_let_chain! {[
- let DeclLocal(ref loc) = decl.node,
- let Some(ref expr) = loc.init,
- let ExprMatch(_, _, MatchSource::ForLoopDesugar) = expr.node
- ], {
- return true;
- }}
- false
-}
-
-
/// Convert a span to a code snippet if available, otherwise use default.
///
/// # Example
}
}
+/// Create a suggestion made from several `span → replacement`.
+///
+/// Note: in the JSON format (used by `compiletest_rs`), the help message will appear once per
+/// replacement. In human-readable format though, it only appears once before the whole suggestion.
+pub fn multispan_sugg(db: &mut DiagnosticBuilder, help_msg: String, sugg: &[(Span, &str)]) {
+ let sugg = rustc_errors::RenderSpan::Suggestion(rustc_errors::CodeSuggestion {
+ msp: MultiSpan::from_spans(sugg.iter().map(|&(span, _)| span).collect()),
+ substitutes: sugg.iter().map(|&(_, subs)| subs.to_owned()).collect(),
+ });
+
+ let sub = rustc_errors::SubDiagnostic {
+ level: rustc_errors::Level::Help,
+ message: help_msg,
+ span: MultiSpan::new(),
+ render_span: Some(sugg),
+ };
+ db.children.push(sub);
+}
+
/// Return the base type for references and raw pointers.
pub fn walk_ptrs_ty(ty: ty::Ty) -> ty::Ty {
match ty.sty {
last_i
}
-/// Represent a range akin to `ast::ExprKind::Range`.
-#[derive(Debug, Copy, Clone)]
-pub struct UnsugaredRange<'a> {
- pub start: Option<&'a Expr>,
- pub end: Option<&'a Expr>,
- pub limits: RangeLimits,
-}
-
-/// Unsugar a `hir` range.
-pub fn unsugar_range(expr: &Expr) -> Option<UnsugaredRange> {
- // To be removed when ranges get stable.
- fn unwrap_unstable(expr: &Expr) -> &Expr {
- if let ExprBlock(ref block) = expr.node {
- if block.rules == BlockCheckMode::PushUnstableBlock || block.rules == BlockCheckMode::PopUnstableBlock {
- if let Some(ref expr) = block.expr {
- return expr;
- }
- }
- }
-
- expr
- }
-
- fn get_field<'a>(name: &str, fields: &'a [Field]) -> Option<&'a Expr> {
- let expr = &fields.iter()
- .find(|field| field.name.node.as_str() == name)
- .unwrap_or_else(|| panic!("missing {} field for range", name))
- .expr;
-
- Some(unwrap_unstable(expr))
- }
-
- // The range syntax is expanded to literal paths starting with `core` or `std` depending on
- // `#[no_std]`. Testing both instead of resolving the paths.
-
- match unwrap_unstable(expr).node {
- ExprPath(None, ref path) => {
- if match_path(path, &paths::RANGE_FULL_STD) || match_path(path, &paths::RANGE_FULL) {
- Some(UnsugaredRange {
- start: None,
- end: None,
- limits: RangeLimits::HalfOpen,
- })
- } else {
- None
- }
- }
- ExprStruct(ref path, ref fields, None) => {
- if match_path(path, &paths::RANGE_FROM_STD) || match_path(path, &paths::RANGE_FROM) {
- Some(UnsugaredRange {
- start: get_field("start", fields),
- end: None,
- limits: RangeLimits::HalfOpen,
- })
- } else if match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY_STD) ||
- match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY) {
- Some(UnsugaredRange {
- start: get_field("start", fields),
- end: get_field("end", fields),
- limits: RangeLimits::Closed,
- })
- } else if match_path(path, &paths::RANGE_STD) || match_path(path, &paths::RANGE) {
- Some(UnsugaredRange {
- start: get_field("start", fields),
- end: get_field("end", fields),
- limits: RangeLimits::HalfOpen,
- })
- } else if match_path(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_path(path, &paths::RANGE_TO_INCLUSIVE) {
- Some(UnsugaredRange {
- start: None,
- end: get_field("end", fields),
- limits: RangeLimits::Closed,
- })
- } else if match_path(path, &paths::RANGE_TO_STD) || match_path(path, &paths::RANGE_TO) {
- Some(UnsugaredRange {
- start: None,
- end: get_field("end", fields),
- limits: RangeLimits::HalfOpen,
- })
- } else {
- None
- }
- }
- _ => None,
- }
-}
-
/// Convenience function to get the return type of a function or `None` if the function diverges.
pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: NodeId) -> Option<ty::Ty<'tcx>> {
let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_item);
})
}
-/// Recover the essential nodes of a desugared for loop:
-/// `for pat in arg { body }` becomes `(pat, arg, body)`.
-pub fn recover_for_loop(expr: &Expr) -> Option<(&Pat, &Expr, &Expr)> {
- if_let_chain! {[
- let ExprMatch(ref iterexpr, ref arms, _) = expr.node,
- let ExprCall(_, ref iterargs) = iterexpr.node,
- iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
- let ExprLoop(ref block, _) = arms[0].body.node,
- block.stmts.is_empty(),
- let Some(ref loopexpr) = block.expr,
- let ExprMatch(_, ref innerarms, MatchSource::ForLoopDesugar) = loopexpr.node,
- innerarms.len() == 2 && innerarms[0].pats.len() == 1,
- let PatKind::TupleStruct(_, ref somepats, _) = innerarms[0].pats[0].node,
- somepats.len() == 1
- ], {
- return Some((&somepats[0],
- &iterargs[0],
- &innerarms[0].body));
- }}
- None
-}
-
/// Return whether the given type is an `unsafe` function.
pub fn type_is_unsafe_function(ty: ty::Ty) -> bool {
match ty.sty {
--- /dev/null
+use rustc::hir;
+use rustc::lint::{EarlyContext, LateContext};
+use std::borrow::Cow;
+use std;
+use syntax::ast;
+use syntax::util::parser::AssocOp;
+use utils::{higher, snippet, snippet_opt};
+use syntax::print::pprust::binop_to_string;
+
+/// A helper type to build suggestion correctly handling parenthesis.
+pub enum Sugg<'a> {
+ /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
+ NonParen(Cow<'a, str>),
+ /// An expression that does not fit in other variants.
+ MaybeParen(Cow<'a, str>),
+ /// A binary operator expression, including `as`-casts and explicit type coercion.
+ BinOp(AssocOp, Cow<'a, str>),
+}
+
+/// Literal constant `1`, for convenience.
+pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
+
+impl<'a> std::fmt::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) => {
+ s.fmt(f)
+ }
+ }
+ }
+}
+
+impl<'a> Sugg<'a> {
+ pub fn hir_opt(cx: &LateContext, expr: &hir::Expr) -> Option<Sugg<'a>> {
+ snippet_opt(cx, expr.span).map(|snippet| {
+ let snippet = Cow::Owned(snippet);
+ match expr.node {
+ hir::ExprAddrOf(..) |
+ hir::ExprBox(..) |
+ hir::ExprClosure(..) |
+ hir::ExprIf(..) |
+ hir::ExprUnary(..) |
+ hir::ExprMatch(..) => Sugg::MaybeParen(snippet),
+ hir::ExprAgain(..) |
+ hir::ExprBlock(..) |
+ hir::ExprBreak(..) |
+ hir::ExprCall(..) |
+ hir::ExprField(..) |
+ hir::ExprIndex(..) |
+ hir::ExprInlineAsm(..) |
+ hir::ExprLit(..) |
+ hir::ExprLoop(..) |
+ hir::ExprMethodCall(..) |
+ hir::ExprPath(..) |
+ hir::ExprRepeat(..) |
+ hir::ExprRet(..) |
+ hir::ExprStruct(..) |
+ hir::ExprTup(..) |
+ hir::ExprTupField(..) |
+ hir::ExprVec(..) |
+ hir::ExprWhile(..) => Sugg::NonParen(snippet),
+ hir::ExprAssign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
+ hir::ExprAssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
+ hir::ExprBinary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
+ hir::ExprCast(..) => Sugg::BinOp(AssocOp::As, snippet),
+ hir::ExprType(..) => Sugg::BinOp(AssocOp::Colon, snippet),
+ }
+ })
+ }
+
+ pub fn hir(cx: &LateContext, expr: &hir::Expr, default: &'a str) -> Sugg<'a> {
+ Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default)))
+ }
+
+ pub fn ast(cx: &EarlyContext, expr: &ast::Expr, default: &'a str) -> Sugg<'a> {
+ use syntax::ast::RangeLimits;
+
+ let snippet = snippet(cx, expr.span, default);
+
+ match expr.node {
+ ast::ExprKind::AddrOf(..) |
+ ast::ExprKind::Box(..) |
+ ast::ExprKind::Closure(..) |
+ ast::ExprKind::If(..) |
+ ast::ExprKind::IfLet(..) |
+ ast::ExprKind::InPlace(..) |
+ ast::ExprKind::Unary(..) |
+ ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
+ ast::ExprKind::Block(..) |
+ ast::ExprKind::Break(..) |
+ ast::ExprKind::Call(..) |
+ ast::ExprKind::Continue(..) |
+ ast::ExprKind::Field(..) |
+ ast::ExprKind::ForLoop(..) |
+ ast::ExprKind::Index(..) |
+ ast::ExprKind::InlineAsm(..) |
+ ast::ExprKind::Lit(..) |
+ ast::ExprKind::Loop(..) |
+ ast::ExprKind::Mac(..) |
+ ast::ExprKind::MethodCall(..) |
+ ast::ExprKind::Paren(..) |
+ ast::ExprKind::Path(..) |
+ ast::ExprKind::Repeat(..) |
+ ast::ExprKind::Ret(..) |
+ ast::ExprKind::Struct(..) |
+ ast::ExprKind::Try(..) |
+ ast::ExprKind::Tup(..) |
+ ast::ExprKind::TupField(..) |
+ ast::ExprKind::Vec(..) |
+ ast::ExprKind::While(..) |
+ ast::ExprKind::WhileLet(..) => Sugg::NonParen(snippet),
+ ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
+ ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotDot, snippet),
+ ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
+ ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
+ ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
+ ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
+ ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
+ }
+ }
+
+ /// Convenience method to create the `<lhs> && <rhs>` suggestion.
+ pub fn and(self, rhs: Self) -> Sugg<'static> {
+ make_binop(ast::BinOpKind::And, &self, &rhs)
+ }
+
+ /// Convenience method to create the `&<expr>` suggestion.
+ pub fn addr(self) -> Sugg<'static> {
+ make_unop("&", self)
+ }
+
+ /// Convenience method to create the `&mut <expr>` suggestion.
+ pub fn mut_addr(self) -> Sugg<'static> {
+ make_unop("&mut ", self)
+ }
+
+ /// Convenience method to create the `*<expr>` suggestion.
+ pub fn deref(self) -> Sugg<'static> {
+ make_unop("*", self)
+ }
+
+ /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>` suggestion.
+ pub fn range(self, end: Self, limit: ast::RangeLimits) -> Sugg<'static> {
+ match limit {
+ ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, &end),
+ ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotDot, &self, &end),
+ }
+ }
+
+ /// Add parenthesis to any expression that might need them. Suitable to the `self` argument of
+ /// a method call (eg. to build `bar.foo()` or `(1 + 2).foo()`).
+ pub fn maybe_par(self) -> Self {
+ match self {
+ Sugg::NonParen(..) => self,
+ Sugg::MaybeParen(sugg) | Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()),
+ }
+ }
+}
+
+impl<'a, 'b> std::ops::Add<Sugg<'b>> for Sugg<'a> {
+ type Output = Sugg<'static>;
+ fn add(self, rhs: Sugg<'b>) -> Sugg<'static> {
+ make_binop(ast::BinOpKind::Add, &self, &rhs)
+ }
+}
+
+impl<'a, 'b> std::ops::Sub<Sugg<'b>> for Sugg<'a> {
+ type Output = Sugg<'static>;
+ fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> {
+ make_binop(ast::BinOpKind::Sub, &self, &rhs)
+ }
+}
+
+impl<'a> std::ops::Not for Sugg<'a> {
+ type Output = Sugg<'static>;
+ fn not(self) -> Sugg<'static> {
+ make_unop("!", self)
+ }
+}
+
+struct ParenHelper<T> {
+ paren: bool,
+ wrapped: T,
+}
+
+impl<T> ParenHelper<T> {
+ fn new(paren: bool, wrapped: T) -> Self {
+ ParenHelper {
+ paren: paren,
+ wrapped: wrapped,
+ }
+ }
+}
+
+impl<T: std::fmt::Display> std::fmt::Display for ParenHelper<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ if self.paren {
+ write!(f, "({})", self.wrapped)
+ } else {
+ self.wrapped.fmt(f)
+ }
+ }
+}
+
+/// Build the string for `<op><expr>` adding parenthesis when necessary.
+///
+/// For convenience, the operator is taken as a string because all unary operators have the same
+/// precedence.
+pub fn make_unop(op: &str, expr: Sugg) -> Sugg<'static> {
+ Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
+}
+
+/// Build the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
+///
+/// Precedence of shift operator relative to other arithmetic operation is often confusing so
+/// parenthesis will always be added for a mix of these.
+pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
+ fn is_shift(op: &AssocOp) -> bool {
+ matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
+ }
+
+ fn is_arith(op: &AssocOp) -> bool {
+ matches!(*op, AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus)
+ }
+
+ fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
+ other.precedence() < op.precedence() ||
+ (other.precedence() == op.precedence() &&
+ ((op != other && associativity(op) != dir) ||
+ (op == other && associativity(op) != Associativity::Both))) ||
+ is_shift(op) && is_arith(other) ||
+ is_shift(other) && is_arith(op)
+ }
+
+ let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {
+ needs_paren(&op, lop, Associativity::Left)
+ } else {
+ false
+ };
+
+ let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs {
+ needs_paren(&op, rop, Associativity::Right)
+ } else {
+ false
+ };
+
+ let lhs = ParenHelper::new(lhs_paren, lhs);
+ let rhs = ParenHelper::new(rhs_paren, rhs);
+ let sugg = match op {
+ AssocOp::Add |
+ AssocOp::BitAnd |
+ AssocOp::BitOr |
+ AssocOp::BitXor |
+ AssocOp::Divide |
+ AssocOp::Equal |
+ AssocOp::Greater |
+ AssocOp::GreaterEqual |
+ AssocOp::LAnd |
+ AssocOp::LOr |
+ AssocOp::Less |
+ AssocOp::LessEqual |
+ AssocOp::Modulus |
+ AssocOp::Multiply |
+ AssocOp::NotEqual |
+ AssocOp::ShiftLeft |
+ AssocOp::ShiftRight |
+ AssocOp::Subtract => format!("{} {} {}", lhs, op.to_ast_binop().expect("Those are AST ops").to_string(), rhs),
+ AssocOp::Inplace => format!("in ({}) {}", lhs, rhs),
+ AssocOp::Assign => format!("{} = {}", lhs, rhs),
+ AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, binop_to_string(op), rhs),
+ AssocOp::As => format!("{} as {}", lhs, rhs),
+ AssocOp::DotDot => format!("{}..{}", lhs, rhs),
+ AssocOp::DotDotDot => format!("{}...{}", lhs, rhs),
+ AssocOp::Colon => format!("{}: {}", lhs, rhs),
+ };
+
+ Sugg::BinOp(op, sugg.into())
+}
+
+/// Convinience wrapper arround `make_assoc` and `AssocOp::from_ast_binop`.
+pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
+ make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
+}
+
+#[derive(PartialEq, Eq)]
+enum Associativity {
+ Both,
+ Left,
+ None,
+ Right,
+}
+
+/// Return the associativity/fixity of an operator. The difference with `AssocOp::fixity` is that
+/// an operator can be both left and right associative (such as `+`:
+/// `a + b + c == (a + b) + c == a + (b + c)`.
+///
+/// Chained `as` and explicit `:` type coercion never need inner parenthesis so they are considered
+/// associative.
+fn associativity(op: &AssocOp) -> Associativity {
+ use syntax::util::parser::AssocOp::*;
+
+ match *op {
+ Inplace | Assign | AssignOp(_) => Associativity::Right,
+ Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply |
+ As | Colon => Associativity::Both,
+ Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft |
+ ShiftRight | Subtract => Associativity::Left,
+ DotDot | DotDotDot => Associativity::None
+ }
+}
+
+/// Convert a `hir::BinOp` to the corresponding assigning binary operator.
+fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
+ use rustc::hir::BinOp_::*;
+ use syntax::parse::token::BinOpToken::*;
+
+ AssocOp::AssignOp(match op.node {
+ BiAdd => Plus,
+ BiBitAnd => And,
+ BiBitOr => Or,
+ BiBitXor => Caret,
+ BiDiv => Slash,
+ BiMul => Star,
+ BiRem => Percent,
+ BiShl => Shl,
+ BiShr => Shr,
+ BiSub => Minus,
+ BiAnd | BiEq | BiGe | BiGt | BiLe | BiLt | BiNe | BiOr => panic!("This operator does not exist"),
+ })
+}
+
+/// Convert an `ast::BinOp` to the corresponding assigning binary operator.
+fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
+ use syntax::ast::BinOpKind::*;
+ use syntax::parse::token::BinOpToken;
+
+ AssocOp::AssignOp(match op.node {
+ Add => BinOpToken::Plus,
+ BitAnd => BinOpToken::And,
+ BitOr => BinOpToken::Or,
+ BitXor => BinOpToken::Caret,
+ Div => BinOpToken::Slash,
+ Mul => BinOpToken::Star,
+ Rem => BinOpToken::Percent,
+ Shl => BinOpToken::Shl,
+ Shr => BinOpToken::Shr,
+ Sub => BinOpToken::Minus,
+ And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
+ })
+}
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::eval_const_expr_partial;
use syntax::codemap::Span;
-use syntax::ptr::P;
-use utils::{is_expn_of, match_path, paths, recover_for_loop, snippet, span_lint_and_then};
+use utils::{higher, snippet, span_lint_and_then};
/// **What it does:** This lint warns about using `&vec![..]` when using `&[..]` would be possible.
///
}}
// search for `for _ in vec![…]`
- if let Some((_, arg, _)) = recover_for_loop(expr) {
+ if let Some((_, arg, _)) = higher::for_loop(expr) {
// report the error around the `vec!` not inside `<std macros>:`
let span = cx.sess().codemap().source_callsite(arg.span);
check_vec_macro(cx, arg, span);
}
fn check_vec_macro(cx: &LateContext, vec: &Expr, span: Span) {
- if let Some(vec_args) = unexpand(cx, vec) {
-
+ if let Some(vec_args) = higher::vec_macro(cx, vec) {
let snippet = match vec_args {
- Args::Repeat(elem, len) => {
- // Check that the length is a constant expression
+ higher::VecArgs::Repeat(elem, len) => {
if eval_const_expr_partial(cx.tcx, len, ExprTypeChecked, None).is_ok() {
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
} else {
return;
}
}
- Args::Vec(args) => {
+ higher::VecArgs::Vec(args) => {
if let Some(last) = args.iter().last() {
let span = Span {
lo: args[0].span.lo,
}
}
-/// Represent the pre-expansion arguments of a `vec!` invocation.
-pub enum Args<'a> {
- /// `vec![elem; len]`
- Repeat(&'a P<Expr>, &'a P<Expr>),
- /// `vec![a, b, c]`
- Vec(&'a [P<Expr>]),
-}
-
-/// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
-pub fn unexpand<'e>(cx: &LateContext, expr: &'e Expr) -> Option<Args<'e>> {
- if_let_chain!{[
- let ExprCall(ref fun, ref args) = expr.node,
- let ExprPath(_, ref path) = fun.node,
- is_expn_of(cx, fun.span, "vec").is_some()
- ], {
- return if match_path(path, &paths::VEC_FROM_ELEM) && args.len() == 2 {
- // `vec![elem; size]` case
- Some(Args::Repeat(&args[0], &args[1]))
- }
- else if match_path(path, &["into_vec"]) && args.len() == 1 {
- // `vec![a, b, c]` case
- if_let_chain!{[
- let ExprBox(ref boxed) = args[0].node,
- let ExprVec(ref args) = boxed.node
- ], {
- return Some(Args::Vec(&*args));
- }}
-
- None
- }
- else {
- None
- };
- }}
-
- None
-}
i += 2; //~ ERROR assign operation detected
//~^ HELP replace it with
//~| SUGGESTION i = i + 2
+ i += 2 + 17; //~ ERROR assign operation detected
+ //~^ HELP replace it with
+ //~| SUGGESTION i = i + 2 + 17
i -= 6; //~ ERROR assign operation detected
//~^ HELP replace it with
//~| SUGGESTION i = i - 6
+ i -= 2 - 1;
+ //~^ ERROR assign operation detected
+ //~| HELP replace it with
+ //~| SUGGESTION i = i - (2 - 1)
i *= 5; //~ ERROR assign operation detected
//~^ HELP replace it with
//~| SUGGESTION i = i * 5
+ i *= 1+5; //~ ERROR assign operation detected
+ //~^ HELP replace it with
+ //~| SUGGESTION i = i * (1+5)
i /= 32; //~ ERROR assign operation detected
//~^ HELP replace it with
//~| SUGGESTION i = i / 32
+ i /= 32 | 5; //~ ERROR assign operation detected
+ //~^ HELP replace it with
+ //~| SUGGESTION i = i / (32 | 5)
+ i /= 32 / 5; //~ ERROR assign operation detected
+ //~^ HELP replace it with
+ //~| SUGGESTION i = i / (32 / 5)
i %= 42; //~ ERROR assign operation detected
//~^ HELP replace it with
//~| SUGGESTION i = i % 42
i <<= 9 + 6 - 7; //~ ERROR assign operation detected
//~^ HELP replace it with
//~| SUGGESTION i = i << (9 + 6 - 7)
+ i += 1 << 5;
+ //~^ ERROR assign operation detected
+ //~| HELP replace it with
+ //~| SUGGESTION i = i + (1 << 5)
}
#[allow(dead_code, unused_assignments)]
}
}
+ if x == "hello" && x == "world" {
+ //~^ ERROR this if statement can be collapsed
+ //~| HELP try
+ //~| SUGGESTION if x == "hello" && x == "world" && (y == "world" || y == "hello") {
+ if y == "world" || y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" || x == "world" {
+ //~^ ERROR this if statement can be collapsed
+ //~| HELP try
+ //~| SUGGESTION if (x == "hello" || x == "world") && y == "world" && y == "hello" {
+ if y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" && x == "world" {
+ //~^ ERROR this if statement can be collapsed
+ //~| HELP try
+ //~| SUGGESTION if x == "hello" && x == "world" && y == "world" && y == "hello" {
+ if y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if 42 == 1337 {
+ //~^ ERROR this if statement can be collapsed
+ //~| HELP try
+ //~| SUGGESTION if 42 == 1337 && 'a' != 'A' {
+ if 'a' != 'A' {
+ println!("world!")
+ }
+ }
+
// Collaspe `else { if .. }` to `else if ..`
if x == "hello" {
print!("Hello ");
fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
if !m.contains_key(&k) { m.insert(k, v); }
- //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
- //~| HELP Consider
+ //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+ //~| HELP consider
//~| SUGGESTION m.entry(k).or_insert(v)
}
fn insert_if_absent1<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
if !m.contains_key(&k) { foo(); m.insert(k, v); }
- //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
- //~| NOTE Consider using `m.entry(k)`
+ //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+ //~| HELP consider
+ //~| SUGGESTION m.entry(k)
}
fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
if !m.contains_key(&k) { m.insert(k, v) } else { None };
- //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
- //~| NOTE Consider using `m.entry(k)`
+ //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+ //~| HELP consider
+ //~| SUGGESTION m.entry(k)
}
fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
if m.contains_key(&k) { None } else { m.insert(k, v) };
- //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
- //~| NOTE Consider using `m.entry(k)`
+ //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+ //~| HELP consider
+ //~| SUGGESTION m.entry(k)
}
fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
if !m.contains_key(&k) { foo(); m.insert(k, v) } else { None };
- //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
- //~| NOTE Consider using `m.entry(k)`
+ //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+ //~| HELP consider
+ //~| SUGGESTION m.entry(k)
}
fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
if m.contains_key(&k) { None } else { foo(); m.insert(k, v) };
- //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
- //~| NOTE Consider using `m.entry(k)`
+ //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+ //~| HELP consider
+ //~| SUGGESTION m.entry(k)
}
fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) {
if !m.contains_key(&k) { foo(); m.insert(k, v) } else { None };
- //~^ ERROR usage of `contains_key` followed by `insert` on `BTreeMap`
- //~| NOTE Consider using `m.entry(k)`
+ //~^ ERROR usage of `contains_key` followed by `insert` on a `BTreeMap`
+ //~| HELP consider
+ //~| SUGGESTION m.entry(k)
}
fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) {
ZERO == 0.0; //no error, comparison with zero is ok
ZERO + ZERO != 1.0; //no error, comparison with zero is ok
- ONE == 1f32; //~ERROR ==-comparison of f32 or f64
- ONE == (1.0 + 0.0); //~ERROR ==-comparison of f32 or f64
-
- ONE + ONE == (ZERO + ONE + ONE); //~ERROR ==-comparison of f32 or f64
-
- ONE != 2.0; //~ERROR !=-comparison of f32 or f64
+ ONE == 1f32;
+ //~^ ERROR strict comparison of f32 or f64
+ //~| HELP within some error
+ //~| SUGGESTION (ONE - 1f32).abs() < error
+ ONE == 1.0 + 0.0;
+ //~^ ERROR strict comparison of f32 or f64
+ //~| HELP within some error
+ //~| SUGGESTION (ONE - (1.0 + 0.0)).abs() < error
+
+ ONE + ONE == ZERO + ONE + ONE;
+ //~^ ERROR strict comparison of f32 or f64
+ //~| HELP within some error
+ //~| SUGGESTION (ONE + ONE - (ZERO + ONE + ONE)).abs() < error
+
+ ONE != 2.0;
+ //~^ ERROR strict comparison of f32 or f64
+ //~| HELP within some error
+ //~| SUGGESTION (ONE - 2.0).abs() < error
ONE != 0.0; // no error, comparison with zero is ok
- twice(ONE) != ONE; //~ERROR !=-comparison of f32 or f64
- ONE as f64 != 2.0; //~ERROR !=-comparison of f32 or f64
+ twice(ONE) != ONE;
+ //~^ ERROR strict comparison of f32 or f64
+ //~| HELP within some error
+ //~| SUGGESTION (twice(ONE) - ONE).abs() < error
+ ONE as f64 != 2.0;
+ //~^ ERROR strict comparison of f32 or f64
+ //~| HELP within some error
+ //~| SUGGESTION (ONE as f64 - 2.0).abs() < error
ONE as f64 != 0.0; // no error, comparison with zero is ok
let x : f64 = 1.0;
- x == 1.0; //~ERROR ==-comparison of f32 or f64
+ x == 1.0;
+ //~^ ERROR strict comparison of f32 or f64
+ //~| HELP within some error
+ //~| SUGGESTION (x - 1.0).abs() < error
x != 0f64; // no error, comparison with zero is ok
- twice(x) != twice(ONE as f64); //~ERROR !=-comparison of f32 or f64
+ twice(x) != twice(ONE as f64);
+ //~^ ERROR strict comparison of f32 or f64
+ //~| HELP within some error
+ //~| SUGGESTION (twice(x) - twice(ONE as f64)).abs() < error
x < 0.0; // no errors, lower or greater comparisons need no fuzzyness
#![plugin(clippy)]
use std::collections::*;
+use std::rc::Rc;
static STATIC: [usize; 4] = [ 0, 1, 8, 16 ];
const CONST: [usize; 4] = [ 0, 1, 8, 16 ];
let mut vec = vec![1, 2, 3, 4];
let vec2 = vec![1, 2, 3, 4];
for i in 0..vec.len() {
- //~^ ERROR `i` is only used to index `vec`. Consider using `for item in &vec`
+ //~^ ERROR `i` is only used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in &vec {
println!("{}", vec[i]);
}
+ for i in 0..vec.len() { let _ = vec[i]; }
+ //~^ ERROR `i` is only used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in &vec { let _ = vec[i]; }
+
// ICE #746
for j in 0..4 {
//~^ ERROR `j` is only used to index `STATIC`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in STATIC.iter().take(4) {
println!("{:?}", STATIC[j]);
}
for j in 0..4 {
//~^ ERROR `j` is only used to index `CONST`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in CONST.iter().take(4) {
println!("{:?}", CONST[j]);
}
for i in 0..vec.len() {
- //~^ ERROR `i` is used to index `vec`. Consider using `for (i, item) in vec.iter().enumerate()`
+ //~^ ERROR `i` is used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for (i, <item>) in vec.iter().enumerate() {
println!("{} {}", vec[i], i);
}
for i in 0..vec.len() { // not an error, indexing more than one variable
}
for i in 0..vec.len() {
- //~^ ERROR `i` is only used to index `vec2`. Consider using `for item in vec2.iter().take(vec.len())`
+ //~^ ERROR `i` is only used to index `vec2`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in vec2.iter().take(vec.len()) {
println!("{}", vec2[i]);
}
for i in 5..vec.len() {
- //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().skip(5)`
+ //~^ ERROR `i` is only used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in vec.iter().skip(5) {
println!("{}", vec[i]);
}
for i in 0..MAX_LEN {
- //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(MAX_LEN)`
+ //~^ ERROR `i` is only used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in vec.iter().take(MAX_LEN) {
println!("{}", vec[i]);
}
for i in 0...MAX_LEN {
- //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(MAX_LEN)`
+ //~^ ERROR `i` is only used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in vec.iter().take(MAX_LEN + 1) {
println!("{}", vec[i]);
}
for i in 5..10 {
- //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(10).skip(5)`
+ //~^ ERROR `i` is only used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in vec.iter().take(10).skip(5) {
println!("{}", vec[i]);
}
for i in 5...10 {
- //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(10).skip(5)`
+ //~^ ERROR `i` is only used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for <item> in vec.iter().take(10 + 1).skip(5) {
println!("{}", vec[i]);
}
for i in 5..vec.len() {
- //~^ ERROR `i` is used to index `vec`. Consider using `for (i, item) in vec.iter().enumerate().skip(5)`
+ //~^ ERROR `i` is used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for (i, <item>) in vec.iter().enumerate().skip(5) {
println!("{} {}", vec[i], i);
}
for i in 5..10 {
- //~^ ERROR `i` is used to index `vec`. Consider using `for (i, item) in vec.iter().enumerate().take(10).skip(5)`
+ //~^ ERROR `i` is used to index `vec`
+ //~| HELP consider
+ //~| HELP consider
+ //~| SUGGESTION for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
println!("{} {}", vec[i], i);
}
for (_, v) in &m {
//~^ you seem to want to iterate on a map's values
//~| HELP use the corresponding method
- //~| SUGGESTION for v in m.values()
+ //~| HELP use the corresponding method
+ //~| SUGGESTION for v in m.values() {
let _v = v;
}
+ let m : Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
+ for (_, v) in &*m {
+ //~^ you seem to want to iterate on a map's values
+ //~| HELP use the corresponding method
+ //~| HELP use the corresponding method
+ //~| SUGGESTION for v in (*m).values() {
+ let _v = v;
+ // Here the `*` is not actually necesarry, but the test tests that we don't suggest
+ // `in *m.values()` as we used to
+ }
+
let mut m : HashMap<u64, u64> = HashMap::new();
for (_, v) in &mut m {
// Ok, there is no values_mut method or equivalent
for (k, _value) in rm {
//~^ you seem to want to iterate on a map's keys
//~| HELP use the corresponding method
- //~| SUGGESTION for k in rm.keys()
+ //~| HELP use the corresponding method
+ //~| SUGGESTION for k in rm.keys() {
let _k = k;
}
match test {
//~^ ERROR you seem to be trying to match on a boolean expression
- //~| HELP try
+ //~| HELP consider
//~| SUGGESTION if test { 0 } else { 42 };
true => 0,
false => 42,
let option = 1;
match option == 1 {
//~^ ERROR you seem to be trying to match on a boolean expression
- //~| HELP try
+ //~| HELP consider
//~| SUGGESTION if option == 1 { 1 } else { 0 };
true => 1,
false => 0,
match test {
//~^ ERROR you seem to be trying to match on a boolean expression
- //~| HELP try
- //~^^ SUGGESTION if !test { println!("Noooo!"); };
+ //~| HELP consider
+ //~| SUGGESTION if !test { println!("Noooo!"); };
true => (),
false => { println!("Noooo!"); }
};
match test {
//~^ ERROR you seem to be trying to match on a boolean expression
- //~| HELP try
- //~^^ SUGGESTION if !test { println!("Noooo!"); };
+ //~| HELP consider
+ //~| SUGGESTION if !test { println!("Noooo!"); };
+ false => { println!("Noooo!"); }
+ _ => (),
+ };
+
+ match test && test {
+ //~^ ERROR you seem to be trying to match on a boolean expression
+ //~| HELP consider
+ //~| SUGGESTION if !(test && test) { println!("Noooo!"); };
+ //~| ERROR equal expressions as operands
false => { println!("Noooo!"); }
_ => (),
};
match test {
//~^ ERROR you seem to be trying to match on a boolean expression
- //~| HELP try
+ //~| HELP consider
//~| SUGGESTION if test { println!("Yes!"); } else { println!("Noooo!"); };
false => { println!("Noooo!"); }
true => { println!("Yes!"); }
//~| HELP try
//~| SUGGESTION let x = &1;
- let ref y : (&_, u8) = (&1, 2);
+ let ref y: (&_, u8) = (&1, 2);
//~^ ERROR `ref` on an entire `let` pattern is discouraged
//~| HELP try
//~| SUGGESTION let y: (&_, u8) = &(&1, 2);
+ let ref z = 1 + 2;
+ //~^ ERROR `ref` on an entire `let` pattern is discouraged
+ //~| HELP try
+ //~| SUGGESTION let z = &(1 + 2);
+
let (ref x, _) = (1,2); // okay, not top level
println!("The answer is {}.", x);
}
//~^ ERROR transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
//~| HELP try
//~| SUGGESTION = &mut *(p as *mut T);
+ let _ = &mut *(p as *mut T);
let _: &T = std::mem::transmute(o);
//~^ ERROR transmute from a pointer type (`*const U`) to a reference type (`&T`)