From 81130ed4b104adf28921120416dcc9db3253c996 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 10 Mar 2018 11:22:55 +0100 Subject: [PATCH 1/5] Split param-bounds-ignored into two, it was testing two independent things Also, tweak the test for ignored type aliases such that replacing the type alias by a newtype struct leads to a well-formed type definition, and errors when used the way the type alias is used. --- ...s-ignored.rs => higher-lifetime-bounds.rs} | 26 +--------- ...d.stderr => higher-lifetime-bounds.stderr} | 48 ++++------------- src/test/ui/type-alias-bounds.rs | 51 +++++++++++++++++++ src/test/ui/type-alias-bounds.stderr | 32 ++++++++++++ 4 files changed, 95 insertions(+), 62 deletions(-) rename src/test/ui/{param-bounds-ignored.rs => higher-lifetime-bounds.rs} (77%) rename src/test/ui/{param-bounds-ignored.stderr => higher-lifetime-bounds.stderr} (61%) create mode 100644 src/test/ui/type-alias-bounds.rs create mode 100644 src/test/ui/type-alias-bounds.stderr diff --git a/src/test/ui/param-bounds-ignored.rs b/src/test/ui/higher-lifetime-bounds.rs similarity index 77% rename from src/test/ui/param-bounds-ignored.rs rename to src/test/ui/higher-lifetime-bounds.rs index 94bcdec945035..70b3b34fbd8fc 100644 --- a/src/test/ui/param-bounds-ignored.rs +++ b/src/test/ui/higher-lifetime-bounds.rs @@ -10,31 +10,7 @@ #![allow(dead_code, non_camel_case_types)] -use std::rc::Rc; - -type SVec = Vec; -//~^ WARN bounds on generic parameters are ignored in type aliases -type VVec<'b, 'a: 'b+'b> = Vec<&'a i32>; -//~^ WARN bounds on generic parameters are ignored in type aliases -type WVec<'b, T: 'b+'b> = Vec; -//~^ WARN bounds on generic parameters are ignored in type aliases -type W2Vec<'b, T> where T: 'b, T: 'b = Vec; -//~^ WARN where clauses are ignored in type aliases - -fn foo<'a>(y: &'a i32) { - // If the bounds above would matter, the code below would be rejected. - let mut x : SVec<_> = Vec::new(); - x.push(Rc::new(42)); - - let mut x : VVec<'static, 'a> = Vec::new(); - x.push(y); - - let mut x : WVec<'static, & 'a i32> = Vec::new(); - x.push(y); - - let mut x : W2Vec<'static, & 'a i32> = Vec::new(); - x.push(y); -} +// Test that bounds on higher-kinded lifetime binders are rejected. fn bar1<'a, 'b>( x: &'a i32, diff --git a/src/test/ui/param-bounds-ignored.stderr b/src/test/ui/higher-lifetime-bounds.stderr similarity index 61% rename from src/test/ui/param-bounds-ignored.stderr rename to src/test/ui/higher-lifetime-bounds.stderr index 657fec54f9608..82c0074743604 100644 --- a/src/test/ui/param-bounds-ignored.stderr +++ b/src/test/ui/higher-lifetime-bounds.stderr @@ -1,94 +1,68 @@ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:42:22 + --> $DIR/higher-lifetime-bounds.rs:18:22 | LL | f: for<'xa, 'xb: 'xa+'xa> fn(&'xa i32, &'xb i32) -> &'xa i32) | ^^^ ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:50:34 + --> $DIR/higher-lifetime-bounds.rs:26:34 | LL | fn bar2<'a, 'b, F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>( | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:65:28 + --> $DIR/higher-lifetime-bounds.rs:41:28 | LL | where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32 | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:77:25 + --> $DIR/higher-lifetime-bounds.rs:53:25 | LL | where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32 | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:85:28 + --> $DIR/higher-lifetime-bounds.rs:61:28 | LL | struct S1 Fn(&'xa i32, &'xb i32) -> &'xa i32>(F); | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:87:40 + --> $DIR/higher-lifetime-bounds.rs:63:40 | LL | struct S2(F) where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32; | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:89:37 + --> $DIR/higher-lifetime-bounds.rs:65:37 | LL | struct S3(F) where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32; | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:92:29 + --> $DIR/higher-lifetime-bounds.rs:68:29 | LL | struct S_fnty(for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32); | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:95:29 + --> $DIR/higher-lifetime-bounds.rs:71:29 | LL | type T1 = Box Fn(&'xa i32, &'xb i32) -> &'xa i32>; | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:99:34 + --> $DIR/higher-lifetime-bounds.rs:75:34 | LL | let _ : Option fn(&'xa i32, &'xb i32) -> &'xa i32> = None; | ^^^ error: lifetime bounds cannot be used in this context - --> $DIR/param-bounds-ignored.rs:101:38 + --> $DIR/higher-lifetime-bounds.rs:77:38 | LL | let _ : Option Fn(&'xa i32, &'xb i32) -> &'xa i32>> = None; | ^^^ -warning: bounds on generic parameters are ignored in type aliases - --> $DIR/param-bounds-ignored.rs:15:14 - | -LL | type SVec = Vec; - | ^^^^ ^^^^ - | - = note: #[warn(ignored_generic_bounds)] on by default - -warning: bounds on generic parameters are ignored in type aliases - --> $DIR/param-bounds-ignored.rs:17:19 - | -LL | type VVec<'b, 'a: 'b+'b> = Vec<&'a i32>; - | ^^ ^^ - -warning: bounds on generic parameters are ignored in type aliases - --> $DIR/param-bounds-ignored.rs:19:18 - | -LL | type WVec<'b, T: 'b+'b> = Vec; - | ^^ ^^ - -warning: where clauses are ignored in type aliases - --> $DIR/param-bounds-ignored.rs:21:25 - | -LL | type W2Vec<'b, T> where T: 'b, T: 'b = Vec; - | ^^^^^ ^^^^^ - error: aborting due to 11 previous errors diff --git a/src/test/ui/type-alias-bounds.rs b/src/test/ui/type-alias-bounds.rs new file mode 100644 index 0000000000000..ed368e8f1500e --- /dev/null +++ b/src/test/ui/type-alias-bounds.rs @@ -0,0 +1,51 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test ignored_generic_bounds lint warning about bounds in type aliases + +// must-compile-successfully +#![allow(dead_code)] + +use std::rc::Rc; + +type SVec = Vec; +//~^ WARN bounds on generic parameters are ignored in type aliases +type S2Vec where T: Send = Vec; +//~^ WARN where clauses are ignored in type aliases +type VVec<'b, 'a: 'b+'b> = (&'b u32, Vec<&'a i32>); +//~^ WARN bounds on generic parameters are ignored in type aliases +type WVec<'b, T: 'b+'b> = (&'b u32, Vec); +//~^ WARN bounds on generic parameters are ignored in type aliases +type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec); +//~^ WARN where clauses are ignored in type aliases + +static STATIC : u32 = 0; + +fn foo<'a>(y: &'a i32) { + // If any of the bounds above would matter, the code below would be rejected. + // This can be seen when replacing the type aliases above by newtype structs. + // (The type aliases have no unused parameters to make that a valid transformation.) + let mut x : SVec<_> = Vec::new(); + x.push(Rc::new(42)); // is not send + + let mut x : S2Vec<_> = Vec::new(); + x.push(Rc::new(42)); // is not send + + let mut x : VVec<'static, 'a> = (&STATIC, Vec::new()); + x.1.push(y); // 'a: 'static does not hold + + let mut x : WVec<'static, &'a i32> = (&STATIC, Vec::new()); + x.1.push(y); // &'a i32: 'static does not hold + + let mut x : W2Vec<'static, &'a i32> = (&STATIC, Vec::new()); + x.1.push(y); // &'a i32: 'static does not hold +} + +fn main() {} diff --git a/src/test/ui/type-alias-bounds.stderr b/src/test/ui/type-alias-bounds.stderr new file mode 100644 index 0000000000000..5237f3c19efcd --- /dev/null +++ b/src/test/ui/type-alias-bounds.stderr @@ -0,0 +1,32 @@ +warning: bounds on generic parameters are ignored in type aliases + --> $DIR/type-alias-bounds.rs:18:14 + | +LL | type SVec = Vec; + | ^^^^ ^^^^ + | + = note: #[warn(ignored_generic_bounds)] on by default + +warning: where clauses are ignored in type aliases + --> $DIR/type-alias-bounds.rs:20:21 + | +LL | type S2Vec where T: Send = Vec; + | ^^^^^^^ + +warning: bounds on generic parameters are ignored in type aliases + --> $DIR/type-alias-bounds.rs:22:19 + | +LL | type VVec<'b, 'a: 'b+'b> = (&'b u32, Vec<&'a i32>); + | ^^ ^^ + +warning: bounds on generic parameters are ignored in type aliases + --> $DIR/type-alias-bounds.rs:24:18 + | +LL | type WVec<'b, T: 'b+'b> = (&'b u32, Vec); + | ^^ ^^ + +warning: where clauses are ignored in type aliases + --> $DIR/type-alias-bounds.rs:26:25 + | +LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec); + | ^^^^^ ^^^^^ + From 562b44d8c3dbcf7c36209774e50fe833e65a98d6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 10 Mar 2018 11:43:51 +0100 Subject: [PATCH 2/5] Rename ignored_generic_bounds -> type_alias_bounds First of all, the lint is specific for type aliases. Second, it turns out the bounds are not entirely ignored but actually used when accessing associated types. So change the wording of the lint, and adapt its name to reality. The lint has never been on stable or beta, so renaming is safe. --- src/librustc_lint/builtin.rs | 25 +++++------ src/librustc_lint/lib.rs | 2 +- src/test/compile-fail/issue-17994.rs | 2 +- .../compile-fail/private-in-public-warn.rs | 4 +- src/test/ui/type-alias-bounds.rs | 25 ++++++++--- src/test/ui/type-alias-bounds.stderr | 43 ++++++++++++++----- 6 files changed, 68 insertions(+), 33 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index c6698cbd00689..bdb36ab15b624 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1316,24 +1316,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub { } } -/// Lint for trait and lifetime bounds that are (accidentally) accepted by the parser, but -/// ignored later. +/// Lint for trait and lifetime bounds in type aliases being mostly ignored: +/// They are relevant when using associated types, but otherwise neither checked +/// at definition site nor enforced at use site. -pub struct IgnoredGenericBounds; +pub struct TypeAliasBounds; declare_lint! { - IGNORED_GENERIC_BOUNDS, + TYPE_ALIAS_BOUNDS, Warn, - "these generic bounds are ignored" + "bounds in type aliases are not enforced" } -impl LintPass for IgnoredGenericBounds { +impl LintPass for TypeAliasBounds { fn get_lints(&self) -> LintArray { - lint_array!(IGNORED_GENERIC_BOUNDS) + lint_array!(TYPE_ALIAS_BOUNDS) } } -impl EarlyLintPass for IgnoredGenericBounds { +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, @@ -1343,8 +1344,8 @@ impl EarlyLintPass for IgnoredGenericBounds { 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(IGNORED_GENERIC_BOUNDS, spans, - "where clauses are ignored in type aliases"); + cx.span_lint(TYPE_ALIAS_BOUNDS, spans, + "where clauses are not enforced in type aliases"); } // The parameters must not have bounds for param in type_alias_generics.params.iter() { @@ -1354,9 +1355,9 @@ impl EarlyLintPass for IgnoredGenericBounds { }; if !spans.is_empty() { cx.span_lint( - IGNORED_GENERIC_BOUNDS, + TYPE_ALIAS_BOUNDS, spans, - "bounds on generic parameters are ignored in type aliases", + "bounds on generic parameters are not enforced in type aliases", ); } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 779aa3a9037ca..38f7a0b6faa0f 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -109,7 +109,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UnusedImportBraces, AnonymousParameters, UnusedDocComment, - IgnoredGenericBounds, + TypeAliasBounds, ); add_early_builtin_with_new!(sess, diff --git a/src/test/compile-fail/issue-17994.rs b/src/test/compile-fail/issue-17994.rs index 0f30e2461cf3b..7c3811e2ef28b 100644 --- a/src/test/compile-fail/issue-17994.rs +++ b/src/test/compile-fail/issue-17994.rs @@ -10,5 +10,5 @@ trait Tr {} type Huh where T: Tr = isize; //~ ERROR type parameter `T` is unused - //~| WARNING where clauses are ignored in type aliases + //~| WARNING where clauses are not enforced in type aliases fn main() {} diff --git a/src/test/compile-fail/private-in-public-warn.rs b/src/test/compile-fail/private-in-public-warn.rs index cc9eed7e65426..8bd9b0a901d65 100644 --- a/src/test/compile-fail/private-in-public-warn.rs +++ b/src/test/compile-fail/private-in-public-warn.rs @@ -58,7 +58,7 @@ mod traits { pub trait PubTr {} pub type Alias = T; //~ ERROR private trait `traits::PrivTr` in public interface - //~^ WARNING bounds on generic parameters are ignored + //~^ WARNING bounds on generic parameters are not enforced in type aliases //~| WARNING hard error pub trait Tr1: PrivTr {} //~ ERROR private trait `traits::PrivTr` in public interface //~^ WARNING hard error @@ -85,7 +85,7 @@ mod traits_where { pub type Alias where T: PrivTr = T; //~^ ERROR private trait `traits_where::PrivTr` in public interface //~| WARNING hard error - //~| WARNING where clauses are ignored in type aliases + //~| WARNING where clauses are not enforced in type aliases pub trait Tr2 where T: PrivTr {} //~^ ERROR private trait `traits_where::PrivTr` in public interface //~| WARNING hard error diff --git a/src/test/ui/type-alias-bounds.rs b/src/test/ui/type-alias-bounds.rs index ed368e8f1500e..7ec3afd9c870a 100644 --- a/src/test/ui/type-alias-bounds.rs +++ b/src/test/ui/type-alias-bounds.rs @@ -10,21 +10,20 @@ // Test ignored_generic_bounds lint warning about bounds in type aliases -// must-compile-successfully #![allow(dead_code)] use std::rc::Rc; type SVec = Vec; -//~^ WARN bounds on generic parameters are ignored in type aliases +//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] type S2Vec where T: Send = Vec; -//~^ WARN where clauses are ignored in type aliases +//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] type VVec<'b, 'a: 'b+'b> = (&'b u32, Vec<&'a i32>); -//~^ WARN bounds on generic parameters are ignored in type aliases +//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] type WVec<'b, T: 'b+'b> = (&'b u32, Vec); -//~^ WARN bounds on generic parameters are ignored in type aliases +//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec); -//~^ WARN where clauses are ignored in type aliases +//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] static STATIC : u32 = 0; @@ -48,4 +47,18 @@ fn foo<'a>(y: &'a i32) { x.1.push(y); // &'a i32: 'static does not hold } +// Bounds are not checked either, i.e. the definition is not necessarily well-formed +struct Sendable(T); +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 T4 = ::Assoc; + fn main() {} diff --git a/src/test/ui/type-alias-bounds.stderr b/src/test/ui/type-alias-bounds.stderr index 5237f3c19efcd..46aacd7321ccb 100644 --- a/src/test/ui/type-alias-bounds.stderr +++ b/src/test/ui/type-alias-bounds.stderr @@ -1,32 +1,53 @@ -warning: bounds on generic parameters are ignored in type aliases - --> $DIR/type-alias-bounds.rs:18:14 +warning: bounds on generic parameters are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:17:14 | LL | type SVec = Vec; | ^^^^ ^^^^ | - = note: #[warn(ignored_generic_bounds)] on by default + = note: #[warn(type_alias_bounds)] on by default -warning: where clauses are ignored in type aliases - --> $DIR/type-alias-bounds.rs:20:21 +warning: where clauses are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:19:21 | LL | type S2Vec where T: Send = Vec; | ^^^^^^^ -warning: bounds on generic parameters are ignored in type aliases - --> $DIR/type-alias-bounds.rs:22:19 +warning: bounds on generic parameters are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:21:19 | LL | type VVec<'b, 'a: 'b+'b> = (&'b u32, Vec<&'a i32>); | ^^ ^^ -warning: bounds on generic parameters are ignored in type aliases - --> $DIR/type-alias-bounds.rs:24:18 +warning: bounds on generic parameters are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:23:18 | LL | type WVec<'b, T: 'b+'b> = (&'b u32, Vec); | ^^ ^^ -warning: where clauses are ignored in type aliases - --> $DIR/type-alias-bounds.rs:26:25 +warning: where clauses are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:25:25 | LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec); | ^^^^^ ^^^^^ +warning: bounds on generic parameters are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:56:12 + | +LL | type T1 = U::Assoc; + | ^^^^^ + +warning: where clauses are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:58:18 + | +LL | type T2 where U: Bound = U::Assoc; + | ^^^^^^^^ + +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 + +error: aborting due to previous error + +If you want more information on this error, try using "rustc --explain E0220" From 0e6d40a3fb1000d1105646703c60c9201f3ed5cc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 10 Mar 2018 13:32:11 +0100 Subject: [PATCH 3/5] 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 | 58 ++++++++++++++----- 5 files changed, 150 insertions(+), 29 deletions(-) diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index f4638c23c5f4b..d4bfa7a1d308c 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 bdb36ab15b624..2ea13b2cb6d68 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 38f7a0b6faa0f..7237d2fd5d1c9 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 7ec3afd9c870a..c1cdeef3a4638 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 46aacd7321ccb..5288dca79be68 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 | ^^^^^^^^ + | + = 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[E0220]: associated type `Assoc` not found for `U` - --> $DIR/type-alias-bounds.rs:60:14 +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 + | ^^^^^ | -LL | type T3 = U::Assoc; - | ^^^^^^^^ associated type `Assoc` not found + = help: the bound will not be checked when the type alias is used, and should be removed -error: aborting due to previous error +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" From 37ff4736c790f87ab957235c65e93ff8351daa80 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 19 Mar 2018 18:01:14 +0100 Subject: [PATCH 4/5] wording nits --- src/librustc_lint/builtin.rs | 8 ++++---- src/test/ui/type-alias-bounds.stderr | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 2ea13b2cb6d68..f6acc085861e6 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1356,9 +1356,9 @@ impl TypeAliasBounds { 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. + // bound. Let's see if this type does that. - // We use an AST visitor to walk the type. + // We use a HIR visitor to walk the type. use rustc::hir::intravisit::{self, Visitor}; use syntax::ast::NodeId; struct WalkAssocTypes<'a, 'db> where 'db: 'a { @@ -1373,8 +1373,8 @@ impl TypeAliasBounds { 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"); + "use fully disambiguated paths (i.e., `::Assoc`) to refer to \ + associated types in type aliases"); } intravisit::walk_qpath(self, qpath, id, span) } diff --git a/src/test/ui/type-alias-bounds.stderr b/src/test/ui/type-alias-bounds.stderr index 5288dca79be68..2a2b0b0f26e34 100644 --- a/src/test/ui/type-alias-bounds.stderr +++ b/src/test/ui/type-alias-bounds.stderr @@ -46,7 +46,7 @@ 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 +help: use fully disambiguated 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 @@ -59,7 +59,7 @@ LL | type T2 where U: Bound = U::Assoc; //~ WARN not enforced in type aliase | ^^^^^^^^ | = 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 +help: use fully disambiguated 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 From c05d23406ead7b5747256c02127097807db45b83 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 19 Mar 2018 18:08:12 +0100 Subject: [PATCH 5/5] update compile-fail tests: fewer warnings because this is now a HIR lint --- src/test/compile-fail/issue-17994.rs | 1 - src/test/compile-fail/private-in-public-warn.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/test/compile-fail/issue-17994.rs b/src/test/compile-fail/issue-17994.rs index 7c3811e2ef28b..25141b9b82557 100644 --- a/src/test/compile-fail/issue-17994.rs +++ b/src/test/compile-fail/issue-17994.rs @@ -10,5 +10,4 @@ trait Tr {} type Huh where T: Tr = isize; //~ ERROR type parameter `T` is unused - //~| WARNING where clauses are not enforced in type aliases fn main() {} diff --git a/src/test/compile-fail/private-in-public-warn.rs b/src/test/compile-fail/private-in-public-warn.rs index 8bd9b0a901d65..6eeb14638e759 100644 --- a/src/test/compile-fail/private-in-public-warn.rs +++ b/src/test/compile-fail/private-in-public-warn.rs @@ -58,7 +58,6 @@ mod traits { pub trait PubTr {} pub type Alias = T; //~ ERROR private trait `traits::PrivTr` in public interface - //~^ WARNING bounds on generic parameters are not enforced in type aliases //~| WARNING hard error pub trait Tr1: PrivTr {} //~ ERROR private trait `traits::PrivTr` in public interface //~^ WARNING hard error @@ -85,7 +84,6 @@ mod traits_where { pub type Alias where T: PrivTr = T; //~^ ERROR private trait `traits_where::PrivTr` in public interface //~| WARNING hard error - //~| WARNING where clauses are not enforced in type aliases pub trait Tr2 where T: PrivTr {} //~^ ERROR private trait `traits_where::PrivTr` in public interface //~| WARNING hard error