Skip to content

Commit 0c5bc12

Browse files
committed
add lint to detect ignored generic bounds; this subsumes the previous 'generic bounds in type aliases are ignored' warning
1 parent 501fc2d commit 0c5bc12

File tree

6 files changed

+224
-70
lines changed

6 files changed

+224
-70
lines changed

src/librustc_lint/builtin.rs

+82
Original file line numberDiff line numberDiff line change
@@ -1386,3 +1386,85 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub {
13861386
self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false);
13871387
}
13881388
}
1389+
1390+
/// Lint for trait and lifetime bounds that are (accidentally) accepted by the parser, but
1391+
/// ignored later.
1392+
1393+
pub struct IgnoredGenericBounds;
1394+
1395+
declare_lint! {
1396+
IGNORED_GENERIC_BOUNDS,
1397+
Warn,
1398+
"these generic bounds are ignored"
1399+
}
1400+
1401+
impl LintPass for IgnoredGenericBounds {
1402+
fn get_lints(&self) -> LintArray {
1403+
lint_array!(IGNORED_GENERIC_BOUNDS)
1404+
}
1405+
}
1406+
1407+
impl IgnoredGenericBounds {
1408+
fn ensure_no_param_bounds(
1409+
cx: &EarlyContext,
1410+
span: Span,
1411+
generics: &Vec<ast::GenericParam>,
1412+
thing: &'static str,
1413+
) {
1414+
let has_bound = generics.iter().any(|param|
1415+
match param {
1416+
&ast::GenericParam::Lifetime(ref lifetime) => {
1417+
!lifetime.bounds.is_empty()
1418+
}
1419+
&ast::GenericParam::Type(ref ty) => {
1420+
!ty.bounds.is_empty()
1421+
}
1422+
}
1423+
);
1424+
1425+
if has_bound {
1426+
cx.span_lint(IGNORED_GENERIC_BOUNDS, span,
1427+
format!("bounds on generic parameters are ignored in {}", thing).as_ref());
1428+
}
1429+
}
1430+
}
1431+
1432+
impl EarlyLintPass for IgnoredGenericBounds {
1433+
fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
1434+
match item.node {
1435+
ast::ItemKind::Ty(_, ref generics) => {
1436+
if !generics.where_clause.predicates.is_empty() {
1437+
cx.span_lint(IGNORED_GENERIC_BOUNDS, item.span,
1438+
"where clauses are ignored in trait bounds");
1439+
}
1440+
IgnoredGenericBounds::ensure_no_param_bounds(cx, item.span, &generics.params,
1441+
"type aliases");
1442+
}
1443+
_ => {}
1444+
}
1445+
}
1446+
1447+
fn check_where_predicate(&mut self, cx: &EarlyContext, p: &ast::WherePredicate) {
1448+
if let &ast::WherePredicate::BoundPredicate(ref bound_predicate) = p {
1449+
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
1450+
IgnoredGenericBounds::ensure_no_param_bounds(cx, bound_predicate.span,
1451+
&bound_predicate.bound_generic_params, "higher-ranked trait bounds (i.e., `for`)");
1452+
}
1453+
}
1454+
1455+
fn check_poly_trait_ref(&mut self, cx: &EarlyContext, t: &ast::PolyTraitRef,
1456+
_: &ast::TraitBoundModifier) {
1457+
IgnoredGenericBounds::ensure_no_param_bounds(cx, t.span, &t.bound_generic_params,
1458+
"higher-ranked trait bounds (i.e., `for`)");
1459+
}
1460+
1461+
fn check_ty(&mut self, cx: &EarlyContext, ty: &ast::Ty) {
1462+
match ty.node {
1463+
ast::TyKind::BareFn(ref fn_ty) => {
1464+
IgnoredGenericBounds::ensure_no_param_bounds(cx, ty.span, &fn_ty.generic_params,
1465+
"higher-ranked function types (i.e., `for`)");
1466+
}
1467+
_ => {}
1468+
}
1469+
}
1470+
}

src/librustc_lint/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
109109
AnonymousParameters,
110110
IllegalFloatLiteralPattern,
111111
UnusedDocComment,
112+
IgnoredGenericBounds,
112113
);
113114

114115
add_early_builtin_with_new!(sess,

src/librustc_typeck/collect.rs

+1-40
Original file line numberDiff line numberDiff line change
@@ -355,39 +355,6 @@ fn is_param<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
355355
}
356356
}
357357

358-
fn ensure_no_param_bounds(tcx: TyCtxt,
359-
span: Span,
360-
generics: &hir::Generics,
361-
thing: &'static str) {
362-
let mut warn = false;
363-
364-
for ty_param in generics.ty_params() {
365-
if !ty_param.bounds.is_empty() {
366-
warn = true;
367-
}
368-
}
369-
370-
for lft_param in generics.lifetimes() {
371-
if !lft_param.bounds.is_empty() {
372-
warn = true;
373-
}
374-
}
375-
376-
if !generics.where_clause.predicates.is_empty() {
377-
warn = true;
378-
}
379-
380-
if warn {
381-
// According to accepted RFC #XXX, we should
382-
// eventually accept these, but it will not be
383-
// part of this PR. Still, convert to warning to
384-
// make bootstrapping easier.
385-
span_warn!(tcx.sess, span, E0122,
386-
"generic bounds are ignored in {}",
387-
thing);
388-
}
389-
}
390-
391358
fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) {
392359
let it = tcx.hir.expect_item(item_id);
393360
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) {
448415
convert_variant_ctor(tcx, struct_def.id());
449416
}
450417
},
451-
hir::ItemTy(_, ref generics) => {
452-
ensure_no_param_bounds(tcx, it.span, generics, "type aliases");
453-
tcx.generics_of(def_id);
454-
tcx.type_of(def_id);
455-
tcx.predicates_of(def_id);
456-
}
457-
hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => {
418+
hir::ItemTy(..) | hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => {
458419
tcx.generics_of(def_id);
459420
tcx.type_of(def_id);
460421
tcx.predicates_of(def_id);

src/librustc_typeck/diagnostics.rs

-20
Original file line numberDiff line numberDiff line change
@@ -1524,26 +1524,6 @@ static BAR: _ = "test"; // error, explicitly write out the type instead
15241524
```
15251525
"##,
15261526

1527-
E0122: r##"
1528-
An attempt was made to add a generic constraint to a type alias. This constraint
1529-
is entirely ignored. For backwards compatibility, Rust still allows this with a
1530-
warning. Consider the example below:
1531-
1532-
```
1533-
trait Foo{}
1534-
1535-
type MyType<R: Foo> = (R, ());
1536-
1537-
fn main() {
1538-
let t: MyType<u32>;
1539-
}
1540-
```
1541-
1542-
We're able to declare a variable of type `MyType<u32>`, despite the fact that
1543-
`u32` does not implement `Foo`. As a result, one should avoid using generic
1544-
constraints in concert with type aliases.
1545-
"##,
1546-
15471527
E0124: r##"
15481528
You declared two fields of a struct with the same name. Erroneous code
15491529
example:

src/test/ui/param-bounds-ignored.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
// except according to those terms.
1010

1111
// must-compile-successfully
12+
#![allow(dead_code, non_camel_case_types)]
1213

1314
use std::rc::Rc;
1415

1516
type SVec<T: Send> = Vec<T>;
1617
type VVec<'b, 'a: 'b> = Vec<&'a i32>;
1718
type WVec<'b, T: 'b> = Vec<T>;
19+
type W2Vec<'b, T> where T: 'b = Vec<T>;
1820

1921
fn foo<'a>(y: &'a i32) {
2022
// If the bounds above would matter, the code below would be rejected.
@@ -26,8 +28,62 @@ fn foo<'a>(y: &'a i32) {
2628

2729
let mut x : WVec<'static, & 'a i32> = Vec::new();
2830
x.push(y);
31+
32+
let mut x : W2Vec<'static, & 'a i32> = Vec::new();
33+
x.push(y);
34+
}
35+
36+
fn bar1<'a, 'b>(
37+
x: &'a i32,
38+
y: &'b i32,
39+
f: for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32)
40+
{
41+
// If the bound in f's type would matter, the call below would (have to)
42+
// be rejected.
43+
f(x, y);
2944
}
3045

46+
fn bar2<'a, 'b, F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(
47+
x: &'a i32,
48+
y: &'b i32,
49+
f: F)
50+
{
51+
// If the bound in f's type would matter, the call below would (have to)
52+
// be rejected.
53+
f(x, y);
54+
}
55+
56+
fn bar3<'a, 'b, F>(
57+
x: &'a i32,
58+
y: &'b i32,
59+
f: F)
60+
where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32
61+
{
62+
// If the bound in f's type would matter, the call below would (have to)
63+
// be rejected.
64+
f(x, y);
65+
}
66+
67+
fn bar4<'a, 'b, F>(
68+
x: &'a i32,
69+
y: &'b i32,
70+
f: F)
71+
where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32
72+
{
73+
// If the bound in f's type would matter, the call below would (have to)
74+
// be rejected.
75+
f(x, y);
76+
}
77+
78+
struct S1<F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(F);
79+
struct S2<F>(F) where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32;
80+
struct S3<F>(F) where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32;
81+
82+
struct S_fnty(for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32);
83+
84+
type T1 = Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>;
85+
3186
fn main() {
32-
foo(&42);
87+
let _ : Option<for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32> = None;
88+
let _ : Option<Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>> = None;
3389
}
+83-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,92 @@
1-
warning[E0122]: generic bounds are ignored in type aliases
2-
--> $DIR/param-bounds-ignored.rs:15:1
1+
warning: bounds on generic parameters are ignored in type aliases
2+
--> $DIR/param-bounds-ignored.rs:16:1
33
|
4-
15 | type SVec<T: Send> = Vec<T>;
4+
16 | type SVec<T: Send> = Vec<T>;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: #[warn(ignored_generic_bounds)] on by default
68

7-
warning[E0122]: generic bounds are ignored in type aliases
8-
--> $DIR/param-bounds-ignored.rs:16:1
9+
warning: bounds on generic parameters are ignored in type aliases
10+
--> $DIR/param-bounds-ignored.rs:17:1
911
|
10-
16 | type VVec<'b, 'a: 'b> = Vec<&'a i32>;
12+
17 | type VVec<'b, 'a: 'b> = Vec<&'a i32>;
1113
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1214

13-
warning[E0122]: generic bounds are ignored in type aliases
14-
--> $DIR/param-bounds-ignored.rs:17:1
15+
warning: bounds on generic parameters are ignored in type aliases
16+
--> $DIR/param-bounds-ignored.rs:18:1
1517
|
16-
17 | type WVec<'b, T: 'b> = Vec<T>;
18+
18 | type WVec<'b, T: 'b> = Vec<T>;
1719
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1820

21+
warning: where clauses are ignored in trait bounds
22+
--> $DIR/param-bounds-ignored.rs:19:1
23+
|
24+
19 | type W2Vec<'b, T> where T: 'b = Vec<T>;
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
27+
warning: bounds on generic parameters are ignored in higher-ranked function types (i.e., `for`)
28+
--> $DIR/param-bounds-ignored.rs:39:8
29+
|
30+
39 | f: for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32)
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
33+
warning: bounds on generic parameters are ignored in higher-ranked trait bounds (i.e., `for`)
34+
--> $DIR/param-bounds-ignored.rs:46:20
35+
|
36+
46 | fn bar2<'a, 'b, F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+
39+
warning: bounds on generic parameters are ignored in higher-ranked trait bounds (i.e., `for`)
40+
--> $DIR/param-bounds-ignored.rs:60:14
41+
|
42+
60 | where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
45+
warning: bounds on generic parameters are ignored in higher-ranked trait bounds (i.e., `for`)
46+
--> $DIR/param-bounds-ignored.rs:71:11
47+
|
48+
71 | where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50+
51+
warning: bounds on generic parameters are ignored in higher-ranked trait bounds (i.e., `for`)
52+
--> $DIR/param-bounds-ignored.rs:78:14
53+
|
54+
78 | struct S1<F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(F);
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
57+
warning: bounds on generic parameters are ignored in higher-ranked trait bounds (i.e., `for`)
58+
--> $DIR/param-bounds-ignored.rs:79:26
59+
|
60+
79 | struct S2<F>(F) where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32;
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62+
63+
warning: bounds on generic parameters are ignored in higher-ranked trait bounds (i.e., `for`)
64+
--> $DIR/param-bounds-ignored.rs:80:23
65+
|
66+
80 | struct S3<F>(F) where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32;
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
68+
69+
warning: bounds on generic parameters are ignored in higher-ranked function types (i.e., `for`)
70+
--> $DIR/param-bounds-ignored.rs:82:15
71+
|
72+
82 | struct S_fnty(for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32);
73+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
74+
75+
warning: bounds on generic parameters are ignored in higher-ranked trait bounds (i.e., `for`)
76+
--> $DIR/param-bounds-ignored.rs:84:15
77+
|
78+
84 | type T1 = Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>;
79+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
80+
81+
warning: bounds on generic parameters are ignored in higher-ranked function types (i.e., `for`)
82+
--> $DIR/param-bounds-ignored.rs:87:20
83+
|
84+
87 | let _ : Option<for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32> = None;
85+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
86+
87+
warning: bounds on generic parameters are ignored in higher-ranked trait bounds (i.e., `for`)
88+
--> $DIR/param-bounds-ignored.rs:88:24
89+
|
90+
88 | let _ : Option<Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>> = None;
91+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
92+

0 commit comments

Comments
 (0)