Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Maxwell Anderson 2018-10-27 12:50:12 -06:00
commit 183e19a1c3
35 changed files with 1285 additions and 92 deletions

View File

@ -9,6 +9,13 @@ os:
sudo: false
branches:
# Don't build these branches
except:
# Used by bors
- trying.tmp
- staging.tmp
env:
global:
- RUST_BACKTRACE=1
@ -56,7 +63,8 @@ matrix:
- env: INTEGRATION=chronotope/chrono
- env: INTEGRATION=serde-rs/serde
- env: INTEGRATION=Geal/nom
- env: INTEGRATION=hyperium/hyper
# uncomment once https://github.com/rust-lang/rust/issues/55376 is fixed
# - env: INTEGRATION=hyperium/hyper
allow_failures:
- os: windows
env: BASE_TEST=true

View File

@ -744,6 +744,7 @@ All notable changes to this project will be documented in this file.
[`match_same_arms`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#match_same_arms
[`match_wild_err_arm`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#match_wild_err_arm
[`maybe_infinite_iter`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#maybe_infinite_iter
[`mem_discriminant_non_enum`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
[`mem_forget`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_forget
[`mem_replace_option_with_none`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
[`min_max`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#min_max
@ -812,6 +813,7 @@ All notable changes to this project will be documented in this file.
[`range_plus_one`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#range_plus_one
[`range_step_by_zero`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#range_step_by_zero
[`range_zip_with_len`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#range_zip_with_len
[`redundant_clone`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_clone
[`redundant_closure`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_closure
[`redundant_closure_call`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_closure_call
[`redundant_field_names`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_field_names
@ -893,6 +895,7 @@ All notable changes to this project will be documented in this file.
[`while_immutable_condition`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#while_immutable_condition
[`while_let_loop`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#while_let_loop
[`while_let_on_iterator`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#while_let_on_iterator
[`wildcard_dependencies`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#wildcard_dependencies
[`write_literal`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#write_literal
[`write_with_newline`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#write_with_newline
[`writeln_empty_string`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#writeln_empty_string

View File

@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
[There are 280 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
[There are 283 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:

View File

@ -7,6 +7,13 @@ environment:
#- TARGET: x86_64-pc-windows-gnu
- TARGET: x86_64-pc-windows-msvc
branches:
# Don't build these branches
except:
# Used by bors
- trying.tmp
- staging.tmp
install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain nightly

4
bors.toml Normal file
View File

@ -0,0 +1,4 @@
status = [
"continuous-integration/travis-ci/push",
"continuous-integration/appveyor/branch"
]

View File

@ -51,7 +51,9 @@ declare_clippy_lint! {
#[derive(Copy, Clone, Default)]
pub struct Arithmetic {
span: Option<Span>,
expr_span: Option<Span>,
/// This field is used to check whether expressions are constants, such as in enum discriminants and consts
const_span: Option<Span>,
}
impl LintPass for Arithmetic {
@ -62,9 +64,15 @@ impl LintPass for Arithmetic {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
if self.span.is_some() {
if self.expr_span.is_some() {
return;
}
if let Some(span) = self.const_span {
if span.contains(expr.span) {
return;
}
}
match expr.node {
hir::ExprKind::Binary(ref op, ref l, ref r) => {
match op.node {
@ -86,20 +94,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
if l_ty.is_integral() && r_ty.is_integral() {
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
self.span = Some(expr.span);
self.expr_span = Some(expr.span);
} else if l_ty.is_floating_point() && r_ty.is_floating_point() {
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
self.span = Some(expr.span);
self.expr_span = Some(expr.span);
}
},
hir::ExprKind::Unary(hir::UnOp::UnNeg, ref arg) => {
let ty = cx.tables.expr_ty(arg);
if ty.is_integral() {
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
self.span = Some(expr.span);
self.expr_span = Some(expr.span);
} else if ty.is_floating_point() {
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
self.span = Some(expr.span);
self.expr_span = Some(expr.span);
}
},
_ => (),
@ -107,8 +115,39 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
}
fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
if Some(expr.span) == self.span {
self.span = None;
if Some(expr.span) == self.expr_span {
self.expr_span = None;
}
}
fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body) {
let body_owner = cx.tcx.hir.body_owner(body.id());
match cx.tcx.hir.body_owner_kind(body_owner) {
hir::BodyOwnerKind::Static(_)
| hir::BodyOwnerKind::Const => {
let body_span = cx.tcx.hir.span(body_owner);
if let Some(span) = self.const_span {
if span.contains(body_span) {
return;
}
}
self.const_span = Some(body_span);
}
hir::BodyOwnerKind::Fn => (),
}
}
fn check_body_post(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body) {
let body_owner = cx.tcx.hir.body_owner(body.id());
let body_span = cx.tcx.hir.span(body_owner);
if let Some(span) = self.const_span {
if span.contains(body_span) {
return;
}
}
self.const_span = None;
}
}

View File

@ -108,19 +108,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing {
if let ExprKind::Index(ref array, ref index) = &expr.node {
let ty = cx.tables.expr_ty(array);
if let Some(range) = higher::range(cx, index) {
// Ranged indexes, i.e. &x[n..m], &x[n..], &x[..n] and &x[..]
if let ty::Array(_, s) = ty.sty {
let size: u128 = s.assert_usize(cx.tcx).unwrap().into();
// Index is a constant range.
if let Some((start, end)) = to_const_range(cx, range, size) {
if start > size || end > size {
let const_range = to_const_range(cx, range, size);
if let (Some(start), _) = const_range {
if start > size {
utils::span_lint(
cx,
OUT_OF_BOUNDS_INDEXING,
expr.span,
range.start.map_or(expr.span, |start| start.span),
"range is out of bounds",
);
return;
}
}
if let (_, Some(end)) = const_range {
if end > size {
utils::span_lint(
cx,
OUT_OF_BOUNDS_INDEXING,
range.end.map_or(expr.span, |end| end.span),
"range is out of bounds",
);
return;
}
}
if let (Some(_), Some(_)) = const_range {
// early return because both start and end are constants
// and we have proven above that they are in bounds
return;
}
}
@ -161,20 +182,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing {
}
}
/// Returns an option containing a tuple with the start and end (exclusive) of
/// the range.
/// Returns a tuple of options with the start and end (exclusive) values of
/// the range. If the start or end is not constant, None is returned.
fn to_const_range<'a, 'tcx>(
cx: &LateContext<'a, 'tcx>,
range: Range<'_>,
array_size: u128,
) -> Option<(u128, u128)> {
) -> (Option<u128>, Option<u128>) {
let s = range
.start
.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c));
let start = match s {
Some(Some(Constant::Int(x))) => x,
Some(_) => return None,
None => 0,
Some(Some(Constant::Int(x))) => Some(x),
Some(_) => None,
None => Some(0),
};
let e = range
@ -182,13 +203,13 @@ fn to_const_range<'a, 'tcx>(
.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c));
let end = match e {
Some(Some(Constant::Int(x))) => if range.limits == RangeLimits::Closed {
x + 1
Some(x + 1)
} else {
x
Some(x)
},
Some(_) => return None,
None => array_size,
Some(_) => None,
None => Some(array_size),
};
Some((start, end))
(start, end)
}

View File

@ -144,6 +144,7 @@ pub mod loops;
pub mod map_clone;
pub mod map_unit_fn;
pub mod matches;
pub mod mem_discriminant;
pub mod mem_forget;
pub mod mem_replace;
pub mod methods;
@ -178,6 +179,7 @@ pub mod ptr;
pub mod ptr_offset_with_cast;
pub mod question_mark;
pub mod ranges;
pub mod redundant_clone;
pub mod redundant_field_names;
pub mod redundant_pattern_matching;
pub mod reference;
@ -200,6 +202,7 @@ pub mod unused_label;
pub mod unwrap;
pub mod use_self;
pub mod vec;
pub mod wildcard_dependencies;
pub mod write;
pub mod zero_div_zero;
// end lints modules, do not remove this comment, its used in `update_lints`
@ -391,12 +394,13 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
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);
reg.register_late_lint_pass(box new_without_default::NewWithoutDefault::default());
reg.register_late_lint_pass(box blacklisted_name::BlackListedName::new(conf.blacklisted_names.clone()));
reg.register_late_lint_pass(box functions::Functions::new(conf.too_many_arguments_threshold));
reg.register_early_lint_pass(box doc::Doc::new(conf.doc_valid_idents.clone()));
reg.register_late_lint_pass(box neg_multiply::NegMultiply);
reg.register_early_lint_pass(box unsafe_removed_from_name::UnsafeNameRemoval);
reg.register_late_lint_pass(box mem_discriminant::MemDiscriminant);
reg.register_late_lint_pass(box mem_forget::MemForget);
reg.register_late_lint_pass(box mem_replace::MemReplace);
reg.register_late_lint_pass(box arithmetic::Arithmetic::default());
@ -438,6 +442,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
reg.register_late_lint_pass(box question_mark::Pass);
reg.register_late_lint_pass(box suspicious_trait_impl::SuspiciousImpl);
reg.register_early_lint_pass(box multiple_crate_versions::Pass);
reg.register_early_lint_pass(box wildcard_dependencies::Pass);
reg.register_late_lint_pass(box map_unit_fn::Pass);
reg.register_late_lint_pass(box infallible_destructuring_match::Pass);
reg.register_late_lint_pass(box inherent_impl::Pass::default());
@ -448,6 +453,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
reg.register_late_lint_pass(box indexing_slicing::IndexingSlicing);
reg.register_late_lint_pass(box non_copy_const::NonCopyConst);
reg.register_late_lint_pass(box ptr_offset_with_cast::Pass);
reg.register_late_lint_pass(box redundant_clone::RedundantClone);
reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
arithmetic::FLOAT_ARITHMETIC,
@ -610,6 +616,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
matches::MATCH_REF_PATS,
matches::MATCH_WILD_ERR_ARM,
matches::SINGLE_MATCH,
mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
methods::CHARS_LAST_CMP,
methods::CHARS_NEXT_CMP,
@ -922,6 +929,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
loops::NEVER_LOOP,
loops::REVERSE_RANGE_LOOP,
loops::WHILE_IMMUTABLE_CONDITION,
mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
methods::CLONE_DOUBLE_REF,
methods::TEMPORARY_CSTRING_AS_PTR,
minmax::MIN_MAX,
@ -967,6 +975,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
reg.register_lint_group("clippy::cargo", Some("clippy_cargo"), vec![
multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
wildcard_dependencies::WILDCARD_DEPENDENCIES,
]);
reg.register_lint_group("clippy::nursery", Some("clippy_nursery"), vec![
@ -974,6 +983,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
fallible_impl_from::FALLIBLE_IMPL_FROM,
mutex_atomic::MUTEX_INTEGER,
needless_borrow::NEEDLESS_BORROW,
redundant_clone::REDUNDANT_CLONE,
unwrap::PANICKING_UNWRAP,
unwrap::UNNECESSARY_UNWRAP,
]);

View File

@ -1068,7 +1068,7 @@ fn check_for_loop_range<'a, 'tcx>(
// linting condition: we only indexed one variable, and indexed it directly
if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
let (indexed, indexed_extent) = visitor
let (indexed, (indexed_extent, indexed_ty)) = visitor
.indexed_directly
.into_iter()
.next()
@ -1119,7 +1119,7 @@ fn check_for_loop_range<'a, 'tcx>(
}
}
if is_len_call(end, indexed) {
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
String::new()
} else {
match limits {
@ -1207,6 +1207,28 @@ fn is_len_call(expr: &Expr, var: Name) -> bool {
false
}
fn is_end_eq_array_len(
cx: &LateContext<'_, '_>,
end: &Expr,
limits: ast::RangeLimits,
indexed_ty: Ty<'_>,
) -> bool {
if_chain! {
if let ExprKind::Lit(ref lit) = end.node;
if let ast::LitKind::Int(end_int, _) = lit.node;
if let ty::TyKind::Array(_, arr_len_const) = indexed_ty.sty;
if let Some(arr_len) = arr_len_const.assert_usize(cx.tcx);
then {
return match limits {
ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
};
}
}
false
}
fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr, expr: &'tcx Expr) {
// if this for loop is iterating over a two-sided range...
if let Some(higher::Range {
@ -1678,7 +1700,7 @@ struct VarVisitor<'a, 'tcx: 'a> {
indexed_indirectly: FxHashMap<Name, Option<region::Scope>>,
/// subset of `indexed` of vars that are indexed directly: `v[i]`
/// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
indexed_directly: FxHashMap<Name, Option<region::Scope>>,
indexed_directly: FxHashMap<Name, (Option<region::Scope>, Ty<'tcx>)>,
/// Any names that are used outside an index operation.
/// Used to detect things like `&mut vec` used together with `vec[i]`
referenced: FxHashSet<Name>,
@ -1725,7 +1747,10 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
}
if index_used_directly {
self.indexed_directly.insert(seqvar.segments[0].ident.name, Some(extent));
self.indexed_directly.insert(
seqvar.segments[0].ident.name,
(Some(extent), self.cx.tables.node_id_to_type(seqexpr.hir_id)),
);
}
return false; // no need to walk further *on the variable*
}
@ -1734,7 +1759,10 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
}
if index_used_directly {
self.indexed_directly.insert(seqvar.segments[0].ident.name, None);
self.indexed_directly.insert(
seqvar.segments[0].ident.name,
(None, self.cx.tables.node_id_to_type(seqexpr.hir_id)),
);
}
return false; // no need to walk further *on the variable*
}

View File

@ -0,0 +1,95 @@
// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::rustc::hir::{Expr, ExprKind};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::utils::{match_def_path, opt_def_id, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
use if_chain::if_chain;
use std::iter;
/// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.
///
/// **Why is this bad?** The value of `mem::discriminant()` on non-enum types
/// is unspecified.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// mem::discriminant(&"hello");
/// mem::discriminant(&&Some(2));
/// ```
declare_clippy_lint! {
pub MEM_DISCRIMINANT_NON_ENUM,
correctness,
"calling mem::descriminant on non-enum type"
}
pub struct MemDiscriminant;
impl LintPass for MemDiscriminant {
fn get_lints(&self) -> LintArray {
lint_array![MEM_DISCRIMINANT_NON_ENUM]
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if_chain! {
if let ExprKind::Call(ref func, ref func_args) = expr.node;
// is `mem::discriminant`
if let ExprKind::Path(ref func_qpath) = func.node;
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id));
if match_def_path(cx.tcx, def_id, &paths::MEM_DISCRIMINANT);
// type is non-enum
let ty_param = cx.tables.node_substs(func.hir_id).type_at(0);
if !ty_param.is_enum();
then {
span_lint_and_then(
cx,
MEM_DISCRIMINANT_NON_ENUM,
expr.span,
&format!("calling `mem::discriminant` on non-enum type `{}`", ty_param),
|db| {
// if this is a reference to an enum, suggest dereferencing
let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param);
if ptr_depth >= 1 && base_ty.is_enum() {
let param = &func_args[0];
// cancel out '&'s first
let mut derefs_needed = ptr_depth;
let mut cur_expr = param;
while derefs_needed > 0 {
if let ExprKind::AddrOf(_, ref inner_expr) = cur_expr.node {
derefs_needed -= 1;
cur_expr = inner_expr;
} else {
break;
}
}
let derefs: String = iter::repeat('*').take(derefs_needed).collect();
db.span_suggestion_with_applicability(
param.span,
"try dereferencing",
format!("{}{}", derefs, snippet(cx, cur_expr.span, "<param>")),
Applicability::MachineApplicable,
);
}
},
)
}
}
}
}

View File

@ -452,8 +452,7 @@ declare_clippy_lint! {
/// **Why is this bad?** As a convention, `new` methods are used to make a new
/// instance of a type.
///
/// **Known problems:** The lint fires when the return type is wrapping `Self`.
/// Example: `fn new() -> Result<Self, E> {}`
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
@ -936,10 +935,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
if let hir::ImplItemKind::Method(_, _) = implitem.node {
let ret_ty = return_ty(cx, implitem.id);
// if return type is impl trait
// walk the return type and check for Self (this does not check associated types)
for inner_type in ret_ty.walk() {
if same_tys(cx, ty, inner_type) { return; }
}
// if return type is impl trait, check the associated types
if let TyKind::Opaque(def_id, _) = ret_ty.sty {
// then one of the associated types must be Self
// one of the associated types must be Self
for predicate in cx.tcx.predicates_of(def_id).predicates.iter() {
match predicate {
(Predicate::Projection(poly_projection_predicate), _) => {

View File

@ -7,12 +7,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! lint on multiple versions of a crate being used
use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax::ast::*;
use crate::syntax::{ast::*, source_map::DUMMY_SP};
use crate::utils::span_lint;
use cargo_metadata;
@ -50,16 +49,11 @@ impl LintPass for Pass {
}
impl EarlyLintPass for Pass {
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
let metadata = if let Ok(metadata) = cargo_metadata::metadata_deps(None, true) {
metadata
} else {
span_lint(
cx,
MULTIPLE_CRATE_VERSIONS,
krate.span,
"could not read cargo metadata"
);
span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata");
return;
};
@ -76,7 +70,7 @@ impl EarlyLintPass for Pass {
span_lint(
cx,
MULTIPLE_CRATE_VERSIONS,
krate.span,
DUMMY_SP,
&format!("multiple versions for dependency `{}`: {}", name, versions),
);
}

View File

@ -11,6 +11,7 @@
use crate::rustc::hir::def_id::DefId;
use crate::rustc::hir;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass, in_external_macro, LintContext};
use crate::rustc::util::nodemap::NodeSet;
use crate::rustc::{declare_tool_lint, lint_array};
use if_chain::if_chain;
use crate::rustc::ty::{self, Ty};
@ -91,8 +92,10 @@ declare_clippy_lint! {
"`fn new() -> Self` without `#[derive]`able `Default` implementation"
}
#[derive(Copy, Clone)]
pub struct NewWithoutDefault;
#[derive(Clone, Default)]
pub struct NewWithoutDefault {
impling_types: Option<NodeSet>,
}
impl LintPass for NewWithoutDefault {
fn get_lints(&self) -> LintArray {
@ -130,13 +133,39 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
return;
}
if sig.decl.inputs.is_empty() && name == "new" && cx.access_levels.is_reachable(id) {
let self_did = cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(id));
let self_ty = cx.tcx
.type_of(cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(id)));
.type_of(self_did);
if_chain! {
if same_tys(cx, self_ty, return_ty(cx, id));
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
if !implements_trait(cx, self_ty, default_trait_id, &[]);
then {
if self.impling_types.is_none() {
let mut impls = NodeSet();
cx.tcx.for_each_impl(default_trait_id, |d| {
if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
if let Some(node_id) = cx.tcx.hir.as_local_node_id(ty_def.did) {
impls.insert(node_id);
}
}
});
self.impling_types = Some(impls);
}
// Check if a Default implementation exists for the Self type, regardless of
// generics
if_chain! {
if let Some(ref impling_types) = self.impling_types;
if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def();
if self_def.did.is_local();
then {
let self_id = cx.tcx.hir.local_def_id_to_node_id(self_def.did.to_local());
if impling_types.contains(&self_id) {
return;
}
}
}
if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) {
span_lint_and_then(
cx,

View File

@ -0,0 +1,290 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::rustc::hir::intravisit::FnKind;
use crate::rustc::hir::{def_id, Body, FnDecl};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::mir::{
self, traversal,
visit::{PlaceContext, Visitor},
TerminatorKind,
};
use crate::rustc::ty;
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::syntax::{
ast::NodeId,
source_map::{BytePos, Span},
};
use crate::utils::{
in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node, span_lint_node_and_then,
walk_ptrs_ty_depth,
};
use if_chain::if_chain;
use std::convert::TryFrom;
macro_rules! unwrap_or_continue {
($x:expr) => {
match $x {
Some(x) => x,
None => continue,
}
};
}
/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
/// value that is going to be dropped without further use.
///
/// **Why is this bad?** It is not always possible for the compiler to eliminate useless
/// allocations and deallocations generated by redundant `clone()`s.
///
/// **Known problems:**
///
/// * Suggestions made by this lint could require NLL to be enabled.
/// * False-positive if there is a borrow preventing the value from moving out.
///
/// ```rust
/// let x = String::new();
///
/// let y = &x;
///
/// foo(x.clone()); // This lint suggests to remove this `clone()`
/// ```
///
/// **Example:**
/// ```rust
/// {
/// let x = Foo::new();
/// call(x.clone());
/// call(x.clone()); // this can just pass `x`
/// }
///
/// ["lorem", "ipsum"].join(" ").to_string()
///
/// Path::new("/a/b").join("c").to_path_buf()
/// ```
declare_clippy_lint! {
pub REDUNDANT_CLONE,
nursery,
"`clone()` of an owned value that is going to be dropped immediately"
}
pub struct RedundantClone;
impl LintPass for RedundantClone {
fn get_lints(&self) -> LintArray {
lint_array!(REDUNDANT_CLONE)
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
fn check_fn(
&mut self,
cx: &LateContext<'a, 'tcx>,
_: FnKind<'tcx>,
_: &'tcx FnDecl,
body: &'tcx Body,
_: Span,
_: NodeId,
) {
let def_id = cx.tcx.hir.body_owner_def_id(body.id());
let mir = cx.tcx.optimized_mir(def_id);
for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
let terminator = bbdata.terminator();
if in_macro(terminator.source_info.span) {
continue;
}
// Give up on loops
if terminator.successors().any(|s| *s == bb) {
continue;
}
let (fn_def_id, arg, arg_ty, _) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
let from_borrow = match_def_path(cx.tcx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
|| match_def_path(cx.tcx, fn_def_id, &paths::TO_OWNED_METHOD)
|| (match_def_path(cx.tcx, fn_def_id, &paths::TO_STRING_METHOD)
&& match_type(cx, arg_ty, &paths::STRING));
let from_deref = !from_borrow
&& (match_def_path(cx.tcx, fn_def_id, &paths::PATH_TO_PATH_BUF)
|| match_def_path(cx.tcx, fn_def_id, &paths::OS_STR_TO_OS_STRING));
if !from_borrow && !from_deref {
continue;
}
// _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
// In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
// block.
let cloned = unwrap_or_continue!(find_stmt_assigns_to(arg, from_borrow, bbdata.statements.iter().rev()));
// _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
let referent = if from_deref {
let ps = mir.predecessors_for(bb);
if ps.len() != 1 {
continue;
}
let pred_terminator = mir[ps[0]].terminator();
let pred_arg = if_chain! {
if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
if *res == mir::Place::Local(cloned);
if match_def_path(cx.tcx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
|| match_type(cx, pred_arg_ty, &paths::OS_STRING);
then {
pred_arg
} else {
continue;
}
};
unwrap_or_continue!(find_stmt_assigns_to(pred_arg, true, mir[ps[0]].statements.iter().rev()))
} else {
cloned
};
let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
// Give up on loops
if tdata.terminator().successors().any(|s| *s == bb) {
return true;
}
let mut vis = LocalUseVisitor {
local: referent,
used_other_than_drop: false,
};
vis.visit_basic_block_data(tbb, tdata);
vis.used_other_than_drop
});
if !used_later {
let span = terminator.source_info.span;
let node = if let mir::ClearCrossCrate::Set(scope_local_data) = &mir.source_scope_local_data {
scope_local_data[terminator.source_info.scope].lint_root
} else {
unreachable!()
};
if_chain! {
if let Some(snip) = snippet_opt(cx, span);
if let Some(dot) = snip.rfind('.');
then {
let sugg_span = span.with_lo(
span.lo() + BytePos(u32::try_from(dot).unwrap())
);
span_lint_node_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |db| {
db.span_suggestion_with_applicability(
sugg_span,
"remove this",
String::new(),
Applicability::MaybeIncorrect,
);
db.span_note(
span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
"this value is dropped without further use",
);
});
} else {
span_lint_node(cx, REDUNDANT_CLONE, node, span, "redundant clone");
}
}
}
}
}
}
/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
fn is_call_with_ref_arg<'tcx>(
cx: &LateContext<'_, 'tcx>,
mir: &'tcx mir::Mir<'tcx>,
kind: &'tcx mir::TerminatorKind<'tcx>,
) -> Option<(def_id::DefId, mir::Local, ty::Ty<'tcx>, Option<&'tcx mir::Place<'tcx>>)> {
if_chain! {
if let TerminatorKind::Call { func, args, destination, .. } = kind;
if args.len() == 1;
if let mir::Operand::Move(mir::Place::Local(local)) = &args[0];
if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
if !is_copy(cx, inner_ty);
then {
Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)))
} else {
None
}
}
}
/// Finds the first `to = (&)from`, and returns `Some(from)`.
fn find_stmt_assigns_to<'a, 'tcx: 'a>(
to: mir::Local,
by_ref: bool,
mut stmts: impl Iterator<Item = &'a mir::Statement<'tcx>>,
) -> Option<mir::Local> {
stmts.find_map(|stmt| {
if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
if *local == to {
if by_ref {
if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
return Some(r);
}
} else if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
return Some(r);
}
}
}
None
})
}
struct LocalUseVisitor {
local: mir::Local,
used_other_than_drop: bool,
}
impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
let statements = &data.statements;
for (statement_index, statement) in statements.iter().enumerate() {
self.visit_statement(block, statement, mir::Location { block, statement_index });
// Once flagged, skip remaining statements
if self.used_other_than_drop {
return;
}
}
self.visit_terminator(
block,
data.terminator(),
mir::Location {
block,
statement_index: statements.len(),
},
);
}
fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext<'tcx>, _: mir::Location) {
match ctx {
PlaceContext::Drop | PlaceContext::StorageDead => return,
_ => {},
}
if *local == self.local {
self.used_other_than_drop = true;
}
}
}

View File

@ -1920,7 +1920,6 @@ enum ImplicitHasherType<'tcx> {
impl<'tcx> ImplicitHasherType<'tcx> {
/// Checks that `ty` is a target type without a BuildHasher.
#[allow(clippy::new_ret_no_self)]
fn new<'a>(cx: &LateContext<'a, 'tcx>, hir_ty: &hir::Ty) -> Option<Self> {
if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.node {
let params: Vec<_> = path.segments.last().as_ref()?.args.as_ref()?

View File

@ -226,6 +226,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
}
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir)
NestedVisitorMap::All(&self.cx.tcx.hir)
}
}

View File

@ -19,12 +19,12 @@ use crate::rustc::hir::print;
use crate::syntax::ast::Attribute;
use crate::utils::get_attr;
/// **What it does:** Dumps every ast/hir node which has the `#[clippy_dump]`
/// **What it does:** Dumps every ast/hir node which has the `#[clippy::dump]`
/// attribute
///
/// **Example:**
/// ```rust
/// #[clippy_dump]
/// #[clippy::dump]
/// extern crate foo;
/// ```
///

View File

@ -562,6 +562,23 @@ pub fn span_lint_and_then<'a, 'tcx: 'a, T: LintContext<'tcx>, F>(
db.docs_link(lint);
}
pub fn span_lint_node(cx: &LateContext<'_, '_>, lint: &'static Lint, node: NodeId, sp: Span, msg: &str) {
DiagnosticWrapper(cx.tcx.struct_span_lint_node(lint, node, sp, msg)).docs_link(lint);
}
pub fn span_lint_node_and_then(
cx: &LateContext<'_, '_>,
lint: &'static Lint,
node: NodeId,
sp: Span,
msg: &str,
f: impl FnOnce(&mut DiagnosticBuilder<'_>),
) {
let mut db = DiagnosticWrapper(cx.tcx.struct_span_lint_node(lint, node, sp, msg));
f(&mut db.0);
db.docs_link(lint);
}
/// Add a span lint with a suggestion on how to fix it.
///
/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.

View File

@ -23,6 +23,7 @@ pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeM
pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"];
pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
@ -31,6 +32,7 @@ pub const C_VOID: [&str; 3] = ["core", "ffi", "c_void"];
pub const C_VOID_LIBC: [&str; 2] = ["libc", "c_void"];
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
@ -55,6 +57,7 @@ pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"];
pub const LINT_ARRAY: [&str; 3] = ["rustc", "lint", "LintArray"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"];
@ -66,7 +69,11 @@ pub const OPTION: [&str; 3] = ["core", "option", "Option"];
pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub const PARTIAL_ORD: [&str; 3] = ["core", "cmp", "PartialOrd"];
pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
@ -99,7 +106,9 @@ pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec
pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
pub const UNINIT: [&str; 4] = ["core", "intrinsics", "", "uninit"];

View File

@ -0,0 +1,69 @@
// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax::{ast::*, source_map::DUMMY_SP};
use crate::utils::span_lint;
use cargo_metadata;
use semver;
/// **What it does:** Checks for wildcard dependencies in the `Cargo.toml`.
///
/// **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
/// it is highly unlikely that you work with any possible version of your dependency,
/// and wildcard dependencies would cause unnecessary breakage in the ecosystem.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```toml
/// [dependencies]
/// regex = "*"
/// ```
declare_clippy_lint! {
pub WILDCARD_DEPENDENCIES,
cargo,
"wildcard dependencies being used"
}
pub struct Pass;
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(WILDCARD_DEPENDENCIES)
}
}
impl EarlyLintPass for Pass {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
let metadata = if let Ok(metadata) = cargo_metadata::metadata(None) {
metadata
} else {
span_lint(cx, WILDCARD_DEPENDENCIES, DUMMY_SP, "could not read cargo metadata");
return;
};
for dep in &metadata.packages[0].dependencies {
// VersionReq::any() does not work
if let Ok(wildcard_ver) = semver::VersionReq::parse("*") {
if dep.req == wildcard_ver {
span_lint(
cx,
WILDCARD_DEPENDENCIES,
DUMMY_SP,
&format!("wildcard dependency for `{}`", dep.name),
);
}
}
}
}
}

View File

@ -37,4 +37,36 @@ fn main() {
f / 2.0;
f - 2.0 * 4.2;
-f;
// No errors for the following items because they are constant expressions
enum Foo {
Bar = -2,
}
struct Baz([i32; 1 + 1]);
union Qux {
field: [i32; 1 + 1],
}
type Alias = [i32; 1 + 1];
const FOO: i32 = -2;
static BAR: i32 = -2;
let _: [i32; 1 + 1] = [0, 0];
let _: [i32; 1 + 1] = {
let a: [i32; 1 + 1] = [0, 0];
a
};
trait Trait {
const ASSOC: i32 = 1 + 1;
}
impl Trait for Foo {
const ASSOC: i32 = {
let _: [i32; 1 + 1];
fn foo() {}
1 + 1
};
}
}

View File

@ -97,8 +97,8 @@ error: the loop variable `j` is only used to index `STATIC`.
| ^^^^
help: consider using an iterator
|
110 | for <item> in STATIC.iter().take(4) {
| ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
110 | for <item> in &STATIC {
| ^^^^^^ ^^^^^^^
error: the loop variable `j` is only used to index `CONST`.
--> $DIR/for_loop.rs:114:14
@ -107,8 +107,8 @@ error: the loop variable `j` is only used to index `CONST`.
| ^^^^
help: consider using an iterator
|
114 | for <item> in CONST.iter().take(4) {
| ^^^^^^ ^^^^^^^^^^^^^^^^^^^^
114 | for <item> in &CONST {
| ^^^^^^ ^^^^^^
error: the loop variable `i` is used to index `vec`
--> $DIR/for_loop.rs:118:14

View File

@ -91,4 +91,9 @@ fn main() {
x[M]; // Ok, should not produce stderr.
v[N];
v[M];
// issue 3102
let num = 1;
&x[num..10]; // should trigger out of bounds error
&x[10..num]; // should trigger out of bounds error
}

View File

@ -1,3 +1,29 @@
error: index out of bounds: the len is 4 but the index is 4
--> $DIR/indexing_slicing.rs:28:5
|
28 | x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
| ^^^^
|
= note: #[deny(const_err)] on by default
error: index out of bounds: the len is 4 but the index is 8
--> $DIR/indexing_slicing.rs:29:5
|
29 | x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
| ^^^^^^^^^
error: index out of bounds: the len is 0 but the index is 0
--> $DIR/indexing_slicing.rs:59:5
|
59 | empty[0]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
| ^^^^^^^^
error: index out of bounds: the len is 4 but the index is 15
--> $DIR/indexing_slicing.rs:90:5
|
90 | x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
| ^^^^
error: indexing may panic.
--> $DIR/indexing_slicing.rs:23:5
|
@ -48,18 +74,18 @@ error: slicing may panic.
= help: Consider using `.get(n..)` or .get_mut(n..)` instead
error: range is out of bounds
--> $DIR/indexing_slicing.rs:30:6
--> $DIR/indexing_slicing.rs:30:11
|
30 | &x[..=4];
| ^^^^^^^
| ^
|
= note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
error: range is out of bounds
--> $DIR/indexing_slicing.rs:31:6
--> $DIR/indexing_slicing.rs:31:11
|
31 | &x[1..5];
| ^^^^^^^
| ^
error: slicing may panic.
--> $DIR/indexing_slicing.rs:32:6
@ -70,34 +96,34 @@ error: slicing may panic.
= help: Consider using `.get(..n)`or `.get_mut(..n)` instead
error: range is out of bounds
--> $DIR/indexing_slicing.rs:32:6
--> $DIR/indexing_slicing.rs:32:8
|
32 | &x[5..][..10]; // Two lint reports, one for [5..] and another for [..10].
| ^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:33:6
--> $DIR/indexing_slicing.rs:33:8
|
33 | &x[5..];
| ^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:34:6
--> $DIR/indexing_slicing.rs:34:10
|
34 | &x[..5];
| ^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:35:6
--> $DIR/indexing_slicing.rs:35:8
|
35 | &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>();
| ^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:36:6
--> $DIR/indexing_slicing.rs:36:12
|
36 | &x[0..=4];
| ^^^^^^^^
| ^
error: slicing may panic.
--> $DIR/indexing_slicing.rs:37:6
@ -148,46 +174,46 @@ error: slicing may panic.
= help: Consider using `.get(..n)`or `.get_mut(..n)` instead
error: range is out of bounds
--> $DIR/indexing_slicing.rs:60:6
--> $DIR/indexing_slicing.rs:60:12
|
60 | &empty[1..5];
| ^^^^^^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:61:6
--> $DIR/indexing_slicing.rs:61:16
|
61 | &empty[0..=4];
| ^^^^^^^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:62:6
--> $DIR/indexing_slicing.rs:62:15
|
62 | &empty[..=4];
| ^^^^^^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:63:6
--> $DIR/indexing_slicing.rs:63:12
|
63 | &empty[1..];
| ^^^^^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:64:6
--> $DIR/indexing_slicing.rs:64:14
|
64 | &empty[..4];
| ^^^^^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:65:6
--> $DIR/indexing_slicing.rs:65:16
|
65 | &empty[0..=0];
| ^^^^^^^^^^^^
| ^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:66:6
--> $DIR/indexing_slicing.rs:66:15
|
66 | &empty[..=0];
| ^^^^^^^^^^^
| ^
error: indexing may panic.
--> $DIR/indexing_slicing.rs:74:5
@ -230,10 +256,10 @@ error: slicing may panic.
= help: Consider using `.get(..n)`or `.get_mut(..n)` instead
error: range is out of bounds
--> $DIR/indexing_slicing.rs:78:6
--> $DIR/indexing_slicing.rs:78:8
|
78 | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
| ^^^^^^^
| ^^
error: slicing may panic.
--> $DIR/indexing_slicing.rs:79:6
@ -267,5 +293,17 @@ error: indexing may panic.
|
= help: Consider using `.get(n)` or `.get_mut(n)` instead
error: aborting due to 37 previous errors
error: range is out of bounds
--> $DIR/indexing_slicing.rs:97:13
|
97 | &x[num..10]; // should trigger out of bounds error
| ^^
error: range is out of bounds
--> $DIR/indexing_slicing.rs:98:8
|
98 | &x[10..num]; // should trigger out of bounds error
| ^^
error: aborting due to 43 previous errors

View File

@ -0,0 +1,55 @@
// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(clippy::mem_discriminant_non_enum)]
use std::mem;
enum Foo {
One(usize),
Two(u8),
}
struct A(Foo);
fn main() {
// bad
mem::discriminant(&"hello");
mem::discriminant(&&Some(2));
mem::discriminant(&&None::<u8>);
mem::discriminant(&&Foo::One(5));
mem::discriminant(&&Foo::Two(5));
mem::discriminant(&A(Foo::One(0)));
let ro = &Some(3);
let rro = &ro;
mem::discriminant(&ro);
mem::discriminant(rro);
mem::discriminant(&rro);
macro_rules! mem_discriminant_but_in_a_macro {
($param:expr) => (mem::discriminant($param))
}
mem_discriminant_but_in_a_macro!(&rro);
let rrrrro = &&&rro;
mem::discriminant(&rrrrro);
mem::discriminant(*rrrrro);
// ok
mem::discriminant(&Some(2));
mem::discriminant(&None::<u8>);
mem::discriminant(&Foo::One(5));
mem::discriminant(&Foo::Two(5));
mem::discriminant(ro);
mem::discriminant(*rro);
mem::discriminant(****rrrrro);
}

View File

@ -0,0 +1,104 @@
error: calling `mem::discriminant` on non-enum type `&str`
--> $DIR/mem_discriminant.rs:24:5
|
24 | mem::discriminant(&"hello");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: lint level defined here
--> $DIR/mem_discriminant.rs:11:9
|
11 | #![deny(clippy::mem_discriminant_non_enum)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
--> $DIR/mem_discriminant.rs:25:5
|
25 | mem::discriminant(&&Some(2));
| ^^^^^^^^^^^^^^^^^^---------^
| |
| help: try dereferencing: `&Some(2)`
error: calling `mem::discriminant` on non-enum type `&std::option::Option<u8>`
--> $DIR/mem_discriminant.rs:26:5
|
26 | mem::discriminant(&&None::<u8>);
| ^^^^^^^^^^^^^^^^^^------------^
| |
| help: try dereferencing: `&None::<u8>`
error: calling `mem::discriminant` on non-enum type `&Foo`
--> $DIR/mem_discriminant.rs:27:5
|
27 | mem::discriminant(&&Foo::One(5));
| ^^^^^^^^^^^^^^^^^^-------------^
| |
| help: try dereferencing: `&Foo::One(5)`
error: calling `mem::discriminant` on non-enum type `&Foo`
--> $DIR/mem_discriminant.rs:28:5
|
28 | mem::discriminant(&&Foo::Two(5));
| ^^^^^^^^^^^^^^^^^^-------------^
| |
| help: try dereferencing: `&Foo::Two(5)`
error: calling `mem::discriminant` on non-enum type `A`
--> $DIR/mem_discriminant.rs:29:5
|
29 | mem::discriminant(&A(Foo::One(0)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
--> $DIR/mem_discriminant.rs:33:5
|
33 | mem::discriminant(&ro);
| ^^^^^^^^^^^^^^^^^^---^
| |
| help: try dereferencing: `ro`
error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
--> $DIR/mem_discriminant.rs:34:5
|
34 | mem::discriminant(rro);
| ^^^^^^^^^^^^^^^^^^---^
| |
| help: try dereferencing: `*rro`
error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>`
--> $DIR/mem_discriminant.rs:35:5
|
35 | mem::discriminant(&rro);
| ^^^^^^^^^^^^^^^^^^----^
| |
| help: try dereferencing: `*rro`
error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>`
--> $DIR/mem_discriminant.rs:38:27
|
38 | ($param:expr) => (mem::discriminant($param))
| ^^^^^^^^^^^^^^^^^^^^^^^^^
...
41 | mem_discriminant_but_in_a_macro!(&rro);
| ---------------------------------------
| | |
| | help: try dereferencing: `*rro`
| in this macro invocation
error: calling `mem::discriminant` on non-enum type `&&&&&std::option::Option<i32>`
--> $DIR/mem_discriminant.rs:44:5
|
44 | mem::discriminant(&rrrrro);
| ^^^^^^^^^^^^^^^^^^-------^
| |
| help: try dereferencing: `****rrrrro`
error: calling `mem::discriminant` on non-enum type `&&&std::option::Option<i32>`
--> $DIR/mem_discriminant.rs:45:5
|
45 | mem::discriminant(*rrrrro);
| ^^^^^^^^^^^^^^^^^^-------^
| |
| help: try dereferencing: `****rrrrro`
error: aborting due to 12 previous errors

View File

@ -13,7 +13,7 @@ fn calc_idx(i: usize) -> usize {
}
fn main() {
let ns = [2, 3, 5, 7];
let ns = vec![2, 3, 5, 7];
for i in 3..10 {
println!("{}", ns[i]);
@ -76,4 +76,18 @@ fn main() {
for i in x..=x + 4 {
vec[i] += 1;
}
let arr = [1,2,3];
for i in 0..3 {
println!("{}", arr[i]);
}
for i in 0..2 {
println!("{}", arr[i]);
}
for i in 1..3 {
println!("{}", arr[i]);
}
}

View File

@ -50,5 +50,35 @@ help: consider using an iterator
76 | for <item> in vec.iter_mut().skip(x).take(4 + 1) {
| ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 5 previous errors
error: the loop variable `i` is only used to index `arr`.
--> $DIR/needless_range_loop.rs:82:14
|
82 | for i in 0..3 {
| ^^^^
help: consider using an iterator
|
82 | for <item> in &arr {
| ^^^^^^ ^^^^
error: the loop variable `i` is only used to index `arr`.
--> $DIR/needless_range_loop.rs:86:14
|
86 | for i in 0..2 {
| ^^^^
help: consider using an iterator
|
86 | for <item> in arr.iter().take(2) {
| ^^^^^^ ^^^^^^^^^^^^^^^^^^
error: the loop variable `i` is only used to index `arr`.
--> $DIR/needless_range_loop.rs:90:14
|
90 | for i in 1..3 {
| ^^^^
help: consider using an iterator
|
90 | for <item> in arr.iter().skip(1) {
| ^^^^^^ ^^^^^^^^^^^^^^^^^^
error: aborting due to 8 previous errors

View File

@ -91,3 +91,87 @@ impl V {
unimplemented!();
}
}
struct TupleReturnerOk;
impl TupleReturnerOk {
// should not trigger lint
pub fn new() -> (Self, u32) { unimplemented!(); }
}
struct TupleReturnerOk2;
impl TupleReturnerOk2 {
// should not trigger lint (it doesn't matter which element in the tuple is Self)
pub fn new() -> (u32, Self) { unimplemented!(); }
}
struct TupleReturnerOk3;
impl TupleReturnerOk3 {
// should not trigger lint (tuple can contain multiple Self)
pub fn new() -> (Self, Self) { unimplemented!(); }
}
struct TupleReturnerBad;
impl TupleReturnerBad {
// should trigger lint
pub fn new() -> (u32, u32) { unimplemented!(); }
}
struct MutPointerReturnerOk;
impl MutPointerReturnerOk {
// should not trigger lint
pub fn new() -> *mut Self { unimplemented!(); }
}
struct MutPointerReturnerOk2;
impl MutPointerReturnerOk2 {
// should not trigger lint
pub fn new() -> *const Self { unimplemented!(); }
}
struct MutPointerReturnerBad;
impl MutPointerReturnerBad {
// should trigger lint
pub fn new() -> *mut V { unimplemented!(); }
}
struct GenericReturnerOk;
impl GenericReturnerOk {
// should not trigger lint
pub fn new() -> Option<Self> { unimplemented!(); }
}
struct GenericReturnerBad;
impl GenericReturnerBad {
// should trigger lint
pub fn new() -> Option<u32> { unimplemented!(); }
}
struct NestedReturnerOk;
impl NestedReturnerOk {
// should not trigger lint
pub fn new() -> (Option<Self>, u32) { unimplemented!(); }
}
struct NestedReturnerOk2;
impl NestedReturnerOk2 {
// should not trigger lint
pub fn new() -> ((Self, u32), u32) { unimplemented!(); }
}
struct NestedReturnerOk3;
impl NestedReturnerOk3 {
// should not trigger lint
pub fn new() -> Option<(Self, u32)> { unimplemented!(); }
}

View File

@ -24,5 +24,23 @@ error: methods called `new` usually return `Self`
92 | | }
| |_____^
error: aborting due to 3 previous errors
error: methods called `new` usually return `Self`
--> $DIR/new_ret_no_self.rs:120:5
|
120 | pub fn new() -> (u32, u32) { unimplemented!(); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: methods called `new` usually return `Self`
--> $DIR/new_ret_no_self.rs:141:5
|
141 | pub fn new() -> *mut V { unimplemented!(); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: methods called `new` usually return `Self`
--> $DIR/new_ret_no_self.rs:155:5
|
155 | pub fn new() -> Option<u32> { unimplemented!(); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 6 previous errors

View File

@ -107,4 +107,13 @@ impl IgnoreUnsafeNew {
pub unsafe fn new() -> Self { IgnoreUnsafeNew }
}
#[derive(Default)]
pub struct OptionRefWrapper<'a, T: 'a>(Option<&'a T>);
impl<'a, T: 'a> OptionRefWrapper<'a, T> {
pub fn new() -> Self {
OptionRefWrapper(None)
}
}
fn main() {}

View File

@ -0,0 +1,47 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![warn(clippy::redundant_clone)]
use std::path::Path;
use std::ffi::OsString;
fn main() {
let _ = ["lorem", "ipsum"].join(" ").to_string();
let s = String::from("foo");
let _ = s.clone();
let s = String::from("foo");
let _ = s.to_string();
let s = String::from("foo");
let _ = s.to_owned();
let _ = Path::new("/a/b/").join("c").to_owned();
let _ = Path::new("/a/b/").join("c").to_path_buf();
let _ = OsString::new().to_owned();
let _ = OsString::new().to_os_string();
// Check that lint level works
#[allow(clippy::redundant_clone)] let _ = String::new().to_string();
}
#[derive(Clone)]
struct Alpha;
fn double(a: Alpha) -> (Alpha, Alpha) {
if true {
(a.clone(), a.clone())
} else {
(Alpha, a)
}
}

View File

@ -0,0 +1,111 @@
error: redundant clone
--> $DIR/redundant_clone.rs:16:41
|
16 | let _ = ["lorem", "ipsum"].join(" ").to_string();
| ^^^^^^^^^^^^ help: remove this
|
= note: `-D clippy::redundant-clone` implied by `-D warnings`
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:16:13
|
16 | let _ = ["lorem", "ipsum"].join(" ").to_string();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
--> $DIR/redundant_clone.rs:19:14
|
19 | let _ = s.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:19:13
|
19 | let _ = s.clone();
| ^
error: redundant clone
--> $DIR/redundant_clone.rs:22:14
|
22 | let _ = s.to_string();
| ^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:22:13
|
22 | let _ = s.to_string();
| ^
error: redundant clone
--> $DIR/redundant_clone.rs:25:14
|
25 | let _ = s.to_owned();
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:25:13
|
25 | let _ = s.to_owned();
| ^
error: redundant clone
--> $DIR/redundant_clone.rs:27:41
|
27 | let _ = Path::new("/a/b/").join("c").to_owned();
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:27:13
|
27 | let _ = Path::new("/a/b/").join("c").to_owned();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
--> $DIR/redundant_clone.rs:29:41
|
29 | let _ = Path::new("/a/b/").join("c").to_path_buf();
| ^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:29:13
|
29 | let _ = Path::new("/a/b/").join("c").to_path_buf();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
--> $DIR/redundant_clone.rs:31:28
|
31 | let _ = OsString::new().to_owned();
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:31:13
|
31 | let _ = OsString::new().to_owned();
| ^^^^^^^^^^^^^^^
error: redundant clone
--> $DIR/redundant_clone.rs:33:28
|
33 | let _ = OsString::new().to_os_string();
| ^^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:33:13
|
33 | let _ = OsString::new().to_os_string();
| ^^^^^^^^^^^^^^^
error: redundant clone
--> $DIR/redundant_clone.rs:43:22
|
43 | (a.clone(), a.clone())
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:43:21
|
43 | (a.clone(), a.clone())
| ^
error: aborting due to 9 previous errors

View File

@ -205,3 +205,17 @@ mod issue2894 {
}
}
}
mod existential {
struct Foo;
impl Foo {
fn bad(foos: &[Self]) -> impl Iterator<Item=&Foo> {
foos.iter()
}
fn good(foos: &[Self]) -> impl Iterator<Item=&Self> {
foos.iter()
}
}
}

View File

@ -120,5 +120,11 @@ error: unnecessary structure name repetition
119 | fn mul(self, rhs: Bad) -> Bad {
| ^^^ help: use the applicable keyword: `Self`
error: aborting due to 20 previous errors
error: unnecessary structure name repetition
--> $DIR/use_self.rs:213:54
|
213 | fn bad(foos: &[Self]) -> impl Iterator<Item=&Foo> {
| ^^^ help: use the applicable keyword: `Self`
error: aborting due to 21 previous errors