Skip to content

Commit 0e6d40a

Browse files
committed
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.
1 parent 562b44d commit 0e6d40a

File tree

5 files changed

+150
-29
lines changed

5 files changed

+150
-29
lines changed

src/librustc/hir/mod.rs

+19
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,15 @@ pub enum TyParamBound {
395395
RegionTyParamBound(Lifetime),
396396
}
397397

398+
impl TyParamBound {
399+
pub fn span(&self) -> Span {
400+
match self {
401+
&TraitTyParamBound(ref t, ..) => t.span,
402+
&RegionTyParamBound(ref l) => l.span,
403+
}
404+
}
405+
}
406+
398407
/// A modifier on a bound, currently this is only used for `?Sized`, where the
399408
/// modifier is `Maybe`. Negative bounds should also be handled here.
400409
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -570,6 +579,16 @@ pub enum WherePredicate {
570579
EqPredicate(WhereEqPredicate),
571580
}
572581

582+
impl WherePredicate {
583+
pub fn span(&self) -> Span {
584+
match self {
585+
&WherePredicate::BoundPredicate(ref p) => p.span,
586+
&WherePredicate::RegionPredicate(ref p) => p.span,
587+
&WherePredicate::EqPredicate(ref p) => p.span,
588+
}
589+
}
590+
}
591+
573592
/// A type bound, eg `for<'c> Foo: Send+Clone+'c`
574593
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
575594
pub struct WhereBoundPredicate {

src/librustc_lint/builtin.rs

+75-8
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use syntax::attr;
4646
use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes};
4747
use syntax_pos::{BytePos, Span, SyntaxContext};
4848
use syntax::symbol::keywords;
49+
use syntax::errors::DiagnosticBuilder;
4950

5051
use rustc::hir::{self, PatKind};
5152
use rustc::hir::intravisit::FnKind;
@@ -1334,31 +1335,97 @@ impl LintPass for TypeAliasBounds {
13341335
}
13351336
}
13361337

1337-
impl EarlyLintPass for TypeAliasBounds {
1338-
fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
1339-
let type_alias_generics = match item.node {
1340-
ast::ItemKind::Ty(_, ref generics) => generics,
1338+
impl TypeAliasBounds {
1339+
fn is_type_variable_assoc(qpath: &hir::QPath) -> bool {
1340+
match *qpath {
1341+
hir::QPath::TypeRelative(ref ty, _) => {
1342+
// If this is a type variable, we found a `T::Assoc`.
1343+
match ty.node {
1344+
hir::TyPath(hir::QPath::Resolved(None, ref path)) => {
1345+
match path.def {
1346+
Def::TyParam(_) => true,
1347+
_ => false
1348+
}
1349+
}
1350+
_ => false
1351+
}
1352+
}
1353+
hir::QPath::Resolved(..) => false,
1354+
}
1355+
}
1356+
1357+
fn suggest_changing_assoc_types(ty: &hir::Ty, err: &mut DiagnosticBuilder) {
1358+
// Access to associates types should use `<T as Bound>::Assoc`, which does not need a
1359+
// bound. Let's see of this type does that.
1360+
1361+
// We use an AST visitor to walk the type.
1362+
use rustc::hir::intravisit::{self, Visitor};
1363+
use syntax::ast::NodeId;
1364+
struct WalkAssocTypes<'a, 'db> where 'db: 'a {
1365+
err: &'a mut DiagnosticBuilder<'db>
1366+
}
1367+
impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> {
1368+
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'v>
1369+
{
1370+
intravisit::NestedVisitorMap::None
1371+
}
1372+
1373+
fn visit_qpath(&mut self, qpath: &'v hir::QPath, id: NodeId, span: Span) {
1374+
if TypeAliasBounds::is_type_variable_assoc(qpath) {
1375+
self.err.span_help(span,
1376+
"use absolute paths (i.e., <T as Trait>::Assoc) to refer to associated \
1377+
types in type aliases");
1378+
}
1379+
intravisit::walk_qpath(self, qpath, id, span)
1380+
}
1381+
}
1382+
1383+
// Let's go for a walk!
1384+
let mut visitor = WalkAssocTypes { err };
1385+
visitor.visit_ty(ty);
1386+
}
1387+
}
1388+
1389+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
1390+
fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
1391+
let (ty, type_alias_generics) = match item.node {
1392+
hir::ItemTy(ref ty, ref generics) => (&*ty, generics),
13411393
_ => return,
13421394
};
1395+
let mut suggested_changing_assoc_types = false;
13431396
// There must not be a where clause
13441397
if !type_alias_generics.where_clause.predicates.is_empty() {
13451398
let spans : Vec<_> = type_alias_generics.where_clause.predicates.iter()
13461399
.map(|pred| pred.span()).collect();
1347-
cx.span_lint(TYPE_ALIAS_BOUNDS, spans,
1400+
let mut err = cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans,
13481401
"where clauses are not enforced in type aliases");
1402+
err.help("the clause will not be checked when the type alias is used, \
1403+
and should be removed");
1404+
if !suggested_changing_assoc_types {
1405+
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
1406+
suggested_changing_assoc_types = true;
1407+
}
1408+
err.emit();
13491409
}
13501410
// The parameters must not have bounds
13511411
for param in type_alias_generics.params.iter() {
13521412
let spans : Vec<_> = match param {
1353-
&ast::GenericParam::Lifetime(ref l) => l.bounds.iter().map(|b| b.span).collect(),
1354-
&ast::GenericParam::Type(ref ty) => ty.bounds.iter().map(|b| b.span()).collect(),
1413+
&hir::GenericParam::Lifetime(ref l) => l.bounds.iter().map(|b| b.span).collect(),
1414+
&hir::GenericParam::Type(ref ty) => ty.bounds.iter().map(|b| b.span()).collect(),
13551415
};
13561416
if !spans.is_empty() {
1357-
cx.span_lint(
1417+
let mut err = cx.struct_span_lint(
13581418
TYPE_ALIAS_BOUNDS,
13591419
spans,
13601420
"bounds on generic parameters are not enforced in type aliases",
13611421
);
1422+
err.help("the bound will not be checked when the type alias is used, \
1423+
and should be removed");
1424+
if !suggested_changing_assoc_types {
1425+
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
1426+
suggested_changing_assoc_types = true;
1427+
}
1428+
err.emit();
13621429
}
13631430
}
13641431
}

src/librustc_lint/lib.rs

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

115114
add_early_builtin_with_new!(sess,
@@ -139,6 +138,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
139138
MutableTransmutes,
140139
UnionsWithDropFields,
141140
UnreachablePub,
141+
TypeAliasBounds,
142142
);
143143

144144
add_builtin_with_new!(sess,

src/test/ui/type-alias-bounds.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
// Test ignored_generic_bounds lint warning about bounds in type aliases
1212

13+
// must-compile-successfully
1314
#![allow(dead_code)]
1415

1516
use std::rc::Rc;
@@ -53,12 +54,16 @@ type MySendable<T> = Sendable<T>; // no error here!
5354

5455
// However, bounds *are* taken into account when accessing associated types
5556
trait Bound { type Assoc; }
56-
type T1<U: Bound> = U::Assoc;
57-
//~^ WARN bounds on generic parameters are not enforced in type aliases
58-
type T2<U> where U: Bound = U::Assoc;
59-
//~^ WARN where clauses are not enforced in type aliases
60-
type T3<U> = U::Assoc;
61-
//~^ ERROR associated type `Assoc` not found for `U`
57+
type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases
58+
type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases
59+
60+
// This errors
61+
// type T3<U> = U::Assoc;
62+
// Do this instead
6263
type T4<U> = <U as Bound>::Assoc;
6364

65+
// Make sure the help about associatd types is not shown incorrectly
66+
type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases
67+
type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases
68+
6469
fn main() {}

src/test/ui/type-alias-bounds.stderr

+44-14
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,83 @@
11
warning: bounds on generic parameters are not enforced in type aliases
2-
--> $DIR/type-alias-bounds.rs:17:14
2+
--> $DIR/type-alias-bounds.rs:18:14
33
|
44
LL | type SVec<T: Send+Send> = Vec<T>;
55
| ^^^^ ^^^^
66
|
77
= note: #[warn(type_alias_bounds)] on by default
8+
= help: the bound will not be checked when the type alias is used, and should be removed
89

910
warning: where clauses are not enforced in type aliases
10-
--> $DIR/type-alias-bounds.rs:19:21
11+
--> $DIR/type-alias-bounds.rs:20:21
1112
|
1213
LL | type S2Vec<T> where T: Send = Vec<T>;
1314
| ^^^^^^^
15+
|
16+
= help: the clause will not be checked when the type alias is used, and should be removed
1417

1518
warning: bounds on generic parameters are not enforced in type aliases
16-
--> $DIR/type-alias-bounds.rs:21:19
19+
--> $DIR/type-alias-bounds.rs:22:19
1720
|
1821
LL | type VVec<'b, 'a: 'b+'b> = (&'b u32, Vec<&'a i32>);
1922
| ^^ ^^
23+
|
24+
= help: the bound will not be checked when the type alias is used, and should be removed
2025

2126
warning: bounds on generic parameters are not enforced in type aliases
22-
--> $DIR/type-alias-bounds.rs:23:18
27+
--> $DIR/type-alias-bounds.rs:24:18
2328
|
2429
LL | type WVec<'b, T: 'b+'b> = (&'b u32, Vec<T>);
2530
| ^^ ^^
31+
|
32+
= help: the bound will not be checked when the type alias is used, and should be removed
2633

2734
warning: where clauses are not enforced in type aliases
28-
--> $DIR/type-alias-bounds.rs:25:25
35+
--> $DIR/type-alias-bounds.rs:26:25
2936
|
3037
LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
3138
| ^^^^^ ^^^^^
39+
|
40+
= help: the clause will not be checked when the type alias is used, and should be removed
3241

3342
warning: bounds on generic parameters are not enforced in type aliases
34-
--> $DIR/type-alias-bounds.rs:56:12
43+
--> $DIR/type-alias-bounds.rs:57:12
3544
|
36-
LL | type T1<U: Bound> = U::Assoc;
45+
LL | type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases
3746
| ^^^^^
47+
|
48+
= help: the bound will not be checked when the type alias is used, and should be removed
49+
help: use absolute paths (i.e., <T as Trait>::Assoc) to refer to associated types in type aliases
50+
--> $DIR/type-alias-bounds.rs:57:21
51+
|
52+
LL | type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases
53+
| ^^^^^^^^
3854

3955
warning: where clauses are not enforced in type aliases
4056
--> $DIR/type-alias-bounds.rs:58:18
4157
|
42-
LL | type T2<U> where U: Bound = U::Assoc;
58+
LL | type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases
4359
| ^^^^^^^^
60+
|
61+
= help: the clause will not be checked when the type alias is used, and should be removed
62+
help: use absolute paths (i.e., <T as Trait>::Assoc) to refer to associated types in type aliases
63+
--> $DIR/type-alias-bounds.rs:58:29
64+
|
65+
LL | type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases
66+
| ^^^^^^^^
4467

45-
error[E0220]: associated type `Assoc` not found for `U`
46-
--> $DIR/type-alias-bounds.rs:60:14
68+
warning: bounds on generic parameters are not enforced in type aliases
69+
--> $DIR/type-alias-bounds.rs:66:12
70+
|
71+
LL | type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases
72+
| ^^^^^
4773
|
48-
LL | type T3<U> = U::Assoc;
49-
| ^^^^^^^^ associated type `Assoc` not found
74+
= help: the bound will not be checked when the type alias is used, and should be removed
5075

51-
error: aborting due to previous error
76+
warning: bounds on generic parameters are not enforced in type aliases
77+
--> $DIR/type-alias-bounds.rs:67:12
78+
|
79+
LL | type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases
80+
| ^^^^^
81+
|
82+
= help: the bound will not be checked when the type alias is used, and should be removed
5283

53-
If you want more information on this error, try using "rustc --explain E0220"

0 commit comments

Comments
 (0)