about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2018-02-18 19:40:35 +0100
committerRalf Jung <post@ralfj.de>2018-02-27 13:16:30 +0100
commit86821f7fb6afbbfebd2d7b0a681d14c4cf6a578e (patch)
tree3e83776e9eb72d7f23e33d54da7d0db91a89235f /src
parentd1ed6cce6ca875b3902f34c9979cf75afa403fed (diff)
downloadrust-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.rs94
-rw-r--r--src/librustc_lint/lib.rs1
-rw-r--r--src/librustc_typeck/collect.rs41
-rw-r--r--src/librustc_typeck/diagnostics.rs21
-rw-r--r--src/libsyntax/ast.rs19
-rw-r--r--src/test/ui/param-bounds-ignored.rs79
-rw-r--r--src/test/ui/param-bounds-ignored.stderr98
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;
+   |                                      ^^^