From 0e6d40a3fb1000d1105646703c60c9201f3ed5cc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 10 Mar 2018 13:32:11 +0100 Subject: [PATCH] type_alias_bounds lint: If the type alias uses an associated type without "as", suggest to use the "as" form instead. This is necessary to get rid of the type bound, and hence silence the warning. --- src/librustc/hir/mod.rs | 19 +++++++ src/librustc_lint/builtin.rs | 83 +++++++++++++++++++++++++--- src/librustc_lint/lib.rs | 2 +- src/test/ui/type-alias-bounds.rs | 17 ++++-- src/test/ui/type-alias-bounds.stderr | 60 +++++++++++++++----- 5 files changed, 151 insertions(+), 30 deletions(-) diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index f4638c23c5f..d4bfa7a1d30 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -395,6 +395,15 @@ pub enum TyParamBound { RegionTyParamBound(Lifetime), } +impl TyParamBound { + pub fn span(&self) -> Span { + match self { + &TraitTyParamBound(ref t, ..) => t.span, + &RegionTyParamBound(ref l) => l.span, + } + } +} + /// A modifier on a bound, currently this is only used for `?Sized`, where the /// modifier is `Maybe`. Negative bounds should also be handled here. #[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] @@ -570,6 +579,16 @@ pub enum WherePredicate { EqPredicate(WhereEqPredicate), } +impl WherePredicate { + pub fn span(&self) -> Span { + match self { + &WherePredicate::BoundPredicate(ref p) => p.span, + &WherePredicate::RegionPredicate(ref p) => p.span, + &WherePredicate::EqPredicate(ref p) => p.span, + } + } +} + /// A type bound, eg `for<'c> Foo: Send+Clone+'c` #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct WhereBoundPredicate { diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index bdb36ab15b6..2ea13b2cb6d 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -46,6 +46,7 @@ use syntax::attr; use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes}; use syntax_pos::{BytePos, Span, SyntaxContext}; use syntax::symbol::keywords; +use syntax::errors::DiagnosticBuilder; use rustc::hir::{self, PatKind}; use rustc::hir::intravisit::FnKind; @@ -1334,31 +1335,97 @@ impl LintPass for TypeAliasBounds { } } -impl EarlyLintPass for TypeAliasBounds { - fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) { - let type_alias_generics = match item.node { - ast::ItemKind::Ty(_, ref generics) => generics, +impl TypeAliasBounds { + fn is_type_variable_assoc(qpath: &hir::QPath) -> bool { + match *qpath { + hir::QPath::TypeRelative(ref ty, _) => { + // If this is a type variable, we found a `T::Assoc`. + match ty.node { + hir::TyPath(hir::QPath::Resolved(None, ref path)) => { + match path.def { + Def::TyParam(_) => true, + _ => false + } + } + _ => false + } + } + hir::QPath::Resolved(..) => false, + } + } + + fn suggest_changing_assoc_types(ty: &hir::Ty, err: &mut DiagnosticBuilder) { + // Access to associates types should use `::Assoc`, which does not need a + // bound. Let's see of this type does that. + + // We use an AST visitor to walk the type. + use rustc::hir::intravisit::{self, Visitor}; + use syntax::ast::NodeId; + struct WalkAssocTypes<'a, 'db> where 'db: 'a { + err: &'a mut DiagnosticBuilder<'db> + } + impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> { + fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'v> + { + intravisit::NestedVisitorMap::None + } + + fn visit_qpath(&mut self, qpath: &'v hir::QPath, id: NodeId, span: Span) { + if TypeAliasBounds::is_type_variable_assoc(qpath) { + self.err.span_help(span, + "use absolute paths (i.e., ::Assoc) to refer to associated \ + types in type aliases"); + } + intravisit::walk_qpath(self, qpath, id, span) + } + } + + // Let's go for a walk! + let mut visitor = WalkAssocTypes { err }; + visitor.visit_ty(ty); + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds { + fn check_item(&mut self, cx: &LateContext, item: &hir::Item) { + let (ty, type_alias_generics) = match item.node { + hir::ItemTy(ref ty, ref generics) => (&*ty, generics), _ => return, }; + let mut suggested_changing_assoc_types = false; // There must not be a where clause if !type_alias_generics.where_clause.predicates.is_empty() { let spans : Vec<_> = type_alias_generics.where_clause.predicates.iter() .map(|pred| pred.span()).collect(); - cx.span_lint(TYPE_ALIAS_BOUNDS, spans, + let mut err = cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, "where clauses are not enforced in type aliases"); + err.help("the clause will not be checked when the type alias is used, \ + and should be removed"); + if !suggested_changing_assoc_types { + TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); + suggested_changing_assoc_types = true; + } + err.emit(); } // The parameters must not have bounds for param in type_alias_generics.params.iter() { let spans : Vec<_> = match param { - &ast::GenericParam::Lifetime(ref l) => l.bounds.iter().map(|b| b.span).collect(), - &ast::GenericParam::Type(ref ty) => ty.bounds.iter().map(|b| b.span()).collect(), + &hir::GenericParam::Lifetime(ref l) => l.bounds.iter().map(|b| b.span).collect(), + &hir::GenericParam::Type(ref ty) => ty.bounds.iter().map(|b| b.span()).collect(), }; if !spans.is_empty() { - cx.span_lint( + let mut err = cx.struct_span_lint( TYPE_ALIAS_BOUNDS, spans, "bounds on generic parameters are not enforced in type aliases", ); + err.help("the bound will not be checked when the type alias is used, \ + and should be removed"); + if !suggested_changing_assoc_types { + TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); + suggested_changing_assoc_types = true; + } + err.emit(); } } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 38f7a0b6faa..7237d2fd5d1 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -109,7 +109,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UnusedImportBraces, AnonymousParameters, UnusedDocComment, - TypeAliasBounds, ); add_early_builtin_with_new!(sess, @@ -139,6 +138,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { MutableTransmutes, UnionsWithDropFields, UnreachablePub, + TypeAliasBounds, ); add_builtin_with_new!(sess, diff --git a/src/test/ui/type-alias-bounds.rs b/src/test/ui/type-alias-bounds.rs index 7ec3afd9c87..c1cdeef3a46 100644 --- a/src/test/ui/type-alias-bounds.rs +++ b/src/test/ui/type-alias-bounds.rs @@ -10,6 +10,7 @@ // Test ignored_generic_bounds lint warning about bounds in type aliases +// must-compile-successfully #![allow(dead_code)] use std::rc::Rc; @@ -53,12 +54,16 @@ type MySendable = Sendable; // no error here! // However, bounds *are* taken into account when accessing associated types trait Bound { type Assoc; } -type T1 = U::Assoc; -//~^ WARN bounds on generic parameters are not enforced in type aliases -type T2 where U: Bound = U::Assoc; -//~^ WARN where clauses are not enforced in type aliases -type T3 = U::Assoc; -//~^ ERROR associated type `Assoc` not found for `U` +type T1 = U::Assoc; //~ WARN not enforced in type aliases +type T2 where U: Bound = U::Assoc; //~ WARN not enforced in type aliases + +// This errors +// type T3 = U::Assoc; +// Do this instead type T4 = ::Assoc; +// Make sure the help about associatd types is not shown incorrectly +type T5 = ::Assoc; //~ WARN not enforced in type aliases +type T6 = ::std::vec::Vec; //~ WARN not enforced in type aliases + fn main() {} diff --git a/src/test/ui/type-alias-bounds.stderr b/src/test/ui/type-alias-bounds.stderr index 46aacd7321c..5288dca79be 100644 --- a/src/test/ui/type-alias-bounds.stderr +++ b/src/test/ui/type-alias-bounds.stderr @@ -1,53 +1,83 @@ warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:17:14 + --> $DIR/type-alias-bounds.rs:18:14 | LL | type SVec = Vec; | ^^^^ ^^^^ | = note: #[warn(type_alias_bounds)] on by default + = help: the bound will not be checked when the type alias is used, and should be removed warning: where clauses are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:19:21 + --> $DIR/type-alias-bounds.rs:20:21 | LL | type S2Vec where T: Send = Vec; | ^^^^^^^ + | + = help: the clause will not be checked when the type alias is used, and should be removed warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:21:19 + --> $DIR/type-alias-bounds.rs:22:19 | LL | type VVec<'b, 'a: 'b+'b> = (&'b u32, Vec<&'a i32>); | ^^ ^^ + | + = help: the bound will not be checked when the type alias is used, and should be removed warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:23:18 + --> $DIR/type-alias-bounds.rs:24:18 | LL | type WVec<'b, T: 'b+'b> = (&'b u32, Vec); | ^^ ^^ + | + = help: the bound will not be checked when the type alias is used, and should be removed warning: where clauses are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:25:25 + --> $DIR/type-alias-bounds.rs:26:25 | LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec); | ^^^^^ ^^^^^ + | + = help: the clause will not be checked when the type alias is used, and should be removed warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:56:12 + --> $DIR/type-alias-bounds.rs:57:12 | -LL | type T1 = U::Assoc; +LL | type T1 = U::Assoc; //~ WARN not enforced in type aliases | ^^^^^ + | + = help: the bound will not be checked when the type alias is used, and should be removed +help: use absolute paths (i.e., ::Assoc) to refer to associated types in type aliases + --> $DIR/type-alias-bounds.rs:57:21 + | +LL | type T1 = U::Assoc; //~ WARN not enforced in type aliases + | ^^^^^^^^ warning: where clauses are not enforced in type aliases --> $DIR/type-alias-bounds.rs:58:18 | -LL | type T2 where U: Bound = U::Assoc; +LL | type T2 where U: Bound = U::Assoc; //~ WARN not enforced in type aliases | ^^^^^^^^ - -error[E0220]: associated type `Assoc` not found for `U` - --> $DIR/type-alias-bounds.rs:60:14 | -LL | type T3 = U::Assoc; - | ^^^^^^^^ associated type `Assoc` not found + = help: the clause will not be checked when the type alias is used, and should be removed +help: use absolute paths (i.e., ::Assoc) to refer to associated types in type aliases + --> $DIR/type-alias-bounds.rs:58:29 + | +LL | type T2 where U: Bound = U::Assoc; //~ WARN not enforced in type aliases + | ^^^^^^^^ -error: aborting due to previous error +warning: bounds on generic parameters are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:66:12 + | +LL | type T5 = ::Assoc; //~ WARN not enforced in type aliases + | ^^^^^ + | + = help: the bound will not be checked when the type alias is used, and should be removed + +warning: bounds on generic parameters are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:67:12 + | +LL | type T6 = ::std::vec::Vec; //~ WARN not enforced in type aliases + | ^^^^^ + | + = help: the bound will not be checked when the type alias is used, and should be removed -If you want more information on this error, try using "rustc --explain E0220"