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"