Merge branch 'master' into dev-fmt-20190728

This commit is contained in:
Michael Wright 2019-08-01 07:02:48 +02:00
commit fd9c5a3c71
26 changed files with 445 additions and 121 deletions

View file

@ -1138,6 +1138,7 @@ Released 2018-09-13
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg

View file

@ -6,7 +6,7 @@
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
[There are 308 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are 309 lints included in this crate!](https://rust-lang.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

@ -16,13 +16,13 @@ pub enum CliError {
impl From<io::Error> for CliError {
fn from(error: io::Error) -> Self {
CliError::IoError(error)
Self::IoError(error)
}
}
impl From<walkdir::Error> for CliError {
fn from(error: walkdir::Error) -> Self {
CliError::WalkDirError(error)
Self::WalkDirError(error)
}
}

View file

@ -160,12 +160,12 @@ impl ConversionType {
/// Creates a conversion type if the type is allowed & conversion is valid
fn try_new(from: &str, to: &str) -> Option<Self> {
if UINTS.contains(&from) {
Some(ConversionType::FromUnsigned)
Some(Self::FromUnsigned)
} else if SINTS.contains(&from) {
if UINTS.contains(&to) {
Some(ConversionType::SignedToUnsigned)
Some(Self::SignedToUnsigned)
} else if SINTS.contains(&to) {
Some(ConversionType::SignedToSigned)
Some(Self::SignedToSigned)
} else {
None
}

View file

@ -48,27 +48,27 @@ pub enum Constant {
impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(&Constant::Str(ref ls), &Constant::Str(ref rs)) => ls == rs,
(&Constant::Binary(ref l), &Constant::Binary(ref r)) => l == r,
(&Constant::Char(l), &Constant::Char(r)) => l == r,
(&Constant::Int(l), &Constant::Int(r)) => l == r,
(&Constant::F64(l), &Constant::F64(r)) => {
(&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
(&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
(&Self::Char(l), &Self::Char(r)) => l == r,
(&Self::Int(l), &Self::Int(r)) => l == r,
(&Self::F64(l), &Self::F64(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
l.to_bits() == r.to_bits()
},
(&Constant::F32(l), &Constant::F32(r)) => {
(&Self::F32(l), &Self::F32(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
f64::from(l).to_bits() == f64::from(r).to_bits()
},
(&Constant::Bool(l), &Constant::Bool(r)) => l == r,
(&Constant::Vec(ref l), &Constant::Vec(ref r)) | (&Constant::Tuple(ref l), &Constant::Tuple(ref r)) => {
(&Self::Bool(l), &Self::Bool(r)) => l == r,
(&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => {
l == r
},
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
(&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
// TODO: are there inter-type equalities?
_ => false,
}
@ -82,38 +82,38 @@ impl Hash for Constant {
{
std::mem::discriminant(self).hash(state);
match *self {
Constant::Str(ref s) => {
Self::Str(ref s) => {
s.hash(state);
},
Constant::Binary(ref b) => {
Self::Binary(ref b) => {
b.hash(state);
},
Constant::Char(c) => {
Self::Char(c) => {
c.hash(state);
},
Constant::Int(i) => {
Self::Int(i) => {
i.hash(state);
},
Constant::F32(f) => {
Self::F32(f) => {
f64::from(f).to_bits().hash(state);
},
Constant::F64(f) => {
Self::F64(f) => {
f.to_bits().hash(state);
},
Constant::Bool(b) => {
Self::Bool(b) => {
b.hash(state);
},
Constant::Vec(ref v) | Constant::Tuple(ref v) => {
Self::Vec(ref v) | Self::Tuple(ref v) => {
v.hash(state);
},
Constant::Repeat(ref c, l) => {
Self::Repeat(ref c, l) => {
c.hash(state);
l.hash(state);
},
Constant::RawPtr(u) => {
Self::RawPtr(u) => {
u.hash(state);
},
Constant::Err(ref s) => {
Self::Err(ref s) => {
s.hash(state);
},
}
@ -123,25 +123,25 @@ impl Hash for Constant {
impl Constant {
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) {
(&Constant::Str(ref ls), &Constant::Str(ref rs)) => Some(ls.cmp(rs)),
(&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)),
(&Constant::Int(l), &Constant::Int(r)) => {
(&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
(&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
(&Self::Int(l), &Self::Int(r)) => {
if let ty::Int(int_ty) = cmp_type.sty {
Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
} else {
Some(l.cmp(&r))
}
},
(&Constant::F64(l), &Constant::F64(r)) => l.partial_cmp(&r),
(&Constant::F32(l), &Constant::F32(r)) => l.partial_cmp(&r),
(&Constant::Bool(ref l), &Constant::Bool(ref r)) => Some(l.cmp(r)),
(&Constant::Tuple(ref l), &Constant::Tuple(ref r)) | (&Constant::Vec(ref l), &Constant::Vec(ref r)) => l
(&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
(&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
(&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
(&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
.iter()
.zip(r.iter())
.map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
.find(|r| r.map_or(true, |o| o != Ordering::Equal))
.unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => {
(&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
match Self::partial_cmp(tcx, cmp_type, lv, rv) {
Some(Equal) => Some(ls.cmp(rs)),
x => x,

View file

@ -1,5 +1,5 @@
use rustc::hir::intravisit as visit;
use rustc::hir::*;
use rustc::hir::{self, *};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::middle::expr_use_visitor::*;
use rustc::middle::mem_categorization::{cmt_, Categorization};
@ -101,6 +101,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal {
}
}
// TODO: Replace with Map::is_argument(..) when it's fixed
fn is_argument(map: &hir::map::Map<'_>, id: HirId) -> bool {
match map.find(id) {
Some(Node::Binding(_)) => (),
_ => return false,
}
match map.find(map.get_parent_node(id)) {
Some(Node::Arg(_)) => true,
_ => false,
}
}
impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
fn consume(&mut self, _: HirId, _: Span, cmt: &cmt_<'tcx>, mode: ConsumeMode) {
if let Categorization::Local(lid) = cmt.cat {
@ -113,11 +126,13 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
fn matched_pat(&mut self, _: &Pat, _: &cmt_<'tcx>, _: MatchMode) {}
fn consume_pat(&mut self, consume_pat: &Pat, cmt: &cmt_<'tcx>, _: ConsumeMode) {
let map = &self.cx.tcx.hir();
if map.is_argument(consume_pat.hir_id) {
if is_argument(map, consume_pat.hir_id) {
// Skip closure arguments
if let Some(Node::Expr(..)) = map.find(map.get_parent_node(consume_pat.hir_id)) {
let parent_id = map.get_parent_node(consume_pat.hir_id);
if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
return;
}
if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) {
self.set.insert(consume_pat.hir_id);
}

View file

@ -143,20 +143,20 @@ impl FloatFormat {
fn new(s: &str) -> Self {
s.chars()
.find_map(|x| match x {
'e' => Some(FloatFormat::LowerExp),
'E' => Some(FloatFormat::UpperExp),
'e' => Some(Self::LowerExp),
'E' => Some(Self::UpperExp),
_ => None,
})
.unwrap_or(FloatFormat::Normal)
.unwrap_or(Self::Normal)
}
fn format<T>(&self, f: T) -> String
where
T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
{
match self {
FloatFormat::LowerExp => format!("{:e}", f),
FloatFormat::UpperExp => format!("{:E}", f),
FloatFormat::Normal => format!("{}", f),
Self::LowerExp => format!("{:e}", f),
Self::UpperExp => format!("{:E}", f),
Self::Normal => format!("{}", f),
}
}
}

View file

@ -263,6 +263,7 @@ pub mod strings;
pub mod suspicious_trait_impl;
pub mod swap;
pub mod temporary_assignment;
pub mod trait_bounds;
pub mod transmute;
pub mod transmuting_null;
pub mod trivially_copy_pass_by_ref;
@ -588,6 +589,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
reg.register_late_lint_pass(box checked_conversions::CheckedConversions);
reg.register_late_lint_pass(box integer_division::IntegerDivision);
reg.register_late_lint_pass(box inherent_to_string::InherentToString);
reg.register_late_lint_pass(box trait_bounds::TraitBounds);
reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
arithmetic::FLOAT_ARITHMETIC,
@ -858,6 +860,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
swap::ALMOST_SWAPPED,
swap::MANUAL_SWAP,
temporary_assignment::TEMPORARY_ASSIGNMENT,
trait_bounds::TYPE_REPETITION_IN_BOUNDS,
transmute::CROSSPOINTER_TRANSMUTE,
transmute::TRANSMUTE_BYTES_TO_STR,
transmute::TRANSMUTE_INT_TO_BOOL,
@ -1039,6 +1042,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
reference::REF_IN_DEREF,
swap::MANUAL_SWAP,
temporary_assignment::TEMPORARY_ASSIGNMENT,
trait_bounds::TYPE_REPETITION_IN_BOUNDS,
transmute::CROSSPOINTER_TRANSMUTE,
transmute::TRANSMUTE_BYTES_TO_STR,
transmute::TRANSMUTE_INT_TO_BOOL,

View file

@ -115,8 +115,8 @@ impl Radix {
/// Returns a reasonable digit group size for this radix.
crate fn suggest_grouping(&self) -> usize {
match *self {
Radix::Binary | Radix::Hexadecimal => 4,
Radix::Octal | Radix::Decimal => 3,
Self::Binary | Self::Hexadecimal => 4,
Self::Octal | Self::Decimal => 3,
}
}
}
@ -285,7 +285,7 @@ enum WarningType {
impl WarningType {
crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
match self {
WarningType::MistypedLiteralSuffix => span_lint_and_sugg(
Self::MistypedLiteralSuffix => span_lint_and_sugg(
cx,
MISTYPED_LITERAL_SUFFIXES,
span,
@ -294,7 +294,7 @@ impl WarningType {
grouping_hint.to_string(),
Applicability::MaybeIncorrect,
),
WarningType::UnreadableLiteral => span_lint_and_sugg(
Self::UnreadableLiteral => span_lint_and_sugg(
cx,
UNREADABLE_LITERAL,
span,
@ -303,7 +303,7 @@ impl WarningType {
grouping_hint.to_owned(),
Applicability::MachineApplicable,
),
WarningType::LargeDigitGroups => span_lint_and_sugg(
Self::LargeDigitGroups => span_lint_and_sugg(
cx,
LARGE_DIGIT_GROUPS,
span,
@ -312,7 +312,7 @@ impl WarningType {
grouping_hint.to_owned(),
Applicability::MachineApplicable,
),
WarningType::InconsistentDigitGrouping => span_lint_and_sugg(
Self::InconsistentDigitGrouping => span_lint_and_sugg(
cx,
INCONSISTENT_DIGIT_GROUPING,
span,
@ -321,7 +321,7 @@ impl WarningType {
grouping_hint.to_owned(),
Applicability::MachineApplicable,
),
WarningType::DecimalRepresentation => span_lint_and_sugg(
Self::DecimalRepresentation => span_lint_and_sugg(
cx,
DECIMAL_LITERAL_REPRESENTATION,
span,

View file

@ -2505,14 +2505,14 @@ impl SelfKind {
let is_actually_self = |ty| is_self_ty(ty) || SpanlessEq::new(cx).eq_ty(ty, self_ty);
if is_self(arg) {
match self {
SelfKind::Value => is_actually_self(ty),
SelfKind::Ref | SelfKind::RefMut => {
Self::Value => is_actually_self(ty),
Self::Ref | Self::RefMut => {
if allow_value_for_ref && is_actually_self(ty) {
return true;
}
match ty.node {
hir::TyKind::Rptr(_, ref mt_ty) => {
let mutability_match = if self == SelfKind::Ref {
let mutability_match = if self == Self::Ref {
mt_ty.mutbl == hir::MutImmutable
} else {
mt_ty.mutbl == hir::MutMutable
@ -2526,20 +2526,20 @@ impl SelfKind {
}
} else {
match self {
SelfKind::Value => false,
SelfKind::Ref => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASREF_TRAIT),
SelfKind::RefMut => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASMUT_TRAIT),
SelfKind::No => true,
Self::Value => false,
Self::Ref => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASREF_TRAIT),
Self::RefMut => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASMUT_TRAIT),
Self::No => true,
}
}
}
fn description(self) -> &'static str {
match self {
SelfKind::Value => "self by value",
SelfKind::Ref => "self by reference",
SelfKind::RefMut => "self by mutable reference",
SelfKind::No => "no self",
Self::Value => "self by value",
Self::Ref => "self by reference",
Self::RefMut => "self by mutable reference",
Self::No => "no self",
}
}
}
@ -2609,8 +2609,8 @@ fn single_segment_ty(ty: &hir::Ty) -> Option<&hir::PathSegment> {
impl Convention {
fn check(&self, other: &str) -> bool {
match *self {
Convention::Eq(this) => this == other,
Convention::StartsWith(this) => other.starts_with(this) && this != other,
Self::Eq(this) => this == other,
Self::StartsWith(this) => other.starts_with(this) && this != other,
}
}
}
@ -2618,8 +2618,8 @@ impl Convention {
impl fmt::Display for Convention {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
Convention::Eq(this) => this.fmt(f),
Convention::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
Self::Eq(this) => this.fmt(f),
Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
}
}
}
@ -2636,11 +2636,11 @@ impl OutType {
fn matches(self, cx: &LateContext<'_, '_>, ty: &hir::FunctionRetTy) -> bool {
let is_unit = |ty: &hir::Ty| SpanlessEq::new(cx).eq_ty_kind(&ty.node, &hir::TyKind::Tup(vec![].into()));
match (self, ty) {
(OutType::Unit, &hir::DefaultReturn(_)) => true,
(OutType::Unit, &hir::Return(ref ty)) if is_unit(ty) => true,
(OutType::Bool, &hir::Return(ref ty)) if is_bool(ty) => true,
(OutType::Any, &hir::Return(ref ty)) if !is_unit(ty) => true,
(OutType::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyKind::Rptr(_, _)),
(Self::Unit, &hir::DefaultReturn(_)) => true,
(Self::Unit, &hir::Return(ref ty)) if is_unit(ty) => true,
(Self::Bool, &hir::Return(ref ty)) if is_bool(ty) => true,
(Self::Any, &hir::Return(ref ty)) if !is_unit(ty) => true,
(Self::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyKind::Rptr(_, _)),
_ => false,
}
}

View file

@ -84,6 +84,7 @@ declare_clippy_lint! {
"referencing const with interior mutability"
}
#[allow(dead_code)]
#[derive(Copy, Clone)]
enum Source {
Item { item: Span },
@ -94,12 +95,12 @@ enum Source {
impl Source {
fn lint(&self) -> (&'static Lint, &'static str, Span) {
match self {
Source::Item { item } | Source::Assoc { item, .. } => (
Self::Item { item } | Self::Assoc { item, .. } => (
DECLARE_INTERIOR_MUTABLE_CONST,
"a const item should never be interior mutable",
*item,
),
Source::Expr { expr } => (
Self::Expr { expr } => (
BORROW_INTERIOR_MUTABLE_CONST,
"a const item with interior mutability should not be borrowed",
*expr,

View file

@ -133,8 +133,8 @@ enum Method {
impl Method {
fn suggestion(self) -> &'static str {
match self {
Method::Offset => "add",
Method::WrappingOffset => "wrapping_add",
Self::Offset => "add",
Self::WrappingOffset => "wrapping_add",
}
}
}
@ -142,8 +142,8 @@ impl Method {
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Method::Offset => write!(f, "offset"),
Method::WrappingOffset => write!(f, "wrapping_offset"),
Self::Offset => write!(f, "offset"),
Self::WrappingOffset => write!(f, "wrapping_offset"),
}
}
}

View file

@ -0,0 +1,77 @@
use crate::utils::{in_macro, snippet, span_help_and_lint, SpanlessHash};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, impl_lint_pass};
use rustc_data_structures::fx::FxHashMap;
#[derive(Copy, Clone)]
pub struct TraitBounds;
declare_clippy_lint! {
/// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
///
/// **Why is this bad?** Repeating the type for every bound makes the code
/// less readable than combining the bounds
///
/// **Example:**
/// ```rust
/// pub fn foo<T>(t: T) where T: Copy, T: Clone
/// ```
///
/// Could be written as:
///
/// ```rust
/// pub fn foo<T>(t: T) where T: Copy + Clone
/// ```
pub TYPE_REPETITION_IN_BOUNDS,
complexity,
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
}
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TraitBounds {
fn check_generics(&mut self, cx: &LateContext<'a, 'tcx>, gen: &'tcx Generics) {
if in_macro(gen.span) {
return;
}
let hash = |ty| -> u64 {
let mut hasher = SpanlessHash::new(cx, cx.tables);
hasher.hash_ty(ty);
hasher.finish()
};
let mut map = FxHashMap::default();
for bound in &gen.where_clause.predicates {
if let WherePredicate::BoundPredicate(ref p) = bound {
let h = hash(&p.bounded_ty);
if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) {
let mut hint_string = format!(
"consider combining the bounds: `{}:",
snippet(cx, p.bounded_ty.span, "_")
);
for b in v.iter() {
if let GenericBound::Trait(ref poly_trait_ref, _) = b {
let path = &poly_trait_ref.trait_ref.path;
hint_string.push_str(&format!(" {} +", path));
}
}
for b in p.bounds.iter() {
if let GenericBound::Trait(ref poly_trait_ref, _) = b {
let path = &poly_trait_ref.trait_ref.path;
hint_string.push_str(&format!(" {} +", path));
}
}
hint_string.truncate(hint_string.len() - 2);
hint_string.push('`');
span_help_and_lint(
cx,
TYPE_REPETITION_IN_BOUNDS,
p.span,
"this type has already been used as a bound predicate",
&hint_string,
);
}
}
}
}
}

View file

@ -1725,10 +1725,10 @@ impl PartialEq for FullInt {
impl PartialOrd for FullInt {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(match (self, other) {
(&FullInt::S(s), &FullInt::S(o)) => s.cmp(&o),
(&FullInt::U(s), &FullInt::U(o)) => s.cmp(&o),
(&FullInt::S(s), &FullInt::U(o)) => Self::cmp_s_u(s, o),
(&FullInt::U(s), &FullInt::S(o)) => Self::cmp_s_u(o, s).reverse(),
(&Self::S(s), &Self::S(o)) => s.cmp(&o),
(&Self::U(s), &Self::U(o)) => s.cmp(&o),
(&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o),
(&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(),
})
}
}

View file

@ -51,9 +51,11 @@ declare_lint_pass!(UseSelf => [USE_SELF]);
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path) {
fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path, last_segment: Option<&PathSegment>) {
let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG));
// Path segments only include actual path, no methods or fields.
let last_path_span = path.segments.last().expect(SEGMENTS_MSG).ident.span;
let last_path_span = last_segment.ident.span;
// Only take path up to the end of last_path_span.
let span = path.span.with_hi(last_path_span.hi());
@ -80,22 +82,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TraitImplTyVisitor<'a, 'tcx> {
let trait_ty = self.trait_type_walker.next();
let impl_ty = self.impl_type_walker.next();
if let TyKind::Path(QPath::Resolved(_, path)) = &t.node {
if_chain! {
if let TyKind::Path(QPath::Resolved(_, path)) = &t.node;
// The implementation and trait types don't match which means that
// the concrete type was specified by the implementation
if impl_ty != trait_ty {
if let Some(impl_ty) = impl_ty {
if self.item_type == impl_ty {
let is_self_ty = if let def::Res::SelfTy(..) = path.res {
true
} else {
false
};
if !is_self_ty {
span_use_self_lint(self.cx, path);
}
}
if impl_ty != trait_ty;
if let Some(impl_ty) = impl_ty;
if self.item_type == impl_ty;
then {
match path.res {
def::Res::SelfTy(..) => {},
_ => span_use_self_lint(self.cx, path, None)
}
}
}
@ -220,15 +218,35 @@ struct UseSelfVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
if path.segments.len() >= 2 {
let last_but_one = &path.segments[path.segments.len() - 2];
if last_but_one.ident.name != kw::SelfUpper {
let enum_def_id = match path.res {
Res::Def(DefKind::Variant, variant_def_id) =>
self.cx.tcx.parent(variant_def_id),
Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => {
let variant_def_id = self.cx.tcx.parent(ctor_def_id);
variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id))
}
_ => None
};
if self.item_path.res.opt_def_id() == enum_def_id {
span_use_self_lint(self.cx, path, Some(last_but_one));
}
}
}
if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
if self.item_path.res == path.res {
span_use_self_lint(self.cx, path);
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, CtorKind::Fn), ctor_did) = path.res {
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_did) {
span_use_self_lint(self.cx, path);
span_use_self_lint(self.cx, path, None);
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, CtorKind::Fn), ctor_def_id) = path.res {
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) {
span_use_self_lint(self.cx, path, None);
}
}
}
walk_path(self, path);
}

View file

@ -44,15 +44,15 @@ pub enum Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
Error::Io(ref err) => err.fmt(f),
Error::Toml(ref err) => err.fmt(f),
Self::Io(ref err) => err.fmt(f),
Self::Toml(ref err) => err.fmt(f),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::Io(e)
Self::Io(e)
}
}

View file

@ -438,9 +438,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(fun);
self.hash_exprs(args);
},
ExprKind::Cast(ref e, ref _ty) | ExprKind::Type(ref e, ref _ty) => {
ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => {
self.hash_expr(e);
// TODO: _ty
self.hash_ty(ty);
},
ExprKind::Closure(cap, _, eid, _, _) => {
match cap {
@ -512,7 +512,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(e);
}
},
ExprKind::Tup(ref v) | ExprKind::Array(ref v) => {
ExprKind::Tup(ref tup) => {
self.hash_exprs(tup);
},
ExprKind::Array(ref v) => {
self.hash_exprs(v);
},
ExprKind::Unary(lop, ref le) => {
@ -574,4 +577,100 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
},
}
}
pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
std::mem::discriminant(&lifetime.name).hash(&mut self.s);
if let LifetimeName::Param(ref name) = lifetime.name {
std::mem::discriminant(name).hash(&mut self.s);
match name {
ParamName::Plain(ref ident) => {
ident.name.hash(&mut self.s);
},
ParamName::Fresh(ref size) => {
size.hash(&mut self.s);
},
ParamName::Error => {},
}
}
}
pub fn hash_ty(&mut self, ty: &Ty) {
self.hash_tykind(&ty.node);
}
pub fn hash_tykind(&mut self, ty: &TyKind) {
std::mem::discriminant(ty).hash(&mut self.s);
match ty {
TyKind::Slice(ty) => {
self.hash_ty(ty);
},
TyKind::Array(ty, anon_const) => {
self.hash_ty(ty);
self.hash_expr(&self.cx.tcx.hir().body(anon_const.body).value);
},
TyKind::Ptr(mut_ty) => {
self.hash_ty(&mut_ty.ty);
mut_ty.mutbl.hash(&mut self.s);
},
TyKind::Rptr(lifetime, mut_ty) => {
self.hash_lifetime(lifetime);
self.hash_ty(&mut_ty.ty);
mut_ty.mutbl.hash(&mut self.s);
},
TyKind::BareFn(bfn) => {
bfn.unsafety.hash(&mut self.s);
bfn.abi.hash(&mut self.s);
for arg in &bfn.decl.inputs {
self.hash_ty(&arg);
}
match bfn.decl.output {
FunctionRetTy::DefaultReturn(_) => {
().hash(&mut self.s);
},
FunctionRetTy::Return(ref ty) => {
self.hash_ty(ty);
},
}
bfn.decl.c_variadic.hash(&mut self.s);
},
TyKind::Tup(ty_list) => {
for ty in ty_list {
self.hash_ty(ty);
}
},
TyKind::Path(qpath) => match qpath {
QPath::Resolved(ref maybe_ty, ref path) => {
if let Some(ref ty) = maybe_ty {
self.hash_ty(ty);
}
for segment in &path.segments {
segment.ident.name.hash(&mut self.s);
}
},
QPath::TypeRelative(ref ty, ref segment) => {
self.hash_ty(ty);
segment.ident.name.hash(&mut self.s);
},
},
TyKind::Def(_, arg_list) => {
for arg in arg_list {
match arg {
GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
GenericArg::Type(ref ty) => self.hash_ty(&ty),
GenericArg::Const(ref ca) => {
self.hash_expr(&self.cx.tcx.hir().body(ca.value.body).value);
},
}
}
},
TyKind::TraitObject(_, lifetime) => {
self.hash_lifetime(lifetime);
},
TyKind::Typeof(anon_const) => {
self.hash_expr(&self.cx.tcx.hir().body(anon_const.body).value);
},
TyKind::CVarArgs(lifetime) => self.hash_lifetime(lifetime),
TyKind::Err | TyKind::Infer | TyKind::Never => {},
}
}
}

View file

@ -6,7 +6,7 @@ pub use lint::Lint;
pub use lint::LINT_LEVELS;
// begin lint list, do not remove this comment, its used in `update_lints`
pub const ALL_LINTS: [Lint; 308] = [
pub const ALL_LINTS: [Lint; 309] = [
Lint {
name: "absurd_extreme_comparisons",
group: "correctness",
@ -1848,6 +1848,13 @@ pub const ALL_LINTS: [Lint; 308] = [
deprecation: None,
module: "types",
},
Lint {
name: "type_repetition_in_bounds",
group: "complexity",
desc: "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`",
deprecation: None,
module: "trait_bounds",
},
Lint {
name: "unicode_not_nfc",
group: "pedantic",

View file

@ -55,3 +55,38 @@ fn test_similarly_named_function() {
forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name
std::mem::forget(&SomeStruct);
}
#[derive(Copy, Clone)]
pub struct Error;
fn produce_half_owl_error() -> Result<(), Error> {
Ok(())
}
fn produce_half_owl_ok() -> Result<bool, ()> {
Ok(true)
}
#[allow(dead_code)]
fn test_owl_result() -> Result<(), ()> {
produce_half_owl_error().map_err(|_| ())?;
produce_half_owl_ok().map(|_| ())?;
// the following should not be linted,
// we should not force users to use toilet closures
// to produce owl results when drop is more convenient
produce_half_owl_error().map_err(drop)?;
produce_half_owl_ok().map_err(drop)?;
Ok(())
}
#[allow(dead_code)]
fn test_owl_result_2() -> Result<u8, ()> {
produce_half_owl_error().map_err(|_| ())?;
produce_half_owl_ok().map(|_| ())?;
// the following should not be linted,
// we should not force users to use toilet closures
// to produce owl results when drop is more convenient
produce_half_owl_error().map_err(drop)?;
produce_half_owl_ok().map(drop)?;
Ok(1)
}

View file

@ -8,8 +8,7 @@ const ONE: f32 = ZERO + 1.0;
fn twice<T>(x: T) -> T
where
T: Add<T, Output = T>,
T: Copy,
T: Add<T, Output = T> + Copy,
{
x + x
}

View file

@ -1,36 +1,36 @@
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:60:5
--> $DIR/float_cmp.rs:59:5
|
LL | ONE as f64 != 2.0;
| ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error`
|
= note: `-D clippy::float-cmp` implied by `-D warnings`
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:60:5
--> $DIR/float_cmp.rs:59:5
|
LL | ONE as f64 != 2.0;
| ^^^^^^^^^^^^^^^^^
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:65:5
--> $DIR/float_cmp.rs:64:5
|
LL | x == 1.0;
| ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error`
|
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:65:5
--> $DIR/float_cmp.rs:64:5
|
LL | x == 1.0;
| ^^^^^^^^
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:68:5
--> $DIR/float_cmp.rs:67:5
|
LL | twice(x) != twice(ONE as f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error`
|
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:68:5
--> $DIR/float_cmp.rs:67:5
|
LL | twice(x) != twice(ONE as f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -0,0 +1,19 @@
#[deny(clippy::type_repetition_in_bounds)]
pub fn foo<T>(_t: T)
where
T: Copy,
T: Clone,
{
unimplemented!();
}
pub fn bar<T, U>(_t: T, _u: U)
where
T: Copy,
U: Clone,
{
unimplemented!();
}
fn main() {}

View file

@ -0,0 +1,15 @@
error: this type has already been used as a bound predicate
--> $DIR/type_repetition_in_bounds.rs:6:5
|
LL | T: Clone,
| ^^^^^^^^
|
note: lint level defined here
--> $DIR/type_repetition_in_bounds.rs:1:8
|
LL | #[deny(clippy::type_repetition_in_bounds)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider combining the bounds: `T: Copy + Clone`
error: aborting due to previous error

View file

@ -265,6 +265,8 @@ mod nesting {
enum Enum {
A,
B(u64),
C { field: bool }
}
impl Enum {
fn method() {
@ -272,6 +274,12 @@ mod nesting {
use self::Enum::*; // Issue 3425
static STATIC: Enum = Enum::A; // Can't use Self as type
}
fn method2() {
let _ = Self::B(42);
let _ = Self::C { field: true };
let _ = Self::A;
}
}
}

View file

@ -265,6 +265,8 @@ mod nesting {
enum Enum {
A,
B(u64),
C { field: bool }
}
impl Enum {
fn method() {
@ -272,6 +274,12 @@ mod nesting {
use self::Enum::*; // Issue 3425
static STATIC: Enum = Enum::A; // Can't use Self as type
}
fn method2() {
let _ = Enum::B(42);
let _ = Enum::C { field: true };
let _ = Enum::A;
}
}
}

View file

@ -175,22 +175,40 @@ LL | Bar { foo: Foo {} }
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:304:13
--> $DIR/use_self.rs:279:21
|
LL | let _ = Enum::B(42);
| ^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:280:21
|
LL | let _ = Enum::C { field: true };
| ^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:281:21
|
LL | let _ = Enum::A;
| ^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:312:13
|
LL | nested::A::fun_1();
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:305:13
--> $DIR/use_self.rs:313:13
|
LL | nested::A::A;
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:307:13
--> $DIR/use_self.rs:315:13
|
LL | nested::A {};
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: aborting due to 31 previous errors
error: aborting due to 34 previous errors