Merge branch 'master' into no_effect_with_drop
This commit is contained in:
commit
fcdce8fc1d
348 changed files with 2221 additions and 1475 deletions
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
# begin automatic update
|
||||
version = "0.0.161"
|
||||
version = "0.0.165"
|
||||
# end automatic update
|
||||
authors = [
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
|
|
@ -28,6 +28,7 @@ serde_derive = "1.0"
|
|||
toml = "0.4"
|
||||
unicode-normalization = "0.1"
|
||||
pulldown-cmark = "0.0.15"
|
||||
url = "1.5.0"
|
||||
|
||||
[features]
|
||||
debugging = []
|
||||
|
|
|
|||
|
|
@ -90,7 +90,17 @@ declare_lint! {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BitMask;
|
||||
pub struct BitMask {
|
||||
verbose_bit_mask_threshold: u64,
|
||||
}
|
||||
|
||||
impl BitMask {
|
||||
pub fn new(verbose_bit_mask_threshold: u64) -> Self {
|
||||
Self {
|
||||
verbose_bit_mask_threshold: verbose_bit_mask_threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LintPass for BitMask {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
|
|
@ -119,6 +129,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask {
|
|||
let Expr_::ExprLit(ref lit1) = right.node,
|
||||
let LitKind::Int(0, _) = lit1.node,
|
||||
n.leading_zeros() == n.count_zeros(),
|
||||
n > u128::from(self.verbose_bit_mask_threshold),
|
||||
], {
|
||||
span_lint_and_then(cx,
|
||||
VERBOSE_BIT_MASK,
|
||||
|
|
@ -307,7 +318,7 @@ fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u128> {
|
|||
cx.tcx.mir_const_qualif(def_id);
|
||||
cx.tcx.hir.body(cx.tcx.hir.body_owned_by(id))
|
||||
} else {
|
||||
cx.tcx.extern_const_body(def_id)
|
||||
cx.tcx.extern_const_body(def_id).body
|
||||
};
|
||||
fetch_int_literal(cx, &body.value)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||
self.tcx.mir_const_qualif(def_id);
|
||||
self.tcx.hir.body(self.tcx.hir.body_owned_by(id))
|
||||
} else {
|
||||
self.tcx.extern_const_body(def_id)
|
||||
self.tcx.extern_const_body(def_id).body
|
||||
};
|
||||
let ret = cx.expr(&body.value);
|
||||
if ret.is_some() {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use syntax::ast;
|
|||
use syntax::codemap::{BytePos, Span};
|
||||
use syntax_pos::Pos;
|
||||
use utils::span_lint;
|
||||
use url::Url;
|
||||
|
||||
/// **What it does:** Checks for the presence of `_`, `::` or camel-case words
|
||||
/// outside ticks in documentation.
|
||||
|
|
@ -195,16 +196,26 @@ fn check_doc<'a, Events: Iterator<Item = (usize, pulldown_cmark::Event<'a>)>>(
|
|||
use pulldown_cmark::Tag::*;
|
||||
|
||||
let mut in_code = false;
|
||||
let mut in_link = None;
|
||||
|
||||
for (offset, event) in docs {
|
||||
match event {
|
||||
Start(CodeBlock(_)) | Start(Code) => in_code = true,
|
||||
End(CodeBlock(_)) | End(Code) => in_code = false,
|
||||
Start(_tag) | End(_tag) => (), // We don't care about other tags
|
||||
Start(Link(link, _)) => in_link = Some(link),
|
||||
End(Link(_, _)) => in_link = None,
|
||||
Start(_tag) | End(_tag) => (), // We don't care about other tags
|
||||
Html(_html) | InlineHtml(_html) => (), // HTML is weird, just ignore it
|
||||
SoftBreak => (),
|
||||
HardBreak => (),
|
||||
FootnoteReference(text) | Text(text) => {
|
||||
if Some(&text) == in_link.as_ref() {
|
||||
// Probably a link of the form `<http://example.com>`
|
||||
// Which are represented as a link to "http://example.com" with
|
||||
// text "http://example.com" by pulldown-cmark
|
||||
continue;
|
||||
}
|
||||
|
||||
if !in_code {
|
||||
let index = match spans.binary_search_by(|c| c.0.cmp(&offset)) {
|
||||
Ok(o) => o,
|
||||
|
|
@ -270,6 +281,18 @@ fn check_word(cx: &EarlyContext, word: &str, span: Span) {
|
|||
s != "_" && !s.contains("\\_") && s.contains('_')
|
||||
}
|
||||
|
||||
if let Ok(url) = Url::parse(word) {
|
||||
// try to get around the fact that `foo::bar` parses as a valid URL
|
||||
if !url.cannot_be_a_base() {
|
||||
span_lint(cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"you should put bare URLs between `<`/`>` or make a proper Markdown link");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if has_underscore(word) || word.contains("::") || is_camel_case(word) {
|
||||
span_lint(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use rustc::hir::*;
|
||||
use rustc::hir::map::Node::NodeItem;
|
||||
use rustc::lint::*;
|
||||
use rustc::ty;
|
||||
use syntax::ast::LitKind;
|
||||
use syntax::symbol::InternedString;
|
||||
use utils::paths;
|
||||
use utils::{is_expn_of, match_def_path, match_type, resolve_node, span_lint, walk_ptrs_ty, opt_def_id};
|
||||
|
||||
|
|
@ -50,8 +48,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, fun.hir_id)),
|
||||
match_def_path(cx.tcx, fun_def_id, &paths::FMT_ARGUMENTS_NEWV1),
|
||||
// ensure the format string is `"{..}"` with only one argument and no text
|
||||
check_static_str(cx, &args[0]),
|
||||
check_static_str(&args[0]),
|
||||
// ensure the format argument is `{}` ie. Display with no fancy option
|
||||
// and that the argument is a string
|
||||
check_arg_is_display(cx, &args[1])
|
||||
], {
|
||||
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
|
||||
|
|
@ -69,44 +68,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the slice of format string parts in an `Arguments::new_v1` call.
|
||||
/// Public because it's shared with a lint in print.rs.
|
||||
pub fn get_argument_fmtstr_parts<'a, 'b>(cx: &LateContext<'a, 'b>, expr: &'a Expr) -> Option<Vec<InternedString>> {
|
||||
/// Checks if the expressions matches `&[""]`
|
||||
fn check_static_str(expr: &Expr) -> bool {
|
||||
if_let_chain! {[
|
||||
let ExprBlock(ref block) = expr.node,
|
||||
block.stmts.len() == 1,
|
||||
let StmtDecl(ref decl, _) = block.stmts[0].node,
|
||||
let DeclItem(ref decl) = decl.node,
|
||||
let Some(NodeItem(decl)) = cx.tcx.hir.find(decl.id),
|
||||
decl.name == "__STATIC_FMTSTR",
|
||||
let ItemStatic(_, _, ref expr) = decl.node,
|
||||
let ExprAddrOf(_, ref expr) = cx.tcx.hir.body(*expr).value.node, // &["…", "…", …]
|
||||
let ExprArray(ref exprs) = expr.node,
|
||||
let ExprAddrOf(_, ref expr) = expr.node, // &[""]
|
||||
let ExprArray(ref exprs) = expr.node, // [""]
|
||||
exprs.len() == 1,
|
||||
let ExprLit(ref lit) = exprs[0].node,
|
||||
let LitKind::Str(ref lit, _) = lit.node,
|
||||
], {
|
||||
let mut result = Vec::new();
|
||||
for expr in exprs {
|
||||
if let ExprLit(ref lit) = expr.node {
|
||||
if let LitKind::Str(ref lit, _) = lit.node {
|
||||
result.push(lit.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Some(result);
|
||||
return lit.as_str().is_empty();
|
||||
}}
|
||||
None
|
||||
}
|
||||
|
||||
/// Checks if the expressions matches
|
||||
/// ```rust, ignore
|
||||
/// { static __STATIC_FMTSTR: &'static[&'static str] = &["a", "b", c];
|
||||
/// __STATIC_FMTSTR }
|
||||
/// ```
|
||||
fn check_static_str(cx: &LateContext, expr: &Expr) -> bool {
|
||||
if let Some(expr) = get_argument_fmtstr_parts(cx, expr) {
|
||||
expr.len() == 1 && expr[0].is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if the expressions matches
|
||||
|
|
|
|||
96
clippy_lints/src/identity_conversion.rs
Normal file
96
clippy_lints/src/identity_conversion.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use syntax::ast::NodeId;
|
||||
use utils::{in_macro, match_def_path, match_trait_method, same_tys, snippet, span_lint_and_then};
|
||||
use utils::{opt_def_id, paths, resolve_node};
|
||||
|
||||
/// **What it does:** Checks for always-identical `Into`/`From` conversions.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // format!() returns a `String`
|
||||
/// let s: String = format!("hello").into();
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub IDENTITY_CONVERSION,
|
||||
Warn,
|
||||
"using always-identical `Into`/`From` conversions"
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct IdentityConversion {
|
||||
try_desugar_arm: Vec<NodeId>,
|
||||
}
|
||||
|
||||
impl LintPass for IdentityConversion {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(IDENTITY_CONVERSION)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if in_macro(e.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if Some(&e.id) == self.try_desugar_arm.last() {
|
||||
return;
|
||||
}
|
||||
|
||||
match e.node {
|
||||
ExprMatch(_, ref arms, MatchSource::TryDesugar) => {
|
||||
let e = match arms[0].body.node {
|
||||
ExprRet(Some(ref e)) | ExprBreak(_, Some(ref e)) => e,
|
||||
_ => return,
|
||||
};
|
||||
if let ExprCall(_, ref args) = e.node {
|
||||
self.try_desugar_arm.push(args[0].id);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
ExprMethodCall(ref name, .., ref args) => {
|
||||
if match_trait_method(cx, e, &paths::INTO[..]) && &*name.name.as_str() == "into" {
|
||||
let a = cx.tables.expr_ty(e);
|
||||
let b = cx.tables.expr_ty(&args[0]);
|
||||
if same_tys(cx, a, b) {
|
||||
let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
|
||||
span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| {
|
||||
db.span_suggestion(e.span, "consider removing `.into()`", sugg);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ExprCall(ref path, ref args) => if let ExprPath(ref qpath) = path.node {
|
||||
if let Some(def_id) = opt_def_id(resolve_node(cx, qpath, path.hir_id)) {
|
||||
if match_def_path(cx.tcx, def_id, &paths::FROM_FROM[..]) {
|
||||
let a = cx.tables.expr_ty(e);
|
||||
let b = cx.tables.expr_ty(&args[0]);
|
||||
if same_tys(cx, a, b) {
|
||||
let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
|
||||
let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
|
||||
span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| {
|
||||
db.span_suggestion(e.span, &sugg_msg, sugg);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if Some(&e.id) == self.try_desugar_arm.last() {
|
||||
self.try_desugar_arm.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
143
clippy_lints/src/int_plus_one.rs
Normal file
143
clippy_lints/src/int_plus_one.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
//! lint on blocks unnecessarily using >= with a + 1 or - 1
|
||||
|
||||
use rustc::lint::*;
|
||||
use syntax::ast::*;
|
||||
|
||||
use utils::{span_lint_and_then, snippet_opt};
|
||||
|
||||
/// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
|
||||
///
|
||||
///
|
||||
/// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x >= y + 1
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// x > y
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub INT_PLUS_ONE,
|
||||
Allow,
|
||||
"instead of using x >= y + 1, use x > y"
|
||||
}
|
||||
|
||||
pub struct IntPlusOne;
|
||||
|
||||
impl LintPass for IntPlusOne {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(INT_PLUS_ONE)
|
||||
}
|
||||
}
|
||||
|
||||
// cases:
|
||||
// BinOpKind::Ge
|
||||
// x >= y + 1
|
||||
// x - 1 >= y
|
||||
//
|
||||
// BinOpKind::Le
|
||||
// x + 1 <= y
|
||||
// x <= y - 1
|
||||
|
||||
enum Side {
|
||||
LHS,
|
||||
RHS,
|
||||
}
|
||||
|
||||
impl IntPlusOne {
|
||||
#[allow(cast_sign_loss)]
|
||||
fn check_lit(&self, lit: &Lit, target_value: i128) -> bool {
|
||||
if let LitKind::Int(value, ..) = lit.node {
|
||||
return value == (target_value as u128)
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn check_binop(&self, cx: &EarlyContext, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> {
|
||||
match (binop, &lhs.node, &rhs.node) {
|
||||
// case where `x - 1 >= ...` or `-1 + x >= ...`
|
||||
(BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
|
||||
match (lhskind.node, &lhslhs.node, &lhsrhs.node) {
|
||||
// `-1 + x`
|
||||
(BinOpKind::Add, &ExprKind::Lit(ref lit), _) if self.check_lit(lit, -1) => self.generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS),
|
||||
// `x - 1`
|
||||
(BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if self.check_lit(lit, 1) => self.generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS),
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
// case where `... >= y + 1` or `... >= 1 + y`
|
||||
(BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) if rhskind.node == BinOpKind::Add => {
|
||||
match (&rhslhs.node, &rhsrhs.node) {
|
||||
// `y + 1` and `1 + y`
|
||||
(&ExprKind::Lit(ref lit), _) if self.check_lit(lit, 1) => self.generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS),
|
||||
(_, &ExprKind::Lit(ref lit)) if self.check_lit(lit, 1) => self.generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS),
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
// case where `x + 1 <= ...` or `1 + x <= ...`
|
||||
(BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) if lhskind.node == BinOpKind::Add => {
|
||||
match (&lhslhs.node, &lhsrhs.node) {
|
||||
// `1 + x` and `x + 1`
|
||||
(&ExprKind::Lit(ref lit), _) if self.check_lit(lit, 1) => self.generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS),
|
||||
(_, &ExprKind::Lit(ref lit)) if self.check_lit(lit, 1) => self.generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS),
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
// case where `... >= y - 1` or `... >= -1 + y`
|
||||
(BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
|
||||
match (rhskind.node, &rhslhs.node, &rhsrhs.node) {
|
||||
// `-1 + y`
|
||||
(BinOpKind::Add, &ExprKind::Lit(ref lit), _) if self.check_lit(lit, -1) => self.generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS),
|
||||
// `y - 1`
|
||||
(BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if self.check_lit(lit, 1) => self.generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS),
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_recommendation(&self, cx: &EarlyContext, binop: BinOpKind, node: &Expr, other_side: &Expr, side: Side) -> Option<String> {
|
||||
let binop_string = match binop {
|
||||
BinOpKind::Ge => ">",
|
||||
BinOpKind::Le => "<",
|
||||
_ => return None
|
||||
};
|
||||
if let Some(snippet) = snippet_opt(cx, node.span) {
|
||||
if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
|
||||
let rec = match side {
|
||||
Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
|
||||
Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
|
||||
};
|
||||
return rec;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn emit_warning(&self, cx: &EarlyContext, block: &Expr, recommendation: String) {
|
||||
span_lint_and_then(cx,
|
||||
INT_PLUS_ONE,
|
||||
block.span,
|
||||
"Unnecessary `>= y + 1` or `x - 1 >=`",
|
||||
|db| {
|
||||
db.span_suggestion(block.span, "change `>= y + 1` to `> y` as shown", recommendation);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for IntPlusOne {
|
||||
fn check_expr(&mut self, cx: &EarlyContext, item: &Expr) {
|
||||
if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.node {
|
||||
if let Some(ref rec) = self.check_binop(cx, kind.node, lhs, rhs) {
|
||||
self.emit_warning(cx, item, rec.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
clippy_lints/src/invalid_ref.rs
Normal file
55
clippy_lints/src/invalid_ref.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use rustc::lint::*;
|
||||
use rustc::ty;
|
||||
use rustc::hir::*;
|
||||
use utils::{match_def_path, paths, span_help_and_lint, opt_def_id};
|
||||
|
||||
/// **What it does:** Checks for creation of references to zeroed or uninitialized memory.
|
||||
///
|
||||
/// **Why is this bad?** Creation of null references is undefined behavior.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let bad_ref: &usize = std::mem::zeroed();
|
||||
/// ```
|
||||
|
||||
declare_lint! {
|
||||
pub INVALID_REF,
|
||||
Warn,
|
||||
"creation of invalid reference"
|
||||
}
|
||||
|
||||
const ZERO_REF_SUMMARY: &str = "reference to zeroed memory";
|
||||
const UNINIT_REF_SUMMARY: &str = "reference to uninitialized memory";
|
||||
const HELP: &str = "Creation of a null reference is undefined behavior; see https://doc.rust-lang.org/reference/behavior-considered-undefined.html";
|
||||
|
||||
pub struct InvalidRef;
|
||||
|
||||
impl LintPass for InvalidRef {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(INVALID_REF)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidRef {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_let_chain!{[
|
||||
let ExprCall(ref path, ref args) = expr.node,
|
||||
let ExprPath(ref qpath) = path.node,
|
||||
args.len() == 0,
|
||||
let ty::TyRef(..) = cx.tables.expr_ty(expr).sty,
|
||||
let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id)),
|
||||
], {
|
||||
let msg = if match_def_path(cx.tcx, def_id, &paths::MEM_ZEROED) | match_def_path(cx.tcx, def_id, &paths::INIT) {
|
||||
ZERO_REF_SUMMARY
|
||||
} else if match_def_path(cx.tcx, def_id, &paths::MEM_UNINIT) | match_def_path(cx.tcx, def_id, &paths::UNINIT) {
|
||||
UNINIT_REF_SUMMARY
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
span_help_and_lint(cx, INVALID_REF, expr.span, msg, HELP);
|
||||
}}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -51,6 +51,7 @@ extern crate lazy_static;
|
|||
|
||||
extern crate itertools;
|
||||
extern crate pulldown_cmark;
|
||||
extern crate url;
|
||||
|
||||
macro_rules! declare_restriction_lint {
|
||||
{ pub $name:tt, $description:tt } => {
|
||||
|
|
@ -92,10 +93,13 @@ pub mod eval_order_dependence;
|
|||
pub mod format;
|
||||
pub mod formatting;
|
||||
pub mod functions;
|
||||
pub mod identity_conversion;
|
||||
pub mod identity_op;
|
||||
pub mod if_let_redundant_pattern_matching;
|
||||
pub mod if_not_else;
|
||||
pub mod infinite_iter;
|
||||
pub mod int_plus_one;
|
||||
pub mod invalid_ref;
|
||||
pub mod is_unit_expr;
|
||||
pub mod items_after_statements;
|
||||
pub mod large_enum_variant;
|
||||
|
|
@ -231,7 +235,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
reg.register_early_lint_pass(box enum_variants::EnumVariantNames::new(conf.enum_variant_name_threshold));
|
||||
reg.register_late_lint_pass(box enum_glob_use::EnumGlobUse);
|
||||
reg.register_late_lint_pass(box enum_clike::UnportableVariant);
|
||||
reg.register_late_lint_pass(box bit_mask::BitMask);
|
||||
reg.register_late_lint_pass(box bit_mask::BitMask::new(conf.verbose_bit_mask_threshold));
|
||||
reg.register_late_lint_pass(box ptr::PointerPass);
|
||||
reg.register_late_lint_pass(box needless_bool::NeedlessBool);
|
||||
reg.register_late_lint_pass(box needless_bool::BoolComparison);
|
||||
|
|
@ -299,6 +303,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
reg.register_early_lint_pass(box formatting::Formatting);
|
||||
reg.register_late_lint_pass(box swap::Swap);
|
||||
reg.register_early_lint_pass(box if_not_else::IfNotElse);
|
||||
reg.register_early_lint_pass(box int_plus_one::IntPlusOne);
|
||||
reg.register_late_lint_pass(box overflow_check_conditional::OverflowCheckConditional);
|
||||
reg.register_late_lint_pass(box unused_label::UnusedLabel);
|
||||
reg.register_late_lint_pass(box new_without_default::NewWithoutDefault);
|
||||
|
|
@ -326,6 +331,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
reg.register_late_lint_pass(box use_self::UseSelf);
|
||||
reg.register_late_lint_pass(box bytecount::ByteCount);
|
||||
reg.register_late_lint_pass(box infinite_iter::Pass);
|
||||
reg.register_late_lint_pass(box invalid_ref::InvalidRef);
|
||||
reg.register_late_lint_pass(box identity_conversion::IdentityConversion::default());
|
||||
|
||||
reg.register_lint_group("clippy_restrictions", vec![
|
||||
arithmetic::FLOAT_ARITHMETIC,
|
||||
|
|
@ -342,6 +349,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
enum_variants::STUTTER,
|
||||
if_not_else::IF_NOT_ELSE,
|
||||
infinite_iter::MAYBE_INFINITE_ITER,
|
||||
int_plus_one::INT_PLUS_ONE,
|
||||
invalid_ref::INVALID_REF,
|
||||
items_after_statements::ITEMS_AFTER_STATEMENTS,
|
||||
matches::SINGLE_MATCH_ELSE,
|
||||
mem_forget::MEM_FORGET,
|
||||
|
|
@ -424,6 +433,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
formatting::SUSPICIOUS_ELSE_FORMATTING,
|
||||
functions::NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
functions::TOO_MANY_ARGUMENTS,
|
||||
identity_conversion::IDENTITY_CONVERSION,
|
||||
identity_op::IDENTITY_OP,
|
||||
if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING,
|
||||
infinite_iter::INFINITE_ITER,
|
||||
|
|
@ -446,6 +456,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
loops::FOR_LOOP_OVER_RESULT,
|
||||
loops::ITER_NEXT_LOOP,
|
||||
loops::MANUAL_MEMCPY,
|
||||
loops::MUT_RANGE_BOUND,
|
||||
loops::NEEDLESS_RANGE_LOOP,
|
||||
loops::NEVER_LOOP,
|
||||
loops::REVERSE_RANGE_LOOP,
|
||||
|
|
@ -458,6 +469,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
matches::MATCH_REF_PATS,
|
||||
matches::MATCH_WILD_ERR_ARM,
|
||||
matches::SINGLE_MATCH,
|
||||
methods::CHARS_LAST_CMP,
|
||||
methods::CHARS_NEXT_CMP,
|
||||
methods::CLONE_DOUBLE_REF,
|
||||
methods::CLONE_ON_COPY,
|
||||
|
|
@ -535,6 +547,9 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
transmute::TRANSMUTE_PTR_TO_REF,
|
||||
transmute::USELESS_TRANSMUTE,
|
||||
transmute::WRONG_TRANSMUTE,
|
||||
transmute::TRANSMUTE_INT_TO_CHAR,
|
||||
transmute::TRANSMUTE_INT_TO_BOOL,
|
||||
transmute::TRANSMUTE_INT_TO_FLOAT,
|
||||
types::ABSURD_EXTREME_COMPARISONS,
|
||||
types::BORROWED_BOX,
|
||||
types::BOX_VEC,
|
||||
|
|
|
|||
|
|
@ -104,19 +104,20 @@ fn check_fn_inner<'a, 'tcx>(
|
|||
for typ in &generics.ty_params {
|
||||
for bound in &typ.bounds {
|
||||
if let TraitTyParamBound(ref trait_ref, _) = *bound {
|
||||
let bounds = &trait_ref
|
||||
let params = &trait_ref
|
||||
.trait_ref
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.expect("a path must have at least one segment")
|
||||
.parameters
|
||||
.lifetimes;
|
||||
for bound in bounds {
|
||||
if bound.name != "'static" && !bound.is_elided() {
|
||||
return;
|
||||
.parameters;
|
||||
if let Some(ref params) = *params {
|
||||
for bound in ¶ms.lifetimes {
|
||||
if bound.name.name() != "'static" && !bound.is_elided() {
|
||||
return;
|
||||
}
|
||||
bounds_lts.push(bound);
|
||||
}
|
||||
bounds_lts.push(bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -225,7 +226,7 @@ fn allowed_lts_from(named_lts: &[LifetimeDef]) -> HashSet<RefLt> {
|
|||
let mut allowed_lts = HashSet::new();
|
||||
for lt in named_lts {
|
||||
if lt.bounds.is_empty() {
|
||||
allowed_lts.insert(RefLt::Named(lt.lifetime.name));
|
||||
allowed_lts.insert(RefLt::Named(lt.lifetime.name.name()));
|
||||
}
|
||||
}
|
||||
allowed_lts.insert(RefLt::Unnamed);
|
||||
|
|
@ -235,8 +236,8 @@ fn allowed_lts_from(named_lts: &[LifetimeDef]) -> HashSet<RefLt> {
|
|||
|
||||
fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
|
||||
for lt in bounds_lts {
|
||||
if lt.name != "'static" {
|
||||
vec.push(RefLt::Named(lt.name));
|
||||
if lt.name.name() != "'static" {
|
||||
vec.push(RefLt::Named(lt.name.name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,12 +267,12 @@ impl<'v, 't> RefVisitor<'v, 't> {
|
|||
|
||||
fn record(&mut self, lifetime: &Option<Lifetime>) {
|
||||
if let Some(ref lt) = *lifetime {
|
||||
if lt.name == "'static" {
|
||||
if lt.name.name() == "'static" {
|
||||
self.lts.push(RefLt::Static);
|
||||
} else if lt.is_elided() {
|
||||
self.lts.push(RefLt::Unnamed);
|
||||
} else {
|
||||
self.lts.push(RefLt::Named(lt.name));
|
||||
self.lts.push(RefLt::Named(lt.name.name()));
|
||||
}
|
||||
} else {
|
||||
self.lts.push(RefLt::Unnamed);
|
||||
|
|
@ -287,23 +288,24 @@ impl<'v, 't> RefVisitor<'v, 't> {
|
|||
}
|
||||
|
||||
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) {
|
||||
let last_path_segment = &last_path_segment(qpath).parameters;
|
||||
if !last_path_segment.parenthesized && last_path_segment.lifetimes.is_empty() {
|
||||
let hir_id = self.cx.tcx.hir.node_to_hir_id(ty.id);
|
||||
match self.cx.tables.qpath_def(qpath, hir_id) {
|
||||
Def::TyAlias(def_id) | Def::Struct(def_id) => {
|
||||
let generics = self.cx.tcx.generics_of(def_id);
|
||||
for _ in generics.regions.as_slice() {
|
||||
self.record(&None);
|
||||
}
|
||||
},
|
||||
Def::Trait(def_id) => {
|
||||
let trait_def = self.cx.tcx.trait_def(def_id);
|
||||
for _ in &self.cx.tcx.generics_of(trait_def.def_id).regions {
|
||||
self.record(&None);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
if let Some(ref last_path_segment) = last_path_segment(qpath).parameters {
|
||||
if !last_path_segment.parenthesized && last_path_segment.lifetimes.is_empty() {
|
||||
let hir_id = self.cx.tcx.hir.node_to_hir_id(ty.id);
|
||||
match self.cx.tables.qpath_def(qpath, hir_id) {
|
||||
Def::TyAlias(def_id) | Def::Struct(def_id) => {
|
||||
let generics = self.cx.tcx.generics_of(def_id);
|
||||
for _ in generics.regions.as_slice() {
|
||||
self.record(&None);
|
||||
}
|
||||
},
|
||||
Def::Trait(def_id) => {
|
||||
let trait_def = self.cx.tcx.trait_def(def_id);
|
||||
for _ in &self.cx.tcx.generics_of(trait_def.def_id).regions {
|
||||
self.record(&None);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -396,7 +398,7 @@ struct LifetimeChecker {
|
|||
impl<'tcx> Visitor<'tcx> for LifetimeChecker {
|
||||
// for lifetimes as parameters of generics
|
||||
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
|
||||
self.map.remove(&lifetime.name);
|
||||
self.map.remove(&lifetime.name.name());
|
||||
}
|
||||
|
||||
fn visit_lifetime_def(&mut self, _: &'tcx LifetimeDef) {
|
||||
|
|
@ -415,7 +417,7 @@ fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx
|
|||
let hs = generics
|
||||
.lifetimes
|
||||
.iter()
|
||||
.map(|lt| (lt.lifetime.name, lt.lifetime.span))
|
||||
.map(|lt| (lt.lifetime.name.name(), lt.lifetime.span))
|
||||
.collect();
|
||||
let mut checker = LifetimeChecker { map: hs };
|
||||
|
||||
|
|
@ -434,7 +436,7 @@ struct BodyLifetimeChecker {
|
|||
impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
|
||||
// for lifetimes as parameters of generics
|
||||
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
|
||||
if lifetime.name != keywords::Invalid.name() && lifetime.name != "'static" {
|
||||
if lifetime.name.name() != keywords::Invalid.name() && lifetime.name.name() != "'static" {
|
||||
self.lifetimes_used_in_body = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,12 +279,19 @@ impl LiteralDigitGrouping {
|
|||
let fractional_part = &parts[1].chars().rev().collect::<String>();
|
||||
let _ = Self::do_lint(fractional_part)
|
||||
.map(|fractional_group_size| {
|
||||
let consistent = Self::parts_consistent(integral_group_size, fractional_group_size, parts[0].len(), parts[1].len());
|
||||
let consistent = Self::parts_consistent(integral_group_size,
|
||||
fractional_group_size,
|
||||
parts[0].len(),
|
||||
parts[1].len());
|
||||
if !consistent {
|
||||
WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(), cx, &lit.span);
|
||||
WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
|
||||
cx,
|
||||
&lit.span);
|
||||
}
|
||||
})
|
||||
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
|
||||
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
|
||||
cx,
|
||||
&lit.span));
|
||||
}
|
||||
})
|
||||
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
|
||||
|
|
@ -332,7 +339,8 @@ impl LiteralDigitGrouping {
|
|||
.windows(2)
|
||||
.all(|ps| ps[1] - ps[0] == group_size + 1)
|
||||
// number of digits to the left of the last group cannot be bigger than group size.
|
||||
&& (digits.len() - underscore_positions.last().expect("there's at least one element") <= group_size + 1);
|
||||
&& (digits.len() - underscore_positions.last()
|
||||
.expect("there's at least one element") <= group_size + 1);
|
||||
|
||||
if !consistent {
|
||||
return Err(WarningType::InconsistentDigitGrouping);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,22 @@ use itertools::Itertools;
|
|||
use reexport::*;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::def_id;
|
||||
use rustc::hir::intravisit::{walk_block, walk_decl, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
|
||||
use rustc::hir::map::Node::{NodeBlock, NodeExpr, NodeStmt};
|
||||
use rustc::lint::*;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::middle::region;
|
||||
// use rustc::middle::region::CodeExtent;
|
||||
use rustc::middle::expr_use_visitor::*;
|
||||
use rustc::middle::mem_categorization::Categorization;
|
||||
use rustc::middle::mem_categorization::cmt;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::subst::{Subst, Substs};
|
||||
use rustc_const_eval::ConstContext;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
use utils::sugg;
|
||||
use utils::const_to_u64;
|
||||
|
||||
|
|
@ -328,6 +334,14 @@ declare_lint! {
|
|||
"any loop that will always `break` or `return`"
|
||||
}
|
||||
|
||||
/// TODO: add documentation
|
||||
|
||||
declare_lint! {
|
||||
pub MUT_RANGE_BOUND,
|
||||
Warn,
|
||||
"for loop over a range where one of the bounds is a mutable variable"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Pass;
|
||||
|
||||
|
|
@ -348,7 +362,8 @@ impl LintPass for Pass {
|
|||
EMPTY_LOOP,
|
||||
WHILE_LET_ON_ITERATOR,
|
||||
FOR_KV_MAP,
|
||||
NEVER_LOOP
|
||||
NEVER_LOOP,
|
||||
MUT_RANGE_BOUND
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -363,7 +378,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
match expr.node {
|
||||
ExprWhile(_, ref block, _) |
|
||||
ExprLoop(ref block, _, _) => {
|
||||
if never_loop(block, &expr.id) {
|
||||
if never_loop(block, expr.id) {
|
||||
span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops");
|
||||
}
|
||||
},
|
||||
|
|
@ -470,11 +485,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
fn never_loop(block: &Block, id: &NodeId) -> bool {
|
||||
!contains_continue_block(block, id) && loop_exit_block(block)
|
||||
fn never_loop(block: &Block, id: NodeId) -> bool {
|
||||
!contains_continue_block(block, Some(id)) && loop_exit_block(block, &mut vec![id])
|
||||
}
|
||||
|
||||
fn contains_continue_block(block: &Block, dest: &NodeId) -> bool {
|
||||
fn contains_continue_block(block: &Block, dest: Option<NodeId>) -> bool {
|
||||
block.stmts.iter().any(|e| contains_continue_stmt(e, dest)) ||
|
||||
block.expr.as_ref().map_or(
|
||||
false,
|
||||
|
|
@ -482,7 +497,7 @@ fn contains_continue_block(block: &Block, dest: &NodeId) -> bool {
|
|||
)
|
||||
}
|
||||
|
||||
fn contains_continue_stmt(stmt: &Stmt, dest: &NodeId) -> bool {
|
||||
fn contains_continue_stmt(stmt: &Stmt, dest: Option<NodeId>) -> bool {
|
||||
match stmt.node {
|
||||
StmtSemi(ref e, _) |
|
||||
StmtExpr(ref e, _) => contains_continue_expr(e, dest),
|
||||
|
|
@ -490,7 +505,7 @@ fn contains_continue_stmt(stmt: &Stmt, dest: &NodeId) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn contains_continue_decl(decl: &Decl, dest: &NodeId) -> bool {
|
||||
fn contains_continue_decl(decl: &Decl, dest: Option<NodeId>) -> bool {
|
||||
match decl.node {
|
||||
DeclLocal(ref local) => {
|
||||
local.init.as_ref().map_or(
|
||||
|
|
@ -502,7 +517,7 @@ fn contains_continue_decl(decl: &Decl, dest: &NodeId) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
|
||||
fn contains_continue_expr(expr: &Expr, dest: Option<NodeId>) -> bool {
|
||||
match expr.node {
|
||||
ExprRet(Some(ref e)) |
|
||||
ExprBox(ref e) |
|
||||
|
|
@ -540,31 +555,32 @@ fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
|
|||
|e| contains_continue_expr(e, dest),
|
||||
)
|
||||
},
|
||||
ExprAgain(d) => d.target_id.opt_id().map_or(false, |id| id == *dest),
|
||||
ExprAgain(d) => dest.map_or(true, |dest| d.target_id.opt_id().map_or(false, |id| id == dest)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn loop_exit_block(block: &Block) -> bool {
|
||||
block.stmts.iter().any(|e| loop_exit_stmt(e)) || block.expr.as_ref().map_or(false, |e| loop_exit_expr(e))
|
||||
fn loop_exit_block(block: &Block, loops: &mut Vec<NodeId>) -> bool {
|
||||
block.stmts.iter().take_while(|s| !contains_continue_stmt(s, None)).any(|s| loop_exit_stmt(s, loops))
|
||||
|| block.expr.as_ref().map_or(false, |e| loop_exit_expr(e, loops))
|
||||
}
|
||||
|
||||
fn loop_exit_stmt(stmt: &Stmt) -> bool {
|
||||
fn loop_exit_stmt(stmt: &Stmt, loops: &mut Vec<NodeId>) -> bool {
|
||||
match stmt.node {
|
||||
StmtSemi(ref e, _) |
|
||||
StmtExpr(ref e, _) => loop_exit_expr(e),
|
||||
StmtDecl(ref d, _) => loop_exit_decl(d),
|
||||
StmtExpr(ref e, _) => loop_exit_expr(e, loops),
|
||||
StmtDecl(ref d, _) => loop_exit_decl(d, loops),
|
||||
}
|
||||
}
|
||||
|
||||
fn loop_exit_decl(decl: &Decl) -> bool {
|
||||
fn loop_exit_decl(decl: &Decl, loops: &mut Vec<NodeId>) -> bool {
|
||||
match decl.node {
|
||||
DeclLocal(ref local) => local.init.as_ref().map_or(false, |e| loop_exit_expr(e)),
|
||||
DeclLocal(ref local) => local.init.as_ref().map_or(false, |e| loop_exit_expr(e, loops)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn loop_exit_expr(expr: &Expr) -> bool {
|
||||
fn loop_exit_expr(expr: &Expr, loops: &mut Vec<NodeId>) -> bool {
|
||||
match expr.node {
|
||||
ExprBox(ref e) |
|
||||
ExprUnary(_, ref e) |
|
||||
|
|
@ -573,22 +589,34 @@ fn loop_exit_expr(expr: &Expr) -> bool {
|
|||
ExprField(ref e, _) |
|
||||
ExprTupField(ref e, _) |
|
||||
ExprAddrOf(_, ref e) |
|
||||
ExprRepeat(ref e, _) => loop_exit_expr(e),
|
||||
ExprRepeat(ref e, _) => loop_exit_expr(e, loops),
|
||||
ExprArray(ref es) |
|
||||
ExprMethodCall(_, _, ref es) |
|
||||
ExprTup(ref es) => es.iter().any(|e| loop_exit_expr(e)),
|
||||
ExprCall(ref e, ref es) => loop_exit_expr(e) || es.iter().any(|e| loop_exit_expr(e)),
|
||||
ExprTup(ref es) => es.iter().any(|e| loop_exit_expr(e, loops)),
|
||||
ExprCall(ref e, ref es) => loop_exit_expr(e, loops) || es.iter().any(|e| loop_exit_expr(e, loops)),
|
||||
ExprBinary(_, ref e1, ref e2) |
|
||||
ExprAssign(ref e1, ref e2) |
|
||||
ExprAssignOp(_, ref e1, ref e2) |
|
||||
ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| loop_exit_expr(e)),
|
||||
ExprIf(ref e, ref e2, ref e3) => {
|
||||
loop_exit_expr(e) || e3.as_ref().map_or(false, |e| loop_exit_expr(e)) && loop_exit_expr(e2)
|
||||
ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| loop_exit_expr(e, loops)),
|
||||
ExprIf(ref e, ref e2, ref e3) => loop_exit_expr(e, loops)
|
||||
|| e3.as_ref().map_or(false, |e3| loop_exit_expr(e3, loops)) && loop_exit_expr(e2, loops),
|
||||
ExprLoop(ref b, _, _) => {
|
||||
loops.push(expr.id);
|
||||
let val = loop_exit_block(b, loops);
|
||||
loops.pop();
|
||||
val
|
||||
},
|
||||
ExprWhile(ref e, ref b, _) => loop_exit_expr(e) || loop_exit_block(b),
|
||||
ExprMatch(ref e, ref arms, _) => loop_exit_expr(e) || arms.iter().all(|a| loop_exit_expr(&a.body)),
|
||||
ExprBlock(ref b) => loop_exit_block(b),
|
||||
ExprBreak(_, _) | ExprAgain(_) | ExprRet(_) => true,
|
||||
ExprWhile(ref e, ref b, _) => {
|
||||
loops.push(expr.id);
|
||||
let val = loop_exit_expr(e, loops) || loop_exit_block(b, loops);
|
||||
loops.pop();
|
||||
val
|
||||
},
|
||||
ExprMatch(ref e, ref arms, _) => loop_exit_expr(e, loops) || arms.iter().all(|a| loop_exit_expr(&a.body, loops)),
|
||||
ExprBlock(ref b) => loop_exit_block(b, loops),
|
||||
ExprAgain(d) => d.target_id.opt_id().map_or(false, |id| loops.iter().skip(1).all(|&id2| id != id2)),
|
||||
ExprBreak(d, _) => d.target_id.opt_id().map_or(false, |id| loops[0] == id),
|
||||
ExprRet(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -605,6 +633,7 @@ fn check_for_loop<'a, 'tcx>(
|
|||
check_for_loop_arg(cx, pat, arg, expr);
|
||||
check_for_loop_explicit_counter(cx, arg, body, expr);
|
||||
check_for_loop_over_map_kv(cx, pat, arg, body, expr);
|
||||
check_for_mut_range_bound(cx, arg, body);
|
||||
detect_manual_memcpy(cx, pat, arg, body, expr);
|
||||
}
|
||||
|
||||
|
|
@ -1294,6 +1323,102 @@ fn check_for_loop_over_map_kv<'a, 'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
struct MutateDelegate {
|
||||
node_id_low: Option<NodeId>,
|
||||
node_id_high: Option<NodeId>,
|
||||
span_low: Option<Span>,
|
||||
span_high: Option<Span>,
|
||||
}
|
||||
|
||||
impl<'tcx> Delegate<'tcx> for MutateDelegate {
|
||||
fn consume(&mut self, _: NodeId, _: Span, _: cmt<'tcx>, _: ConsumeMode) {
|
||||
}
|
||||
|
||||
fn matched_pat(&mut self, _: &Pat, _: cmt<'tcx>, _: MatchMode) {
|
||||
}
|
||||
|
||||
fn consume_pat(&mut self, _: &Pat, _: cmt<'tcx>, _: ConsumeMode) {
|
||||
}
|
||||
|
||||
fn borrow(&mut self, _: NodeId, sp: Span, cmt: cmt<'tcx>, _: ty::Region, bk: ty::BorrowKind, _: LoanCause) {
|
||||
if let ty::BorrowKind::MutBorrow = bk {
|
||||
if let Categorization::Local(id) = cmt.cat {
|
||||
if Some(id) == self.node_id_low {
|
||||
self.span_low = Some(sp)
|
||||
}
|
||||
if Some(id) == self.node_id_high {
|
||||
self.span_high = Some(sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mutate(&mut self, _: NodeId, sp: Span, cmt: cmt<'tcx>, _: MutateMode) {
|
||||
if let Categorization::Local(id) = cmt.cat {
|
||||
if Some(id) == self.node_id_low {
|
||||
self.span_low = Some(sp)
|
||||
}
|
||||
if Some(id) == self.node_id_high {
|
||||
self.span_high = Some(sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decl_without_init(&mut self, _: NodeId, _: Span) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MutateDelegate {
|
||||
fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
|
||||
(self.span_low, self.span_high)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_mut_range_bound(cx: &LateContext, arg: &Expr, body: &Expr) {
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(arg) {
|
||||
let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
|
||||
if mut_ids[0].is_some() || mut_ids[1].is_some() {
|
||||
let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
|
||||
mut_warn_with_span(cx, span_low);
|
||||
mut_warn_with_span(cx, span_high);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mut_warn_with_span(cx: &LateContext, span: Option<Span>) {
|
||||
if let Some(sp) = span {
|
||||
span_lint(cx, MUT_RANGE_BOUND, sp, "attempt to mutate range bound within loop; note that the range of the loop is unchanged");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_mutability(cx: &LateContext, bound: &Expr) -> Option<NodeId> {
|
||||
if_let_chain! {[
|
||||
let ExprPath(ref qpath) = bound.node,
|
||||
let QPath::Resolved(None, _) = *qpath,
|
||||
], {
|
||||
let def = cx.tables.qpath_def(qpath, bound.hir_id);
|
||||
if let Def::Local(node_id) = def {
|
||||
let node_str = cx.tcx.hir.get(node_id);
|
||||
if_let_chain! {[
|
||||
let map::Node::NodeBinding(pat) = node_str,
|
||||
let PatKind::Binding(bind_ann, _, _, _) = pat.node,
|
||||
let BindingAnnotation::Mutable = bind_ann,
|
||||
], {
|
||||
return Some(node_id);
|
||||
}}
|
||||
}
|
||||
}}
|
||||
None
|
||||
}
|
||||
|
||||
fn check_for_mutation(cx: &LateContext, body: &Expr, bound_ids: &[Option<NodeId>]) -> (Option<Span>, Option<Span>) {
|
||||
let mut delegate = MutateDelegate { node_id_low: bound_ids[0], node_id_high: bound_ids[1], span_low: None, span_high: None };
|
||||
let def_id = def_id::DefId::local(body.hir_id.owner);
|
||||
let region_scope_tree = &cx.tcx.region_scope_tree(def_id);
|
||||
ExprUseVisitor::new(&mut delegate, cx.tcx, cx.param_env, region_scope_tree, cx.tables).walk_expr(body);
|
||||
delegate.mutation_span()
|
||||
}
|
||||
|
||||
/// Return true if the pattern is a `PatWild` or an ident prefixed with `'_'`.
|
||||
fn pat_is_wild<'tcx>(pat: &'tcx PatKind, body: &'tcx Expr) -> bool {
|
||||
match *pat {
|
||||
|
|
@ -1492,7 +1617,7 @@ fn is_ref_iterable_type(cx: &LateContext, e: &Expr) -> bool {
|
|||
fn is_iterable_array(ty: Ty) -> bool {
|
||||
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
|
||||
match ty.sty {
|
||||
ty::TyArray(_, n) => (0...32).contains(const_to_u64(n)),
|
||||
ty::TyArray(_, n) => (0..=32).contains(const_to_u64(n)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,8 @@ fn expr_eq_name(expr: &Expr, id: ast::Name) -> bool {
|
|||
let arg_segment = [
|
||||
PathSegment {
|
||||
name: id,
|
||||
parameters: PathParameters::none(),
|
||||
parameters: None,
|
||||
infer_types: true,
|
||||
},
|
||||
];
|
||||
!path.is_global() && path.segments[..] == arg_segment
|
||||
|
|
|
|||
|
|
@ -1617,11 +1617,18 @@ fn is_as_ref_or_mut_trait(ty: &hir::Ty, self_ty: &hir::Ty, generics: &hir::Gener
|
|||
match_path(path, name) &&
|
||||
path.segments
|
||||
.last()
|
||||
.map_or(false, |s| if s.parameters.parenthesized {
|
||||
false
|
||||
} else {
|
||||
s.parameters.types.len() == 1 &&
|
||||
(is_self_ty(&s.parameters.types[0]) || is_ty(&*s.parameters.types[0], self_ty))
|
||||
.map_or(false, |s| {
|
||||
if let Some(ref params) = s.parameters {
|
||||
if params.parenthesized {
|
||||
false
|
||||
} else {
|
||||
params.types.len() == 1 &&
|
||||
(is_self_ty(¶ms.types[0])
|
||||
|| is_ty(&*params.types[0], self_ty))
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
|
|
|
|||
|
|
@ -15,16 +15,7 @@
|
|||
// *rustc*'s
|
||||
// [`missing_doc`].
|
||||
//
|
||||
// [`missing_doc`]:
|
||||
// https://github.
|
||||
// com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// rs#L246
|
||||
// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
|
||||
//
|
||||
|
||||
use rustc::hir;
|
||||
|
|
|
|||
|
|
@ -144,7 +144,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
|||
let TyPath(QPath::Resolved(_, ref path)) = input.node,
|
||||
let Some(elem_ty) = path.segments.iter()
|
||||
.find(|seg| seg.name == "Vec")
|
||||
.map(|ps| &ps.parameters.types[0]),
|
||||
.and_then(|ps| ps.parameters.as_ref())
|
||||
.map(|params| ¶ms.types[0]),
|
||||
], {
|
||||
let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
|
||||
db.span_suggestion(input.span,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use rustc::hir::*;
|
||||
use rustc::hir::map::Node::{NodeImplItem, NodeItem};
|
||||
use rustc::lint::*;
|
||||
use utils::{paths, opt_def_id};
|
||||
use syntax::ast::LitKind;
|
||||
use syntax::symbol::InternedString;
|
||||
use utils::{is_expn_of, match_def_path, match_path, resolve_node, span_lint};
|
||||
use format::get_argument_fmtstr_parts;
|
||||
use utils::{paths, opt_def_id};
|
||||
|
||||
/// **What it does:** This lint warns when you using `print!()` with a format
|
||||
/// string that
|
||||
|
|
@ -103,15 +104,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
let ExprTup(ref args) = args.node,
|
||||
|
||||
// collect the format string parts and check the last one
|
||||
let Some(fmtstrs) = get_argument_fmtstr_parts(cx, &args_args[0]),
|
||||
let Some(last_str) = fmtstrs.last(),
|
||||
let Some('\n') = last_str.chars().last(),
|
||||
let Some((fmtstr, fmtlen)) = get_argument_fmtstr_parts(&args_args[0]),
|
||||
let Some('\n') = fmtstr.chars().last(),
|
||||
|
||||
// "foo{}bar" is made into two strings + one argument,
|
||||
// if the format string starts with `{}` (eg. "{}foo"),
|
||||
// the string array is prepended an empty string "".
|
||||
// We only want to check the last string after any `{}`:
|
||||
args.len() < fmtstrs.len(),
|
||||
args.len() < fmtlen,
|
||||
], {
|
||||
span_lint(cx, PRINT_WITH_NEWLINE, span,
|
||||
"using `print!()` with a format string that ends in a \
|
||||
|
|
@ -124,7 +124,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
else if args.len() == 2 && match_def_path(cx.tcx, fun_id, &paths::FMT_ARGUMENTV1_NEW) {
|
||||
if let ExprPath(ref qpath) = args[1].node {
|
||||
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, args[1].hir_id)) {
|
||||
if match_def_path(cx.tcx, def_id, &paths::DEBUG_FMT_METHOD) && !is_in_debug_impl(cx, expr) && is_expn_of(expr.span, "panic").is_none() {
|
||||
if match_def_path(cx.tcx, def_id, &paths::DEBUG_FMT_METHOD)
|
||||
&& !is_in_debug_impl(cx, expr) && is_expn_of(expr.span, "panic").is_none() {
|
||||
span_lint(cx, USE_DEBUG, args[0].span, "use of `Debug`-based formatting");
|
||||
}
|
||||
}
|
||||
|
|
@ -149,3 +150,17 @@ fn is_in_debug_impl(cx: &LateContext, expr: &Expr) -> bool {
|
|||
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns the slice of format string parts in an `Arguments::new_v1` call.
|
||||
fn get_argument_fmtstr_parts(expr: &Expr) -> Option<(InternedString, usize)> {
|
||||
if_let_chain! {[
|
||||
let ExprAddrOf(_, ref expr) = expr.node, // &["…", "…", …]
|
||||
let ExprArray(ref exprs) = expr.node,
|
||||
let Some(expr) = exprs.last(),
|
||||
let ExprLit(ref lit) = expr.node,
|
||||
let LitKind::Str(ref lit, _) = lit.node,
|
||||
], {
|
||||
return Some((lit.as_str(), exprs.len()));
|
||||
}}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! Checks for usage of `&Vec[_]` and `&String`.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc::hir::map::NodeItem;
|
||||
|
|
@ -158,49 +159,53 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId, opt_body_id: Option<
|
|||
let mut ty_snippet = None;
|
||||
if_let_chain!([
|
||||
let TyPath(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).node,
|
||||
let Some(&PathSegment{ref parameters, ..}) = path.segments.last(),
|
||||
let Some(&PathSegment{parameters: Some(ref parameters), ..}) = path.segments.last(),
|
||||
parameters.types.len() == 1,
|
||||
], {
|
||||
ty_snippet = snippet_opt(cx, parameters.types[0].span);
|
||||
});
|
||||
let spans = get_spans(cx, opt_body_id, idx, "to_owned");
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PTR_ARG,
|
||||
arg.span,
|
||||
"writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
|
||||
with non-Vec-based slices.",
|
||||
|db| {
|
||||
if let Some(ref snippet) = ty_snippet {
|
||||
if let Ok(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PTR_ARG,
|
||||
arg.span,
|
||||
"writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
|
||||
with non-Vec-based slices.",
|
||||
|db| {
|
||||
if let Some(ref snippet) = ty_snippet {
|
||||
db.span_suggestion(arg.span,
|
||||
"change this to",
|
||||
format!("&[{}]", snippet));
|
||||
}
|
||||
for (clonespan, suggestion) in spans {
|
||||
db.span_suggestion(clonespan,
|
||||
&snippet_opt(cx, clonespan).map_or("change the call to".into(),
|
||||
|x| Cow::Owned(format!("change `{}` to", x))),
|
||||
suggestion.into());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if match_type(cx, ty, &paths::STRING) {
|
||||
if let Ok(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PTR_ARG,
|
||||
arg.span,
|
||||
"writing `&String` instead of `&str` involves a new object where a slice will do.",
|
||||
|db| {
|
||||
db.span_suggestion(arg.span,
|
||||
"change this to",
|
||||
format!("&[{}]", snippet));
|
||||
"&str".into());
|
||||
for (clonespan, suggestion) in spans {
|
||||
db.span_suggestion_short(clonespan,
|
||||
&snippet_opt(cx, clonespan).map_or("change the call to".into(),
|
||||
|x| Cow::Owned(format!("change `{}` to", x))),
|
||||
suggestion.into());
|
||||
}
|
||||
}
|
||||
for (clonespan, suggestion) in spans {
|
||||
db.span_suggestion(clonespan,
|
||||
"change the `.clone()` to",
|
||||
suggestion);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else if match_type(cx, ty, &paths::STRING) {
|
||||
let spans = get_spans(cx, opt_body_id, idx, "to_string");
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PTR_ARG,
|
||||
arg.span,
|
||||
"writing `&String` instead of `&str` involves a new object where a slice will do.",
|
||||
|db| {
|
||||
db.span_suggestion(arg.span,
|
||||
"change this to",
|
||||
"&str".into());
|
||||
for (clonespan, suggestion) in spans {
|
||||
db.span_suggestion_short(clonespan,
|
||||
"change the `.clone` to ",
|
||||
suggestion);
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -229,38 +234,50 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId, opt_body_id: Option<
|
|||
}
|
||||
}
|
||||
|
||||
fn get_spans(cx: &LateContext, opt_body_id: Option<BodyId>, idx: usize, fn_name: &'static str) -> Vec<(Span, String)> {
|
||||
fn get_spans(cx: &LateContext, opt_body_id: Option<BodyId>, idx: usize, replacements: &'static [(&'static str, &'static str)]) -> Result<Vec<(Span, Cow<'static, str>)>, ()> {
|
||||
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir.body(id)) {
|
||||
get_binding_name(&body.arguments[idx]).map_or_else(Vec::new,
|
||||
|name| extract_clone_suggestions(cx, name, fn_name, body))
|
||||
get_binding_name(&body.arguments[idx]).map_or_else(|| Ok(vec![]),
|
||||
|name| extract_clone_suggestions(cx, name, replacements, body))
|
||||
} else {
|
||||
vec![]
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_clone_suggestions<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, name: Name, fn_name: &'static str, body: &'tcx Body) -> Vec<(Span, String)> {
|
||||
fn extract_clone_suggestions<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, name: Name, replace: &'static [(&'static str, &'static str)], body: &'tcx Body) -> Result<Vec<(Span, Cow<'static, str>)>, ()> {
|
||||
let mut visitor = PtrCloneVisitor {
|
||||
cx,
|
||||
name,
|
||||
fn_name,
|
||||
spans: vec![]
|
||||
replace,
|
||||
spans: vec![],
|
||||
abort: false,
|
||||
};
|
||||
visitor.visit_body(body);
|
||||
visitor.spans
|
||||
if visitor.abort { Err(()) } else { Ok(visitor.spans) }
|
||||
}
|
||||
|
||||
struct PtrCloneVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
name: Name,
|
||||
fn_name: &'static str,
|
||||
spans: Vec<(Span, String)>,
|
||||
replace: &'static [(&'static str, &'static str)],
|
||||
spans: Vec<(Span, Cow<'static, str>)>,
|
||||
abort: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if self.abort { return; }
|
||||
if let ExprMethodCall(ref seg, _, ref args) = expr.node {
|
||||
if args.len() == 1 && match_var(&args[0], self.name) && seg.name == "clone" {
|
||||
self.spans.push((expr.span, format!("{}.{}()", snippet(self.cx, args[0].span, "_"), self.fn_name)));
|
||||
if args.len() == 1 && match_var(&args[0], self.name) {
|
||||
if seg.name == "capacity" {
|
||||
self.abort = true;
|
||||
return;
|
||||
}
|
||||
for &(fn_name, suffix) in self.replace {
|
||||
if seg.name == fn_name {
|
||||
self.spans.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use rustc::lint::*;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::hir::*;
|
||||
use std::borrow::Cow;
|
||||
use syntax::ast;
|
||||
use utils::{last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_then};
|
||||
use utils::{sugg, opt_def_id};
|
||||
|
||||
|
|
@ -76,11 +78,73 @@ declare_lint! {
|
|||
"transmutes from a pointer to a reference type"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for transmutes from an integer to a `char`.
|
||||
///
|
||||
/// **Why is this bad?** Not every integer is a unicode scalar value.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _: char = std::mem::transmute(x); // where x: u32
|
||||
/// // should be:
|
||||
/// let _: Option<char> = std::char::from_u32(x);
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub TRANSMUTE_INT_TO_CHAR,
|
||||
Warn,
|
||||
"transmutes from an integer to a `char`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for transmutes from an integer to a `bool`.
|
||||
///
|
||||
/// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _: bool = std::mem::transmute(x); // where x: u8
|
||||
/// // should be:
|
||||
/// let _: bool = x != 0;
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub TRANSMUTE_INT_TO_BOOL,
|
||||
Warn,
|
||||
"transmutes from an integer to a `bool`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for transmutes from an integer to a float.
|
||||
///
|
||||
/// **Why is this bad?** This might result in an invalid in-memory representation of a float.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _: f32 = std::mem::transmute(x); // where x: u32
|
||||
/// // should be:
|
||||
/// let _: f32 = f32::from_bits(x);
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub TRANSMUTE_INT_TO_FLOAT,
|
||||
Warn,
|
||||
"transmutes from an integer to a float"
|
||||
}
|
||||
|
||||
pub struct Transmute;
|
||||
|
||||
impl LintPass for Transmute {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, USELESS_TRANSMUTE, WRONG_TRANSMUTE)
|
||||
lint_array!(
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
TRANSMUTE_PTR_TO_REF,
|
||||
USELESS_TRANSMUTE,
|
||||
WRONG_TRANSMUTE,
|
||||
TRANSMUTE_INT_TO_CHAR,
|
||||
TRANSMUTE_INT_TO_BOOL,
|
||||
TRANSMUTE_INT_TO_FLOAT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +241,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
|||
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
|
||||
},
|
||||
),
|
||||
(&ty::TyInt(ast::IntTy::I32), &ty::TyChar) |
|
||||
(&ty::TyUint(ast::UintTy::U32), &ty::TyChar) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_CHAR,
|
||||
e.span,
|
||||
&format!("transmute from a `{}` to a `char`", from_ty),
|
||||
|db| {
|
||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
||||
let arg = if let ty::TyInt(_) = from_ty.sty {
|
||||
arg.as_ty(ty::TyUint(ast::UintTy::U32))
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
db.span_suggestion(e.span, "consider using", format!("std::char::from_u32({})", arg.to_string()));
|
||||
}
|
||||
),
|
||||
(&ty::TyInt(ast::IntTy::I8), &ty::TyBool) |
|
||||
(&ty::TyUint(ast::UintTy::U8), &ty::TyBool) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_BOOL,
|
||||
e.span,
|
||||
&format!("transmute from a `{}` to a `bool`", from_ty),
|
||||
|db| {
|
||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
||||
let zero = sugg::Sugg::NonParen(Cow::from("0"));
|
||||
db.span_suggestion(e.span, "consider using", sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string());
|
||||
}
|
||||
),
|
||||
(&ty::TyInt(_), &ty::TyFloat(_)) |
|
||||
(&ty::TyUint(_), &ty::TyFloat(_)) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_FLOAT,
|
||||
e.span,
|
||||
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|
||||
|db| {
|
||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
||||
let arg = if let ty::TyInt(int_ty) = from_ty.sty {
|
||||
arg.as_ty(format!("u{}", int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string())))
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
db.span_suggestion(e.span, "consider using", format!("{}::from_bits({})", to_ty, arg.to_string()));
|
||||
}
|
||||
),
|
||||
_ => return,
|
||||
};
|
||||
}
|
||||
|
|
@ -194,8 +302,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
|||
fn get_type_snippet(cx: &LateContext, path: &QPath, to_rty: Ty) -> String {
|
||||
let seg = last_path_segment(path);
|
||||
if_let_chain!{[
|
||||
!seg.parameters.parenthesized,
|
||||
let Some(to_ty) = seg.parameters.types.get(1),
|
||||
let Some(ref params) = seg.parameters,
|
||||
!params.parenthesized,
|
||||
let Some(to_ty) = params.types.get(1),
|
||||
let TyRptr(_, ref to_ty) = to_ty.node,
|
||||
], {
|
||||
return snippet(cx, to_ty.ty.span, &to_rty.to_string()).to_string();
|
||||
|
|
|
|||
|
|
@ -154,8 +154,9 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
|
|||
if Some(def_id) == cx.tcx.lang_items().owned_box() {
|
||||
let last = last_path_segment(qpath);
|
||||
if_let_chain! {[
|
||||
!last.parameters.parenthesized,
|
||||
let Some(vec) = last.parameters.types.get(0),
|
||||
let Some(ref params) = last.parameters,
|
||||
!params.parenthesized,
|
||||
let Some(vec) = params.types.get(0),
|
||||
let TyPath(ref qpath) = vec.node,
|
||||
let Some(did) = opt_def_id(cx.tables.qpath_def(qpath, cx.tcx.hir.node_to_hir_id(vec.id))),
|
||||
match_def_path(cx.tcx, did, &paths::VEC),
|
||||
|
|
@ -183,67 +184,32 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
|
|||
check_ty(cx, ty, is_local);
|
||||
for ty in p.segments
|
||||
.iter()
|
||||
.flat_map(|seg| seg.parameters.types.iter())
|
||||
.flat_map(|seg| seg.parameters.as_ref()
|
||||
.map_or_else(|| [].iter(),
|
||||
|params| params.types.iter()))
|
||||
{
|
||||
check_ty(cx, ty, is_local);
|
||||
}
|
||||
},
|
||||
QPath::Resolved(None, ref p) => for ty in p.segments
|
||||
.iter()
|
||||
.flat_map(|seg| seg.parameters.types.iter())
|
||||
.flat_map(|seg| seg.parameters.as_ref()
|
||||
.map_or_else(|| [].iter(),
|
||||
|params| params.types.iter()))
|
||||
{
|
||||
check_ty(cx, ty, is_local);
|
||||
},
|
||||
QPath::TypeRelative(ref ty, ref seg) => {
|
||||
check_ty(cx, ty, is_local);
|
||||
for ty in seg.parameters.types.iter() {
|
||||
check_ty(cx, ty, is_local);
|
||||
if let Some(ref params) = seg.parameters {
|
||||
for ty in params.types.iter() {
|
||||
check_ty(cx, ty, is_local);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
TyRptr(ref lt, MutTy { ref ty, ref mutbl }) => {
|
||||
match ty.node {
|
||||
TyPath(ref qpath) => {
|
||||
let hir_id = cx.tcx.hir.node_to_hir_id(ty.id);
|
||||
let def = cx.tables.qpath_def(qpath, hir_id);
|
||||
if_let_chain! {[
|
||||
let Some(def_id) = opt_def_id(def),
|
||||
Some(def_id) == cx.tcx.lang_items().owned_box(),
|
||||
let QPath::Resolved(None, ref path) = *qpath,
|
||||
let [ref bx] = *path.segments,
|
||||
!bx.parameters.parenthesized,
|
||||
let [ref inner] = *bx.parameters.types
|
||||
], {
|
||||
if is_any_trait(inner) {
|
||||
// Ignore `Box<Any>` types, see #1884 for details.
|
||||
return;
|
||||
}
|
||||
|
||||
let ltopt = if lt.is_elided() {
|
||||
"".to_owned()
|
||||
} else {
|
||||
format!("{} ", lt.name.as_str())
|
||||
};
|
||||
let mutopt = if *mutbl == Mutability::MutMutable {
|
||||
"mut "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
span_lint_and_sugg(cx,
|
||||
BORROWED_BOX,
|
||||
ast_ty.span,
|
||||
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
|
||||
"try",
|
||||
format!("&{}{}{}", ltopt, mutopt, &snippet(cx, inner.span, ".."))
|
||||
);
|
||||
return; // don't recurse into the type
|
||||
}};
|
||||
check_ty(cx, ty, is_local);
|
||||
},
|
||||
_ => check_ty(cx, ty, is_local),
|
||||
}
|
||||
},
|
||||
TyRptr(ref lt, ref mut_ty) => check_ty_rptr(cx, ast_ty, is_local, lt, mut_ty),
|
||||
// recurse
|
||||
TySlice(ref ty) | TyArray(ref ty, _) | TyPtr(MutTy { ref ty, .. }) => check_ty(cx, ty, is_local),
|
||||
TyTup(ref tys) => for ty in tys {
|
||||
|
|
@ -253,6 +219,50 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_ty_rptr(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool, lt: &Lifetime, mut_ty: &MutTy) {
|
||||
match mut_ty.ty.node {
|
||||
TyPath(ref qpath) => {
|
||||
let hir_id = cx.tcx.hir.node_to_hir_id(mut_ty.ty.id);
|
||||
let def = cx.tables.qpath_def(qpath, hir_id);
|
||||
if_let_chain! {[
|
||||
let Some(def_id) = opt_def_id(def),
|
||||
Some(def_id) == cx.tcx.lang_items().owned_box(),
|
||||
let QPath::Resolved(None, ref path) = *qpath,
|
||||
let [ref bx] = *path.segments,
|
||||
let Some(ref params) = bx.parameters,
|
||||
!params.parenthesized,
|
||||
let [ref inner] = *params.types
|
||||
], {
|
||||
if is_any_trait(inner) {
|
||||
// Ignore `Box<Any>` types, see #1884 for details.
|
||||
return;
|
||||
}
|
||||
|
||||
let ltopt = if lt.is_elided() {
|
||||
"".to_owned()
|
||||
} else {
|
||||
format!("{} ", lt.name.name().as_str())
|
||||
};
|
||||
let mutopt = if mut_ty.mutbl == Mutability::MutMutable {
|
||||
"mut "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
span_lint_and_sugg(cx,
|
||||
BORROWED_BOX,
|
||||
ast_ty.span,
|
||||
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
|
||||
"try",
|
||||
format!("&{}{}{}", ltopt, mutopt, &snippet(cx, inner.span, ".."))
|
||||
);
|
||||
return; // don't recurse into the type
|
||||
}};
|
||||
check_ty(cx, &mut_ty.ty, is_local);
|
||||
},
|
||||
_ => check_ty(cx, &mut_ty.ty, is_local),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if given type is `Any` trait.
|
||||
fn is_any_trait(t: &hir::Ty) -> bool {
|
||||
if_let_chain! {[
|
||||
|
|
|
|||
|
|
@ -59,7 +59,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
|
|||
let Ty_::TyPath(QPath::Resolved(_, ref item_path)) = item_type.node,
|
||||
], {
|
||||
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).parameters;
|
||||
if !parameters.parenthesized && parameters.lifetimes.len() == 0 {
|
||||
let should_check = if let Some(ref params) = *parameters {
|
||||
!params.parenthesized && params.lifetimes.len() == 0
|
||||
} else {
|
||||
true
|
||||
};
|
||||
if should_check {
|
||||
let visitor = &mut UseSelfVisitor {
|
||||
item_path: item_path,
|
||||
cx: cx,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ macro_rules! define_Conf {
|
|||
#[serde(rename_all="kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Conf {
|
||||
$(#[$doc] #[serde(default=$rust_name_str)] #[serde(with=$rust_name_str)] pub $rust_name: define_Conf!(TY $($ty)+),)+
|
||||
$(#[$doc] #[serde(default=$rust_name_str)] #[serde(with=$rust_name_str)]
|
||||
pub $rust_name: define_Conf!(TY $($ty)+),)+
|
||||
#[allow(dead_code)]
|
||||
#[serde(default)]
|
||||
third_party: Option<::toml::Value>,
|
||||
|
|
@ -91,10 +92,12 @@ macro_rules! define_Conf {
|
|||
mod $rust_name {
|
||||
use serde;
|
||||
use serde::Deserialize;
|
||||
pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<define_Conf!(TY $($ty)+), D::Error> {
|
||||
pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
||||
-> Result<define_Conf!(TY $($ty)+), D::Error> {
|
||||
type T = define_Conf!(TY $($ty)+);
|
||||
Ok(T::deserialize(deserializer).unwrap_or_else(|e| {
|
||||
::utils::conf::ERRORS.lock().expect("no threading here").push(::utils::conf::Error::Toml(e.to_string()));
|
||||
::utils::conf::ERRORS.lock().expect("no threading here")
|
||||
.push(::utils::conf::Error::Toml(e.to_string()));
|
||||
super::$rust_name()
|
||||
}))
|
||||
}
|
||||
|
|
@ -154,10 +157,10 @@ define_Conf! {
|
|||
"JavaScript",
|
||||
"NaN",
|
||||
"OAuth",
|
||||
"OpenGL",
|
||||
"OpenGL", "OpenSSH", "OpenSSL",
|
||||
"TrueType",
|
||||
"iOS", "macOS",
|
||||
"TeX", "LaTeX", "BibTex", "BibLaTex",
|
||||
"TeX", "LaTeX", "BibTeX", "BibLaTeX",
|
||||
"MinGW",
|
||||
] => Vec<String>),
|
||||
/// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
|
||||
|
|
@ -172,6 +175,8 @@ define_Conf! {
|
|||
(enum_variant_name_threshold, "enum_variant_name_threshold", 3 => u64),
|
||||
/// Lint: LARGE_ENUM_VARIANT. The maximum size of a emum's variant to avoid box suggestion
|
||||
(enum_variant_size_threshold, "enum_variant_size_threshold", 200 => u64),
|
||||
/// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
|
||||
(verbose_bit_mask_threshold, "verbose_bit_mask_threshold", 1 => u64),
|
||||
}
|
||||
|
||||
/// Search for the configuration file.
|
||||
|
|
|
|||
|
|
@ -214,7 +214,14 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
|
|||
fn eq_path_segment(&self, left: &PathSegment, right: &PathSegment) -> bool {
|
||||
// The == of idents doesn't work with different contexts,
|
||||
// we have to be explicit about hygiene
|
||||
left.name.as_str() == right.name.as_str() && self.eq_path_parameters(&left.parameters, &right.parameters)
|
||||
if left.name.as_str() != right.name.as_str() {
|
||||
return false;
|
||||
}
|
||||
match (&left.parameters, &right.parameters) {
|
||||
(&None, &None) => true,
|
||||
(&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r),
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn eq_ty(&self, left: &Ty, right: &Ty) -> bool {
|
||||
|
|
|
|||
|
|
@ -26,10 +26,13 @@ pub const DOUBLE_ENDED_ITERATOR: [&'static str; 4] = ["core", "iter", "traits",
|
|||
pub const DROP: [&'static str; 3] = ["core", "mem", "drop"];
|
||||
pub const FMT_ARGUMENTS_NEWV1: [&'static str; 4] = ["core", "fmt", "Arguments", "new_v1"];
|
||||
pub const FMT_ARGUMENTV1_NEW: [&'static str; 4] = ["core", "fmt", "ArgumentV1", "new"];
|
||||
pub const FROM_FROM: [&'static str; 4] = ["core", "convert", "From", "from"];
|
||||
pub const HASH: [&'static str; 2] = ["hash", "Hash"];
|
||||
pub const HASHMAP: [&'static str; 5] = ["std", "collections", "hash", "map", "HashMap"];
|
||||
pub const HASHMAP_ENTRY: [&'static str; 5] = ["std", "collections", "hash", "map", "Entry"];
|
||||
pub const HASHSET: [&'static str; 5] = ["std", "collections", "hash", "set", "HashSet"];
|
||||
pub const INIT: [&'static str; 4] = ["core", "intrinsics", "", "init"];
|
||||
pub const INTO: [&'static str; 3] = ["core", "convert", "Into"];
|
||||
pub const INTO_ITERATOR: [&'static str; 4] = ["core", "iter", "traits", "IntoIterator"];
|
||||
pub const IO_PRINT: [&'static str; 4] = ["std", "io", "stdio", "_print"];
|
||||
pub const IO_READ: [&'static str; 3] = ["std", "io", "Read"];
|
||||
|
|
@ -39,6 +42,8 @@ pub const LINKED_LIST: [&'static str; 3] = ["alloc", "linked_list", "LinkedList"
|
|||
pub const LINT: [&'static str; 3] = ["rustc", "lint", "Lint"];
|
||||
pub const LINT_ARRAY: [&'static str; 3] = ["rustc", "lint", "LintArray"];
|
||||
pub const MEM_FORGET: [&'static str; 3] = ["core", "mem", "forget"];
|
||||
pub const MEM_UNINIT: [&'static str; 3] = ["core", "mem", "uninitialized"];
|
||||
pub const MEM_ZEROED: [&'static str; 3] = ["core", "mem", "zeroed"];
|
||||
pub const MUTEX: [&'static str; 4] = ["std", "sync", "mutex", "Mutex"];
|
||||
pub const OPEN_OPTIONS: [&'static str; 3] = ["std", "fs", "OpenOptions"];
|
||||
pub const OPS_MODULE: [&'static str; 2] = ["core", "ops"];
|
||||
|
|
@ -80,6 +85,7 @@ pub const TO_OWNED: [&'static str; 3] = ["alloc", "borrow", "ToOwned"];
|
|||
pub const TO_STRING: [&'static str; 3] = ["alloc", "string", "ToString"];
|
||||
pub const TRANSMUTE: [&'static str; 4] = ["core", "intrinsics", "", "transmute"];
|
||||
pub const TRY_INTO_RESULT: [&'static str; 4] = ["std", "ops", "Try", "into_result"];
|
||||
pub const UNINIT: [&'static str; 4] = ["core", "intrinsics", "", "uninit"];
|
||||
pub const VEC: [&'static str; 3] = ["alloc", "vec", "Vec"];
|
||||
pub const VEC_DEQUE: [&'static str; 3] = ["alloc", "vec_deque", "VecDeque"];
|
||||
pub const VEC_FROM_ELEM: [&'static str; 3] = ["alloc", "vec", "from_elem"];
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ impl<'a> Sugg<'a> {
|
|||
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::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, 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),
|
||||
|
|
@ -165,7 +165,7 @@ impl<'a> Sugg<'a> {
|
|||
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),
|
||||
ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, &end),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +312,7 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
|
|||
AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_to_string(&token::BinOp(op)), rhs),
|
||||
AssocOp::As => format!("{} as {}", lhs, rhs),
|
||||
AssocOp::DotDot => format!("{}..{}", lhs, rhs),
|
||||
AssocOp::DotDotDot => format!("{}...{}", lhs, rhs),
|
||||
AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
|
||||
AssocOp::Colon => format!("{}: {}", lhs, rhs),
|
||||
};
|
||||
|
||||
|
|
@ -362,7 +362,7 @@ fn associativity(op: &AssocOp) -> Associativity {
|
|||
ShiftLeft |
|
||||
ShiftRight |
|
||||
Subtract => Associativity::Left,
|
||||
DotDot | DotDotDot => Associativity::None,
|
||||
DotDot | DotDotEq => Associativity::None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ fn check_vec_macro<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, vec_args: &higher::VecA
|
|||
.eval(len)
|
||||
.is_ok()
|
||||
{
|
||||
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
|
||||
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len"))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ fn check_vec_macro<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, vec_args: &higher::VecA
|
|||
higher::VecArgs::Vec(args) => if let Some(last) = args.iter().last() {
|
||||
let span = args[0].span.to(last.span);
|
||||
|
||||
format!("&[{}]", snippet(cx, span, "..")).into()
|
||||
format!("&[{}]", snippet(cx, span, ".."))
|
||||
} else {
|
||||
"&[]".into()
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue