Skip to content

Commit 86821f7

Browse files
committed
add lint to detect ignored generic bounds; this subsumes the previous 'generic bounds in type aliases are ignored' warning
1 parent d1ed6cc commit 86821f7

File tree

7 files changed

+277
-76
lines changed

7 files changed

+277
-76
lines changed

src/librustc_lint/builtin.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,3 +1386,97 @@ 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+
generics: &Vec<ast::GenericParam>,
1411+
thing: &'static str,
1412+
) {
1413+
for param in generics.iter() {
1414+
match param {
1415+
&ast::GenericParam::Lifetime(ref lifetime) => {
1416+
if !lifetime.bounds.is_empty() {
1417+
let spans : Vec<_> = lifetime.bounds.iter().map(|b| b.span).collect();
1418+
cx.span_lint(
1419+
IGNORED_GENERIC_BOUNDS,
1420+
spans,
1421+
format!("bounds on generic lifetime parameters are ignored in {}",
1422+
thing).as_ref()
1423+
);
1424+
}
1425+
}
1426+
&ast::GenericParam::Type(ref ty) => {
1427+
if !ty.bounds.is_empty() {
1428+
let spans : Vec<_> = ty.bounds.iter().map(|b| b.span()).collect();
1429+
cx.span_lint(
1430+
IGNORED_GENERIC_BOUNDS,
1431+
spans,
1432+
format!("bounds on generic type parameters are ignored in {}", thing)
1433+
.as_ref()
1434+
);
1435+
}
1436+
}
1437+
}
1438+
}
1439+
}
1440+
}
1441+
1442+
impl EarlyLintPass for IgnoredGenericBounds {
1443+
fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
1444+
match item.node {
1445+
ast::ItemKind::Ty(_, ref generics) => {
1446+
if !generics.where_clause.predicates.is_empty() {
1447+
let spans : Vec<_> = generics.where_clause.predicates.iter()
1448+
.map(|pred| pred.span()).collect();
1449+
cx.span_lint(IGNORED_GENERIC_BOUNDS, spans,
1450+
"where clauses are ignored in type aliases");
1451+
}
1452+
IgnoredGenericBounds::ensure_no_param_bounds(cx, &generics.params,
1453+
"type aliases");
1454+
}
1455+
_ => {}
1456+
}
1457+
}
1458+
1459+
fn check_where_predicate(&mut self, cx: &EarlyContext, p: &ast::WherePredicate) {
1460+
if let &ast::WherePredicate::BoundPredicate(ref bound_predicate) = p {
1461+
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
1462+
IgnoredGenericBounds::ensure_no_param_bounds(cx,
1463+
&bound_predicate.bound_generic_params, "higher-ranked trait bounds (i.e., `for`)");
1464+
}
1465+
}
1466+
1467+
fn check_poly_trait_ref(&mut self, cx: &EarlyContext, t: &ast::PolyTraitRef,
1468+
_: &ast::TraitBoundModifier) {
1469+
IgnoredGenericBounds::ensure_no_param_bounds(cx, &t.bound_generic_params,
1470+
"higher-ranked trait bounds (i.e., `for`)");
1471+
}
1472+
1473+
fn check_ty(&mut self, cx: &EarlyContext, ty: &ast::Ty) {
1474+
match ty.node {
1475+
ast::TyKind::BareFn(ref fn_ty) => {
1476+
IgnoredGenericBounds::ensure_no_param_bounds(cx, &fn_ty.generic_params,
1477+
"higher-ranked function types (i.e., `for`)");
1478+
}
1479+
_ => {}
1480+
}
1481+
}
1482+
}

src/librustc_lint/lib.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 40 deletions
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

Lines changed: 1 addition & 20 deletions
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:
@@ -4758,6 +4738,7 @@ register_diagnostics! {
47584738
// E0086,
47594739
// E0103,
47604740
// E0104,
4741+
// E0122, // bounds in type aliases are ignored, turned into proper lint
47614742
// E0123,
47624743
// E0127,
47634744
// E0129,

src/libsyntax/ast.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,15 @@ pub enum TyParamBound {
294294
RegionTyParamBound(Lifetime)
295295
}
296296

297+
impl TyParamBound {
298+
pub fn span(&self) -> Span {
299+
match self {
300+
&TraitTyParamBound(ref t, ..) => t.span,
301+
&RegionTyParamBound(ref l) => l.span,
302+
}
303+
}
304+
}
305+
297306
/// A modifier on a bound, currently this is only used for `?Sized`, where the
298307
/// modifier is `Maybe`. Negative bounds should also be handled here.
299308
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -404,6 +413,16 @@ pub enum WherePredicate {
404413
EqPredicate(WhereEqPredicate),
405414
}
406415

416+
impl WherePredicate {
417+
pub fn span(&self) -> Span {
418+
match self {
419+
&WherePredicate::BoundPredicate(ref p) => p.span,
420+
&WherePredicate::RegionPredicate(ref p) => p.span,
421+
&WherePredicate::EqPredicate(ref p) => p.span,
422+
}
423+
}
424+
}
425+
407426
/// A type bound.
408427
///
409428
/// E.g. `for<'c> Foo: Send+Clone+'c`

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

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@
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

15-
type SVec<T: Send> = Vec<T>;
16-
type VVec<'b, 'a: 'b> = Vec<&'a i32>;
17-
type WVec<'b, T: 'b> = Vec<T>;
16+
type SVec<T: Send+Send> = Vec<T>;
17+
//~^ WARN bounds on generic type parameters are ignored in type aliases
18+
type VVec<'b, 'a: 'b+'b> = Vec<&'a i32>;
19+
//~^ WARN bounds on generic lifetime parameters are ignored in type aliases
20+
type WVec<'b, T: 'b+'b> = Vec<T>;
21+
//~^ WARN bounds on generic type parameters are ignored in type aliases
22+
type W2Vec<'b, T> where T: 'b, T: 'b = Vec<T>;
23+
//~^ WARN where clauses are ignored in type aliases
1824

1925
fn foo<'a>(y: &'a i32) {
2026
// If the bounds above would matter, the code below would be rejected.
@@ -26,8 +32,73 @@ fn foo<'a>(y: &'a i32) {
2632

2733
let mut x : WVec<'static, & 'a i32> = Vec::new();
2834
x.push(y);
35+
36+
let mut x : W2Vec<'static, & 'a i32> = Vec::new();
37+
x.push(y);
38+
}
39+
40+
fn bar1<'a, 'b>(
41+
x: &'a i32,
42+
y: &'b i32,
43+
f: for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32)
44+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types
45+
{
46+
// If the bound in f's type would matter, the call below would (have to)
47+
// be rejected.
48+
f(x, y);
2949
}
3050

51+
fn bar2<'a, 'b, F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(
52+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
53+
x: &'a i32,
54+
y: &'b i32,
55+
f: F)
56+
{
57+
// If the bound in f's type would matter, the call below would (have to)
58+
// be rejected.
59+
f(x, y);
60+
}
61+
62+
fn bar3<'a, 'b, F>(
63+
x: &'a i32,
64+
y: &'b i32,
65+
f: F)
66+
where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32
67+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
68+
{
69+
// If the bound in f's type would matter, the call below would (have to)
70+
// be rejected.
71+
f(x, y);
72+
}
73+
74+
fn bar4<'a, 'b, F>(
75+
x: &'a i32,
76+
y: &'b i32,
77+
f: F)
78+
where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32
79+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
80+
{
81+
// If the bound in f's type would matter, the call below would (have to)
82+
// be rejected.
83+
f(x, y);
84+
}
85+
86+
struct S1<F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(F);
87+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
88+
struct S2<F>(F) where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32;
89+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
90+
struct S3<F>(F) where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32;
91+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
92+
93+
struct S_fnty(for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32);
94+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types
95+
96+
type T1 = Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>;
97+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
98+
3199
fn main() {
32-
foo(&42);
100+
let _ : Option<for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32> = None;
101+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types
102+
let _ : Option<Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>> = None;
103+
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
33104
}

0 commit comments

Comments
 (0)