commit
ad1cd99054
33 changed files with 1064 additions and 465 deletions
|
|
@ -1,7 +1,10 @@
|
|||
# Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 0.0.78 - 2016-07-02
|
||||
## 0.0.79 — ?
|
||||
* Major suggestions refactoring
|
||||
|
||||
## 0.0.78 — 2016-07-02
|
||||
* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
|
||||
* New lints: [`wrong_transmute`, `double_neg`]
|
||||
* For compatibility, `cargo clippy` does not defines the `clippy` feature
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ name
|
|||
[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 } ...`
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_const_eval::eval_const_expr_partial;
|
|||
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.
|
||||
///
|
||||
|
|
@ -77,7 +77,7 @@ impl LateLintPass for ArrayIndexing {
|
|||
}
|
||||
|
||||
// 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());
|
||||
|
|
@ -94,7 +94,7 @@ impl LateLintPass for ArrayIndexing {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
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:**
|
||||
/// ```
|
||||
|
|
@ -15,14 +16,14 @@ use utils::{span_lint_and_then, span_lint, snippet_opt, SpanlessEq, get_trait_de
|
|||
/// ```
|
||||
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:**
|
||||
///
|
||||
|
|
@ -50,24 +51,14 @@ impl LateLintPass for AssignOps {
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -13,11 +13,10 @@
|
|||
//! 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
|
||||
|
|
@ -104,31 +103,17 @@ fn check_collapsible_no_if_let(
|
|||
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();
|
||||
|
|
|
|||
|
|
@ -121,21 +121,21 @@ impl<'a, 'tcx, 'v, 'b> Visitor<'v> for InsertVisitor<'a, 'tcx, 'b> {
|
|||
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);
|
||||
}
|
||||
});
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ fn check_consecutive_ifs(cx: &EarlyContext, first: &ast::Expr, second: &ast::Exp
|
|||
}
|
||||
}
|
||||
|
||||
/// 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_) |
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#![feature(box_syntax)]
|
||||
#![feature(collections)]
|
||||
#![feature(custom_attribute)]
|
||||
#![feature(dotdot_in_tuple_patterns)]
|
||||
#![feature(question_mark)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(slice_patterns)]
|
||||
|
|
@ -36,6 +37,7 @@ extern crate quine_mc_cluskey;
|
|||
|
||||
extern crate rustc_serialize;
|
||||
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_plugin;
|
||||
extern crate rustc_const_eval;
|
||||
extern crate rustc_const_math;
|
||||
|
|
|
|||
|
|
@ -9,15 +9,14 @@ use rustc::middle::region::CodeExtent;
|
|||
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.
|
||||
///
|
||||
|
|
@ -224,7 +223,7 @@ impl LintPass for Pass {
|
|||
|
||||
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`
|
||||
|
|
@ -333,7 +332,7 @@ fn check_for_loop(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &E
|
|||
/// 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 {
|
||||
|
|
@ -361,34 +360,41 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
|
|||
|
||||
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)
|
||||
|
|
@ -396,14 +402,16 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
|
|||
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),
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -427,7 +435,7 @@ fn is_len_call(expr: &Expr, var: &Name) -> bool {
|
|||
|
||||
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) {
|
||||
|
|
@ -588,18 +596,20 @@ fn check_for_loop_explicit_counter(cx: &LateContext, arg: &Expr, body: &Expr, ex
|
|||
|
||||
/// 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));
|
||||
|
|
@ -607,14 +617,13 @@ fn check_for_loop_over_map_kv(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Ex
|
|||
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)),
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use syntax::ast::LitKind;
|
|||
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.
|
||||
///
|
||||
|
|
@ -234,56 +235,54 @@ fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, arms: &[Arm], expr:
|
|||
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,
|
||||
}
|
||||
} 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, "..")))
|
||||
}
|
||||
(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:",
|
||||
"you seem to be trying to match on a boolean expression",
|
||||
move |db| {
|
||||
if let Some(sugg) = sugg {
|
||||
db.span_suggestion(expr.span, "try this", 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,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,26 +306,28 @@ fn check_overlapping_arms(cx: &LateContext, ex: &Expr, arms: &[Arm]) {
|
|||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -409,12 +410,11 @@ fn has_only_ref_pats(arms: &[Arm]) -> bool {
|
|||
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!"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ use std::fmt;
|
|||
use syntax::codemap::Span;
|
||||
use syntax::ptr::P;
|
||||
use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, match_path, match_trait_method,
|
||||
match_type, method_chain_args, return_ty, same_tys, snippet, snippet_opt, span_lint,
|
||||
match_type, method_chain_args, return_ty, same_tys, snippet, span_lint,
|
||||
span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};
|
||||
use utils::MethodArgs;
|
||||
use utils::paths;
|
||||
use utils::sugg;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Pass;
|
||||
|
|
@ -628,8 +629,8 @@ fn lint_clone_double_ref(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr, ty
|
|||
expr.span,
|
||||
"using `clone` on a double-reference; \
|
||||
this will copy the reference instead of cloning the inner type",
|
||||
|db| if let Some(snip) = snippet_opt(cx, arg.span) {
|
||||
db.span_suggestion(expr.span, "try dereferencing it", format!("(*{}).clone()", snip));
|
||||
|db| if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
|
||||
db.span_suggestion(expr.span, "try dereferencing it", format!("({}).clone()", snip.deref()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -641,14 +642,13 @@ fn lint_extend(cx: &LateContext, expr: &hir::Expr, args: &MethodArgs) {
|
|||
return;
|
||||
}
|
||||
let arg_ty = cx.tcx.expr_ty(&args[1]);
|
||||
if let Some((span, r)) = derefs_to_slice(cx, &args[1], &arg_ty) {
|
||||
if let Some(slice) = derefs_to_slice(cx, &args[1], &arg_ty) {
|
||||
span_lint_and_then(cx, EXTEND_FROM_SLICE, expr.span, "use of `extend` to extend a Vec by a slice", |db| {
|
||||
db.span_suggestion(expr.span,
|
||||
"try this",
|
||||
format!("{}.extend_from_slice({}{})",
|
||||
format!("{}.extend_from_slice({})",
|
||||
snippet(cx, args[0].span, "_"),
|
||||
r,
|
||||
snippet(cx, span, "_")));
|
||||
slice));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -672,20 +672,20 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr
|
|||
#[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,
|
||||
|
|
@ -695,7 +695,7 @@ fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &MethodArgs, is_
|
|||
);
|
||||
}
|
||||
|
||||
fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: &ty::Ty) -> Option<(Span, &'static str)> {
|
||||
fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: &ty::Ty) -> Option<sugg::Sugg<'static>> {
|
||||
fn may_slice(cx: &LateContext, ty: &ty::Ty) -> bool {
|
||||
match ty.sty {
|
||||
ty::TySlice(_) => true,
|
||||
|
|
@ -706,19 +706,22 @@ fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: &ty::Ty) -> Option<(S
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
if let hir::ExprMethodCall(name, _, ref args) = expr.node {
|
||||
if &name.node.as_str() == &"iter" && may_slice(cx, &cx.tcx.expr_ty(&args[0])) {
|
||||
Some((args[0].span, "&"))
|
||||
sugg::Sugg::hir_opt(cx, &*args[0]).map(|sugg| {
|
||||
sugg.addr()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
match ty.sty {
|
||||
ty::TySlice(_) => Some((expr.span, "")),
|
||||
ty::TySlice(_) => sugg::Sugg::hir_opt(cx, expr),
|
||||
ty::TyRef(_, ty::TypeAndMut { ty: ref inner, .. }) |
|
||||
ty::TyBox(ref inner) => {
|
||||
if may_slice(cx, inner) {
|
||||
Some((expr.span, ""))
|
||||
sugg::Sugg::hir_opt(cx, expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use utils::{
|
|||
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`.
|
||||
///
|
||||
|
|
@ -69,14 +70,15 @@ impl LateLintPass for TopLevelRefPass {
|
|||
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()));
|
||||
}
|
||||
);
|
||||
}}
|
||||
|
|
@ -164,15 +166,19 @@ impl LateLintPass for FloatCmp {
|
|||
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.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ impl EarlyLintPass for MiscEarly {
|
|||
"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);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use rustc::hir;
|
|||
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.
|
||||
///
|
||||
|
|
@ -49,7 +49,7 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for MutVisitor<'a, 'tcx> {
|
|||
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) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ use rustc::lint::*;
|
|||
use rustc::hir::*;
|
||||
use syntax::ast::LitKind;
|
||||
use syntax::codemap::Spanned;
|
||||
use utils::{span_lint, span_lint_and_then, snippet, snippet_opt};
|
||||
use utils::{span_lint, span_lint_and_then, snippet};
|
||||
use utils::sugg::Sugg;
|
||||
|
||||
/// **What it does:** This lint checks for expressions of the form `if c { true } else { false }` (or vice versa) and suggest using the condition directly.
|
||||
///
|
||||
|
|
@ -49,11 +50,20 @@ impl LateLintPass for NeedlessBool {
|
|||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
use self::Expression::*;
|
||||
if let ExprIf(ref pred, ref then_block, Some(ref else_expr)) = e.node {
|
||||
let reduce = |hint: &str, not| {
|
||||
let hint = match snippet_opt(cx, pred.span) {
|
||||
Some(pred_snip) => format!("`{}{}`", not, pred_snip),
|
||||
None => hint.into(),
|
||||
let reduce = |ret, not| {
|
||||
let snip = Sugg::hir(cx, pred, "<predicate>");
|
||||
let snip = if not {
|
||||
!snip
|
||||
} else {
|
||||
snip
|
||||
};
|
||||
|
||||
let hint = if ret {
|
||||
format!("return {}", snip)
|
||||
} else {
|
||||
snip.to_string()
|
||||
};
|
||||
|
||||
span_lint_and_then(cx,
|
||||
NEEDLESS_BOOL,
|
||||
e.span,
|
||||
|
|
@ -77,10 +87,10 @@ impl LateLintPass for NeedlessBool {
|
|||
e.span,
|
||||
"this if-then-else expression will always return false");
|
||||
}
|
||||
(RetBool(true), RetBool(false)) => reduce("its predicate", "return "),
|
||||
(Bool(true), Bool(false)) => reduce("its predicate", ""),
|
||||
(RetBool(false), RetBool(true)) => reduce("`!` and its predicate", "return !"),
|
||||
(Bool(false), Bool(true)) => reduce("`!` and its predicate", "!"),
|
||||
(RetBool(true), RetBool(false)) => reduce(true, false),
|
||||
(Bool(true), Bool(false)) => reduce(false, false),
|
||||
(RetBool(false), RetBool(true)) => reduce(true, true),
|
||||
(Bool(false), Bool(true)) => reduce(false, true),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -122,23 +132,23 @@ impl LateLintPass for BoolComparison {
|
|||
});
|
||||
}
|
||||
(Bool(false), Other) => {
|
||||
let hint = format!("!{}", snippet(cx, right_side.span, ".."));
|
||||
let hint = Sugg::hir(cx, right_side, "..");
|
||||
span_lint_and_then(cx,
|
||||
BOOL_COMPARISON,
|
||||
e.span,
|
||||
"equality checks against false can be replaced by a negation",
|
||||
|db| {
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", hint);
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", (!hint).to_string());
|
||||
});
|
||||
}
|
||||
(Other, Bool(false)) => {
|
||||
let hint = format!("!{}", snippet(cx, left_side.span, ".."));
|
||||
let hint = Sugg::hir(cx, left_side, "..");
|
||||
span_lint_and_then(cx,
|
||||
BOOL_COMPARISON,
|
||||
e.span,
|
||||
"equality checks against false can be replaced by a negation",
|
||||
|db| {
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", hint);
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", (!hint).to_string());
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
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.
|
||||
///
|
||||
|
|
@ -54,7 +55,7 @@ impl LateLintPass for StepByZero {
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc::hir::*;
|
|||
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.
|
||||
///
|
||||
|
|
@ -91,7 +91,7 @@ fn check_decl(cx: &LateContext, decl: &Decl, bindings: &mut Vec<(Name, Span)>) {
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use rustc::hir::*;
|
|||
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.
|
||||
///
|
||||
|
|
@ -100,17 +101,17 @@ fn check_manual_swap(cx: &LateContext, block: &Block) {
|
|||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
@ -147,8 +148,8 @@ fn check_suspicious_swap(cx: &LateContext, block: &Block) {
|
|||
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())
|
||||
};
|
||||
|
|
@ -162,7 +163,7 @@ fn check_suspicious_swap(cx: &LateContext, block: &Block) {
|
|||
|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`?");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use rustc::lint::*;
|
|||
use rustc::ty::TypeVariants::{TyRawPtr, TyRef};
|
||||
use rustc::ty;
|
||||
use rustc::hir::*;
|
||||
use utils::{match_def_path, paths, snippet_opt, span_lint, span_lint_and_then};
|
||||
use utils::{match_def_path, paths, 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
|
||||
///
|
||||
|
|
@ -92,14 +93,14 @@ impl LateLintPass for Transmute {
|
|||
e.span,
|
||||
"transmute from a reference to a pointer",
|
||||
|db| {
|
||||
if let Some(arg) = snippet_opt(cx, args[0].span) {
|
||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &*args[0]) {
|
||||
let sugg = if ptr_ty == rty {
|
||||
format!("{} as {}", arg, to_ty)
|
||||
arg.as_ty(to_ty)
|
||||
} else {
|
||||
format!("{} as {} as {}", arg, cx.tcx.mk_ptr(rty), to_ty)
|
||||
arg.as_ty(cx.tcx.mk_ptr(rty)).as_ty(to_ty)
|
||||
};
|
||||
|
||||
db.span_suggestion(e.span, "try", sugg);
|
||||
db.span_suggestion(e.span, "try", sugg.to_string());
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
@ -110,8 +111,8 @@ impl LateLintPass for Transmute {
|
|||
e.span,
|
||||
"transmute from an integer to a pointer",
|
||||
|db| {
|
||||
if let Some(arg) = snippet_opt(cx, args[0].span) {
|
||||
db.span_suggestion(e.span, "try", format!("{} as {}", arg, to_ty));
|
||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &*args[0]) {
|
||||
db.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()).to_string());
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
@ -148,28 +149,21 @@ impl LateLintPass for Transmute {
|
|||
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 arg = if from_pty.ty == to_rty.ty {
|
||||
arg
|
||||
} else {
|
||||
arg.as_ty(&format!("{} {}", cast, to_rty.ty))
|
||||
};
|
||||
|
||||
db.span_suggestion(e.span, "try", sugg);
|
||||
}
|
||||
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
|
||||
},
|
||||
),
|
||||
_ => return,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc::ty;
|
|||
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;
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ fn check_let_unit(cx: &LateContext, decl: &Decl) {
|
|||
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,
|
||||
|
|
|
|||
190
clippy_lints/src/utils/higher.rs
Normal file
190
clippy_lints/src/utils/higher.rs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
//! 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
|
||||
}
|
||||
|
|
@ -9,21 +9,23 @@ use rustc::traits::ProjectionMode;
|
|||
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>>;
|
||||
|
||||
|
|
@ -80,6 +82,8 @@ macro_rules! if_let_chain {
|
|||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
@ -317,19 +321,6 @@ pub fn get_item_name(cx: &LateContext, expr: &Expr) -> Option<Name> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
|
@ -500,6 +491,25 @@ pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint,
|
|||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
|
@ -681,93 +691,6 @@ pub fn camel_case_from(s: &str) -> usize {
|
|||
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);
|
||||
|
|
@ -792,28 +715,6 @@ pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: ty::Ty<'tcx>, b: ty::Ty
|
|||
})
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
|
|
|||
356
clippy_lints/src/utils/sugg.rs
Normal file
356
clippy_lints/src/utils/sugg.rs
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(wrong_self_convention)] // ok, because of the function `as_ty` method
|
||||
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 `<lhs> as <rhs>` suggestion.
|
||||
pub fn as_ty<R: std::fmt::Display>(self, rhs: R) -> Sugg<'static> {
|
||||
make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
|
||||
}
|
||||
|
||||
/// 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"),
|
||||
})
|
||||
}
|
||||
|
|
@ -4,8 +4,7 @@ use rustc::hir::*;
|
|||
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.
|
||||
///
|
||||
|
|
@ -44,7 +43,7 @@ impl LateLintPass for Pass {
|
|||
}}
|
||||
|
||||
// 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);
|
||||
|
|
@ -53,18 +52,16 @@ impl LateLintPass for Pass {
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
@ -85,40 +82,3 @@ fn check_vec_macro(cx: &LateContext, vec: &Expr, span: Span) {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,15 +8,31 @@ fn main() {
|
|||
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
|
||||
|
|
@ -26,6 +42,10 @@ fn main() {
|
|||
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)]
|
||||
|
|
|
|||
|
|
@ -23,6 +23,42 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
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 ");
|
||||
|
|
|
|||
|
|
@ -11,45 +11,51 @@ fn foo() {}
|
|||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -40,23 +40,47 @@ fn main() {
|
|||
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 == 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 ==-comparison of f32 or f64
|
||||
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 !=-comparison of f32 or f64
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#![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 ];
|
||||
|
|
@ -96,23 +97,41 @@ fn main() {
|
|||
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
|
||||
|
|
@ -120,42 +139,66 @@ fn main() {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -346,10 +389,22 @@ fn main() {
|
|||
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
|
||||
|
|
@ -361,7 +416,8 @@ fn main() {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ fn match_bool() {
|
|||
|
||||
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,
|
||||
|
|
@ -121,7 +121,7 @@ fn match_bool() {
|
|||
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,
|
||||
|
|
@ -129,23 +129,32 @@ fn match_bool() {
|
|||
|
||||
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!"); }
|
||||
|
|
|
|||
|
|
@ -5,21 +5,28 @@
|
|||
#[allow(if_same_then_else)]
|
||||
fn main() {
|
||||
let x = true;
|
||||
let y = false;
|
||||
if x { true } else { true }; //~ERROR this if-then-else expression will always return true
|
||||
if x { false } else { false }; //~ERROR this if-then-else expression will always return false
|
||||
if x { true } else { false };
|
||||
//~^ ERROR this if-then-else expression returns a bool literal
|
||||
//~| HELP you can reduce it to
|
||||
//~| SUGGESTION `x`
|
||||
//~| SUGGESTION x
|
||||
if x { false } else { true };
|
||||
//~^ ERROR this if-then-else expression returns a bool literal
|
||||
//~| HELP you can reduce it to
|
||||
//~| SUGGESTION `!x`
|
||||
//~| SUGGESTION !x
|
||||
if x && y { false } else { true };
|
||||
//~^ ERROR this if-then-else expression returns a bool literal
|
||||
//~| HELP you can reduce it to
|
||||
//~| SUGGESTION !(x && y)
|
||||
if x { x } else { false }; // would also be questionable, but we don't catch this yet
|
||||
bool_ret(x);
|
||||
bool_ret2(x);
|
||||
bool_ret3(x);
|
||||
bool_ret5(x, x);
|
||||
bool_ret4(x);
|
||||
bool_ret6(x, x);
|
||||
}
|
||||
|
||||
#[allow(if_same_then_else, needless_return)]
|
||||
|
|
@ -39,7 +46,15 @@ fn bool_ret3(x: bool) -> bool {
|
|||
if x { return true } else { return false };
|
||||
//~^ ERROR this if-then-else expression returns a bool literal
|
||||
//~| HELP you can reduce it to
|
||||
//~| SUGGESTION `return x`
|
||||
//~| SUGGESTION return x
|
||||
}
|
||||
|
||||
#[allow(needless_return)]
|
||||
fn bool_ret5(x: bool, y: bool) -> bool {
|
||||
if x && y { return true } else { return false };
|
||||
//~^ ERROR this if-then-else expression returns a bool literal
|
||||
//~| HELP you can reduce it to
|
||||
//~| SUGGESTION return x && y
|
||||
}
|
||||
|
||||
#[allow(needless_return)]
|
||||
|
|
@ -47,5 +62,13 @@ fn bool_ret4(x: bool) -> bool {
|
|||
if x { return false } else { return true };
|
||||
//~^ ERROR this if-then-else expression returns a bool literal
|
||||
//~| HELP you can reduce it to
|
||||
//~| SUGGESTION `return !x`
|
||||
//~| SUGGESTION return !x
|
||||
}
|
||||
|
||||
#[allow(needless_return)]
|
||||
fn bool_ret6(x: bool, y: bool) -> bool {
|
||||
if x && y { return false } else { return true };
|
||||
//~^ ERROR this if-then-else expression returns a bool literal
|
||||
//~| HELP you can reduce it to
|
||||
//~| SUGGESTION return !(x && y)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,16 @@ fn main() {
|
|||
//~| 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
|
|||
//~^ 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`)
|
||||
|
|
@ -110,6 +111,13 @@ fn useless() {
|
|||
//~^ ERROR transmute from an integer to a pointer
|
||||
//~| HELP try
|
||||
//~| SUGGESTION 5_isize as *const usize
|
||||
let _ = 5_isize as *const usize;
|
||||
|
||||
let _: *const usize = std::mem::transmute(1+1usize);
|
||||
//~^ ERROR transmute from an integer to a pointer
|
||||
//~| HELP try
|
||||
//~| SUGGESTION (1+1usize) as *const usize
|
||||
let _ = (1+1_usize) as *const usize;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue