rust-clippy/clippy_lints/src/misc_early.rs

287 lines
11 KiB
Rust
Raw Normal View History

use rustc::lint::*;
use std::collections::HashMap;
use std::char;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::visit::FnKind;
use utils::{span_lint, span_help_and_lint, snippet, snippet_opt, span_lint_and_then};
/// **What it does:** This lint checks for structure field patterns bound to wildcards.
2015-12-14 21:16:56 +00:00
///
/// **Why is this bad?** Using `..` instead is shorter and leaves the focus on the fields that are actually bound.
///
/// **Known problems:** None.
///
2016-07-15 22:25:44 +00:00
/// **Example:**
/// ```rust
/// let { a: _, b: ref b, c: _ } = ..
/// ```
declare_lint! {
pub UNNEEDED_FIELD_PATTERN, Warn,
"Struct fields are bound to a wildcard instead of using `..`"
}
/// **What it does:** This lint checks for function arguments having the similar names differing by an underscore
///
2016-01-03 15:55:09 +00:00
/// **Why is this bad?** It affects code readability
///
/// **Known problems:** None.
///
2016-07-15 22:25:44 +00:00
/// **Example:**
/// ```rust
/// fn foo(a: i32, _a: i32) {}
/// ```
declare_lint! {
pub DUPLICATE_UNDERSCORE_ARGUMENT, Warn,
"Function arguments having names which only differ by an underscore"
}
/// **What it does:** This lint detects closures called in the same expression where they are defined.
///
/// **Why is this bad?** It is unnecessarily adding to the expression's complexity.
///
/// **Known problems:** None.
///
2016-07-15 22:25:44 +00:00
/// **Example:**
/// ```rust
/// (|| 42)()
/// ```
declare_lint! {
pub REDUNDANT_CLOSURE_CALL, Warn,
"Closures should not be called in the expression they are defined"
}
2016-06-29 23:00:25 +00:00
/// **What it does:** This lint detects expressions of the form `--x`
///
/// **Why is this bad?** It can mislead C/C++ programmers to think `x` was decremented.
///
/// **Known problems:** None.
///
2016-07-15 22:25:44 +00:00
/// **Example:**
/// ```rust
/// --x;
/// ```
2016-06-29 23:00:25 +00:00
declare_lint! {
pub DOUBLE_NEG, Warn,
2016-06-30 04:33:21 +00:00
"`--x` is a double negation of `x` and not a pre-decrement as in C or C++"
2016-06-29 23:00:25 +00:00
}
/// **What it does:** Warns on hexadecimal literals with mixed-case letter digits.
///
/// **Why is this bad?** It looks confusing.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let y = 0x1a9BAcD;
/// ```
declare_lint! {
pub MIXED_CASE_HEX_LITERALS, Warn,
"letter digits in hex literals should be either completely upper- or lowercased"
}
/// **What it does:** Warns if literal suffixes are not separated by an underscore.
///
/// **Why is this bad?** It is much less readable.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let y = 123832i32;
/// ```
declare_lint! {
pub UNSEPARATED_LITERAL_SUFFIX, Allow,
"literal suffixes should be separated with an underscore"
}
2016-06-29 23:00:25 +00:00
#[derive(Copy, Clone)]
pub struct MiscEarly;
impl LintPass for MiscEarly {
fn get_lints(&self) -> LintArray {
lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, REDUNDANT_CLOSURE_CALL,
DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX)
}
}
impl EarlyLintPass for MiscEarly {
fn check_pat(&mut self, cx: &EarlyContext, pat: &Pat) {
if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
let mut wilds = 0;
2016-02-20 20:03:45 +00:00
let type_name = npat.segments.last().expect("A path must have at least one segment").identifier.name;
for field in pfields {
if field.node.pat.node == PatKind::Wild {
wilds += 1;
}
}
if !pfields.is_empty() && wilds == pfields.len() {
2016-01-04 04:26:12 +00:00
span_help_and_lint(cx,
UNNEEDED_FIELD_PATTERN,
pat.span,
"All the struct fields are matched to a wildcard pattern, consider using `..`.",
&format!("Try with `{} {{ .. }}` instead", type_name));
return;
}
if wilds > 0 {
2016-01-04 04:26:12 +00:00
let mut normal = vec![];
2016-01-02 04:52:13 +00:00
for field in pfields {
if field.node.pat.node != PatKind::Wild {
2016-01-02 04:52:13 +00:00
if let Ok(n) = cx.sess().codemap().span_to_snippet(field.span) {
normal.push(n);
}
}
}
for field in pfields {
if field.node.pat.node == PatKind::Wild {
2016-01-02 04:52:13 +00:00
wilds -= 1;
if wilds > 0 {
2016-01-04 04:26:12 +00:00
span_lint(cx,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` instead");
2016-01-02 04:52:13 +00:00
} else {
2016-01-04 04:26:12 +00:00
span_help_and_lint(cx,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` \
instead",
2016-01-02 04:52:13 +00:00
&format!("Try with `{} {{ {}, .. }}`",
type_name,
normal[..].join(", ")));
}
}
}
}
}
}
fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
2016-01-04 04:26:12 +00:00
let mut registered_names: HashMap<String, Span> = HashMap::new();
2016-08-01 14:59:14 +00:00
for arg in &decl.inputs {
if let PatKind::Ident(_, sp_ident, None) = arg.pat.node {
2016-01-03 15:55:09 +00:00
let arg_name = sp_ident.node.to_string();
if arg_name.starts_with('_') {
if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
2016-01-04 04:26:12 +00:00
span_lint(cx,
DUPLICATE_UNDERSCORE_ARGUMENT,
*correspondence,
2016-01-04 04:26:12 +00:00
&format!("`{}` already exists, having another argument having almost the same \
name makes code comprehension and documentation more difficult",
arg_name[1..].to_owned()));;
2016-01-03 15:55:09 +00:00
}
} else {
registered_names.insert(arg_name, arg.pat.span);
}
}
}
}
fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
2016-06-29 23:00:25 +00:00
match expr.node {
ExprKind::Call(ref paren, _) => {
if let ExprKind::Paren(ref closure) = paren.node {
if let ExprKind::Closure(_, ref decl, ref block, _) = closure.node {
span_lint_and_then(cx,
REDUNDANT_CLOSURE_CALL,
expr.span,
"Try not to call a closure in the expression where it is declared.",
|db| {
if decl.inputs.is_empty() {
2016-07-01 19:01:56 +00:00
let hint = snippet(cx, block.span, "..").into_owned();
2016-06-29 23:00:25 +00:00
db.span_suggestion(expr.span, "Try doing something like: ", hint);
}
});
}
}
}
2016-06-29 23:00:25 +00:00
ExprKind::Unary(UnOp::Neg, ref inner) => {
if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
span_lint(cx,
DOUBLE_NEG,
expr.span,
"`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
}
}
ExprKind::Lit(ref lit) => {
if_let_chain! {[
let LitKind::Int(..) = lit.node,
let Some(src) = snippet_opt(cx, lit.span),
let Some(firstch) = src.chars().next(),
char::to_digit(firstch, 10).is_some()
], {
let mut prev = '\0';
for ch in src.chars() {
if ch == 'i' || ch == 'u' {
if prev != '_' {
span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
"integer type suffix should be separated by an underscore");
}
break;
}
prev = ch;
}
if src.starts_with("0x") {
let mut seen = (false, false);
for ch in src.chars() {
match ch {
'a' ... 'f' => seen.0 = true,
'A' ... 'F' => seen.1 = true,
'i' | 'u' => break, // start of suffix already
_ => ()
}
}
if seen.0 && seen.1 {
span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span,
"inconsistent casing in hexadecimal literal");
}
}
}}
if_let_chain! {[
let LitKind::Float(..) = lit.node,
let Some(src) = snippet_opt(cx, lit.span),
let Some(firstch) = src.chars().next(),
char::to_digit(firstch, 10).is_some()
], {
let mut prev = '\0';
for ch in src.chars() {
if ch == 'f' {
if prev != '_' {
span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
"float type suffix should be separated by an underscore");
}
break;
}
prev = ch;
}
}}
2016-06-29 23:00:25 +00:00
}
_ => ()
}
}
fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
for w in block.stmts.windows(2) {
if_let_chain! {[
let StmtKind::Local(ref local) = w[0].node,
let Option::Some(ref t) = local.init,
let ExprKind::Closure(_, _, _, _) = t.node,
let PatKind::Ident(_, sp_ident, _) = local.pat.node,
let StmtKind::Semi(ref second) = w[1].node,
let ExprKind::Assign(_, ref call) = second.node,
let ExprKind::Call(ref closure, _) = call.node,
let ExprKind::Path(_, ref path) = closure.node
], {
if sp_ident.node == (&path.segments[0]).identifier {
span_lint(cx, REDUNDANT_CLOSURE_CALL, second.span, "Closure called just once immediately after it was declared");
}
}}
}
}
}