diff options
| author | Ralf Jung <post@ralfj.de> | 2018-02-18 19:40:35 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2018-02-27 13:16:30 +0100 |
| commit | 86821f7fb6afbbfebd2d7b0a681d14c4cf6a578e (patch) | |
| tree | 3e83776e9eb72d7f23e33d54da7d0db91a89235f /src | |
| parent | d1ed6cce6ca875b3902f34c9979cf75afa403fed (diff) | |
| download | rust-86821f7fb6afbbfebd2d7b0a681d14c4cf6a578e.tar.gz rust-86821f7fb6afbbfebd2d7b0a681d14c4cf6a578e.zip | |
add lint to detect ignored generic bounds; this subsumes the previous 'generic bounds in type aliases are ignored' warning
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc_lint/builtin.rs | 94 | ||||
| -rw-r--r-- | src/librustc_lint/lib.rs | 1 | ||||
| -rw-r--r-- | src/librustc_typeck/collect.rs | 41 | ||||
| -rw-r--r-- | src/librustc_typeck/diagnostics.rs | 21 | ||||
| -rw-r--r-- | src/libsyntax/ast.rs | 19 | ||||
| -rw-r--r-- | src/test/ui/param-bounds-ignored.rs | 79 | ||||
| -rw-r--r-- | src/test/ui/param-bounds-ignored.stderr | 98 |
7 files changed, 277 insertions, 76 deletions
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index de55710bdf3..2452bda8d43 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1386,3 +1386,97 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub { self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false); } } + +/// Lint for trait and lifetime bounds that are (accidentally) accepted by the parser, but +/// ignored later. + +pub struct IgnoredGenericBounds; + +declare_lint! { + IGNORED_GENERIC_BOUNDS, + Warn, + "these generic bounds are ignored" +} + +impl LintPass for IgnoredGenericBounds { + fn get_lints(&self) -> LintArray { + lint_array!(IGNORED_GENERIC_BOUNDS) + } +} + +impl IgnoredGenericBounds { + fn ensure_no_param_bounds( + cx: &EarlyContext, + generics: &Vec<ast::GenericParam>, + thing: &'static str, + ) { + for param in generics.iter() { + match param { + &ast::GenericParam::Lifetime(ref lifetime) => { + if !lifetime.bounds.is_empty() { + let spans : Vec<_> = lifetime.bounds.iter().map(|b| b.span).collect(); + cx.span_lint( + IGNORED_GENERIC_BOUNDS, + spans, + format!("bounds on generic lifetime parameters are ignored in {}", + thing).as_ref() + ); + } + } + &ast::GenericParam::Type(ref ty) => { + if !ty.bounds.is_empty() { + let spans : Vec<_> = ty.bounds.iter().map(|b| b.span()).collect(); + cx.span_lint( + IGNORED_GENERIC_BOUNDS, + spans, + format!("bounds on generic type parameters are ignored in {}", thing) + .as_ref() + ); + } + } + } + } + } +} + +impl EarlyLintPass for IgnoredGenericBounds { + fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) { + match item.node { + ast::ItemKind::Ty(_, ref generics) => { + if !generics.where_clause.predicates.is_empty() { + let spans : Vec<_> = generics.where_clause.predicates.iter() + .map(|pred| pred.span()).collect(); + cx.span_lint(IGNORED_GENERIC_BOUNDS, spans, + "where clauses are ignored in type aliases"); + } + IgnoredGenericBounds::ensure_no_param_bounds(cx, &generics.params, + "type aliases"); + } + _ => {} + } + } + + fn check_where_predicate(&mut self, cx: &EarlyContext, p: &ast::WherePredicate) { + if let &ast::WherePredicate::BoundPredicate(ref bound_predicate) = p { + // A type binding, eg `for<'c> Foo: Send+Clone+'c` + IgnoredGenericBounds::ensure_no_param_bounds(cx, + &bound_predicate.bound_generic_params, "higher-ranked trait bounds (i.e., `for`)"); + } + } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext, t: &ast::PolyTraitRef, + _: &ast::TraitBoundModifier) { + IgnoredGenericBounds::ensure_no_param_bounds(cx, &t.bound_generic_params, + "higher-ranked trait bounds (i.e., `for`)"); + } + + fn check_ty(&mut self, cx: &EarlyContext, ty: &ast::Ty) { + match ty.node { + ast::TyKind::BareFn(ref fn_ty) => { + IgnoredGenericBounds::ensure_no_param_bounds(cx, &fn_ty.generic_params, + "higher-ranked function types (i.e., `for`)"); + } + _ => {} + } + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index f3c6ff2f2b3..de1b79259dd 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -109,6 +109,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { AnonymousParameters, IllegalFloatLiteralPattern, UnusedDocComment, + IgnoredGenericBounds, ); add_early_builtin_with_new!(sess, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1c8d22e4666..d0424c52088 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -355,39 +355,6 @@ fn is_param<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn ensure_no_param_bounds(tcx: TyCtxt, - span: Span, - generics: &hir::Generics, - thing: &'static str) { - let mut warn = false; - - for ty_param in generics.ty_params() { - if !ty_param.bounds.is_empty() { - warn = true; - } - } - - for lft_param in generics.lifetimes() { - if !lft_param.bounds.is_empty() { - warn = true; - } - } - - if !generics.where_clause.predicates.is_empty() { - warn = true; - } - - if warn { - // According to accepted RFC #XXX, we should - // eventually accept these, but it will not be - // part of this PR. Still, convert to warning to - // make bootstrapping easier. - span_warn!(tcx.sess, span, E0122, - "generic bounds are ignored in {}", - thing); - } -} - fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) { let it = tcx.hir.expect_item(item_id); debug!("convert: item {} with id {}", it.name, it.id); @@ -448,13 +415,7 @@ fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) { convert_variant_ctor(tcx, struct_def.id()); } }, - hir::ItemTy(_, ref generics) => { - ensure_no_param_bounds(tcx, it.span, generics, "type aliases"); - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); - } - hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => { + hir::ItemTy(..) | hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => { tcx.generics_of(def_id); tcx.type_of(def_id); tcx.predicates_of(def_id); diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 1c0e084832e..617f7615fb9 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1524,26 +1524,6 @@ static BAR: _ = "test"; // error, explicitly write out the type instead ``` "##, -E0122: r##" -An attempt was made to add a generic constraint to a type alias. This constraint -is entirely ignored. For backwards compatibility, Rust still allows this with a -warning. Consider the example below: - -``` -trait Foo{} - -type MyType<R: Foo> = (R, ()); - -fn main() { - let t: MyType<u32>; -} -``` - -We're able to declare a variable of type `MyType<u32>`, despite the fact that -`u32` does not implement `Foo`. As a result, one should avoid using generic -constraints in concert with type aliases. -"##, - E0124: r##" You declared two fields of a struct with the same name. Erroneous code example: @@ -4758,6 +4738,7 @@ register_diagnostics! { // E0086, // E0103, // E0104, +// E0122, // bounds in type aliases are ignored, turned into proper lint // E0123, // E0127, // E0129, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 6609b77b132..245025d3e4f 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -294,6 +294,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)] @@ -404,6 +413,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. /// /// E.g. `for<'c> Foo: Send+Clone+'c` diff --git a/src/test/ui/param-bounds-ignored.rs b/src/test/ui/param-bounds-ignored.rs index 9e09102f2d4..a136ec60252 100644 --- a/src/test/ui/param-bounds-ignored.rs +++ b/src/test/ui/param-bounds-ignored.rs @@ -9,12 +9,18 @@ // except according to those terms. // must-compile-successfully +#![allow(dead_code, non_camel_case_types)] use std::rc::Rc; -type SVec<T: Send> = Vec<T>; -type VVec<'b, 'a: 'b> = Vec<&'a i32>; -type WVec<'b, T: 'b> = Vec<T>; +type SVec<T: Send+Send> = Vec<T>; +//~^ WARN bounds on generic type parameters are ignored in type aliases +type VVec<'b, 'a: 'b+'b> = Vec<&'a i32>; +//~^ WARN bounds on generic lifetime parameters are ignored in type aliases +type WVec<'b, T: 'b+'b> = Vec<T>; +//~^ WARN bounds on generic type parameters are ignored in type aliases +type W2Vec<'b, T> where T: 'b, T: 'b = Vec<T>; +//~^ 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. @@ -26,8 +32,73 @@ fn foo<'a>(y: &'a i32) { let mut x : WVec<'static, & 'a i32> = Vec::new(); x.push(y); + + let mut x : W2Vec<'static, & 'a i32> = Vec::new(); + x.push(y); +} + +fn bar1<'a, 'b>( + x: &'a i32, + y: &'b i32, + f: for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32) + //~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types +{ + // If the bound in f's type would matter, the call below would (have to) + // be rejected. + f(x, y); } +fn bar2<'a, 'b, F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>( + //~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds + x: &'a i32, + y: &'b i32, + f: F) +{ + // If the bound in f's type would matter, the call below would (have to) + // be rejected. + f(x, y); +} + +fn bar3<'a, 'b, F>( + x: &'a i32, + y: &'b i32, + f: F) + where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32 + //~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds +{ + // If the bound in f's type would matter, the call below would (have to) + // be rejected. + f(x, y); +} + +fn bar4<'a, 'b, F>( + x: &'a i32, + y: &'b i32, + f: F) + where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32 + //~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds +{ + // If the bound in f's type would matter, the call below would (have to) + // be rejected. + f(x, y); +} + +struct S1<F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(F); +//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds +struct S2<F>(F) where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32; +//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds +struct S3<F>(F) where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32; +//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds + +struct S_fnty(for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32); +//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types + +type T1 = Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>; +//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds + fn main() { - foo(&42); + let _ : Option<for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32> = None; + //~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types + let _ : Option<Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>> = None; + //~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds } diff --git a/src/test/ui/param-bounds-ignored.stderr b/src/test/ui/param-bounds-ignored.stderr index fe5986448fa..55df5d1c939 100644 --- a/src/test/ui/param-bounds-ignored.stderr +++ b/src/test/ui/param-bounds-ignored.stderr @@ -1,18 +1,92 @@ -warning[E0122]: generic bounds are ignored in type aliases - --> $DIR/param-bounds-ignored.rs:15:1 +warning: bounds on generic type parameters are ignored in type aliases + --> $DIR/param-bounds-ignored.rs:16:14 | -LL | type SVec<T: Send> = Vec<T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type SVec<T: Send+Send> = Vec<T>; + | ^^^^ ^^^^ + | + = note: #[warn(ignored_generic_bounds)] on by default + +warning: bounds on generic lifetime parameters are ignored in type aliases + --> $DIR/param-bounds-ignored.rs:18:19 + | +LL | type VVec<'b, 'a: 'b+'b> = Vec<&'a i32>; + | ^^ ^^ + +warning: bounds on generic type parameters are ignored in type aliases + --> $DIR/param-bounds-ignored.rs:20:18 + | +LL | type WVec<'b, T: 'b+'b> = Vec<T>; + | ^^ ^^ + +warning: where clauses are ignored in type aliases + --> $DIR/param-bounds-ignored.rs:22:25 + | +LL | type W2Vec<'b, T> where T: 'b, T: 'b = Vec<T>; + | ^^^^^ ^^^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked function types (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:43:22 + | +LL | f: for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32) + | ^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:51:34 + | +LL | fn bar2<'a, 'b, F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>( + | ^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:66:28 + | +LL | where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32 + | ^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:78:25 + | +LL | where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32 + | ^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:86:28 + | +LL | struct S1<F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(F); + | ^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:88:40 + | +LL | struct S2<F>(F) where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32; + | ^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:90:37 + | +LL | struct S3<F>(F) where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32; + | ^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked function types (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:93:29 + | +LL | struct S_fnty(for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32); + | ^^^ + +warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:96:29 + | +LL | type T1 = Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>; + | ^^^ -warning[E0122]: generic bounds are ignored in type aliases - --> $DIR/param-bounds-ignored.rs:16:1 +warning: bounds on generic lifetime parameters are ignored in higher-ranked function types (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:100:34 | -LL | type VVec<'b, 'a: 'b> = Vec<&'a i32>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ : Option<for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32> = None; + | ^^^ -warning[E0122]: generic bounds are ignored in type aliases - --> $DIR/param-bounds-ignored.rs:17:1 +warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`) + --> $DIR/param-bounds-ignored.rs:102:38 | -LL | type WVec<'b, T: 'b> = Vec<T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ : Option<Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>> = None; + | ^^^ |
