Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
183e19a1c3
10
.travis.yml
10
.travis.yml
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
4
bors.toml
Normal file
@ -0,0 +1,4 @@
|
||||
status = [
|
||||
"continuous-integration/travis-ci/push",
|
||||
"continuous-integration/appveyor/branch"
|
||||
]
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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, it’s 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,
|
||||
]);
|
||||
|
@ -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*
|
||||
}
|
||||
|
95
clippy_lints/src/mem_discriminant.rs
Normal file
95
clippy_lints/src/mem_discriminant.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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), _) => {
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
|
290
clippy_lints/src/redundant_clone.rs
Normal file
290
clippy_lints/src/redundant_clone.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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()?
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
/// ```
|
||||
///
|
||||
|
@ -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.
|
||||
|
@ -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"];
|
||||
|
69
clippy_lints/src/wildcard_dependencies.rs
Normal file
69
clippy_lints/src/wildcard_dependencies.rs
Normal 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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
55
tests/ui/mem_discriminant.rs
Normal file
55
tests/ui/mem_discriminant.rs
Normal 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);
|
||||
}
|
104
tests/ui/mem_discriminant.stderr
Normal file
104
tests/ui/mem_discriminant.stderr
Normal 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
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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!(); }
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {}
|
||||
|
47
tests/ui/redundant_clone.rs
Normal file
47
tests/ui/redundant_clone.rs
Normal 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)
|
||||
}
|
||||
}
|
111
tests/ui/redundant_clone.stderr
Normal file
111
tests/ui/redundant_clone.stderr
Normal 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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user