Merge branch 'master' into no_effect_with_drop

This commit is contained in:
Chris Emerson 2017-10-07 23:24:36 +01:00
commit fcdce8fc1d
348 changed files with 2221 additions and 1475 deletions

View file

@ -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 = []

View file

@ -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)
})

View file

@ -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() {

View file

@ -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,

View file

@ -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

View 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();
}
}
}

View 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());
}
}
}
}

View 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;
}
}

View file

@ -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,

View file

@ -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 &params.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;
}
}

View file

@ -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);

View file

@ -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,
}
}

View file

@ -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

View file

@ -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(&params.types[0])
|| is_ty(&*params.types[0], self_ty))
}
} else {
false
}
})
} else {
false

View file

@ -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;

View file

@ -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| &params.types[0]),
], {
let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
db.span_suggestion(input.span,

View file

@ -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
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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! {[

View file

@ -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,

View file

@ -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.

View 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 {

View file

@ -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"];

View file

@ -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,
}
}

View file

@ -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()
},