@@ -46,6 +46,7 @@ use syntax::attr;
46
46
use syntax:: feature_gate:: { AttributeGate , AttributeType , Stability , deprecated_attributes} ;
47
47
use syntax_pos:: { BytePos , Span , SyntaxContext } ;
48
48
use syntax:: symbol:: keywords;
49
+ use syntax:: errors:: DiagnosticBuilder ;
49
50
50
51
use rustc:: hir:: { self , PatKind } ;
51
52
use rustc:: hir:: intravisit:: FnKind ;
@@ -1334,31 +1335,97 @@ impl LintPass for TypeAliasBounds {
1334
1335
}
1335
1336
}
1336
1337
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) ,
1341
1393
_ => return ,
1342
1394
} ;
1395
+ let mut suggested_changing_assoc_types = false ;
1343
1396
// There must not be a where clause
1344
1397
if !type_alias_generics. where_clause . predicates . is_empty ( ) {
1345
1398
let spans : Vec < _ > = type_alias_generics. where_clause . predicates . iter ( )
1346
1399
. 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,
1348
1401
"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 ( ) ;
1349
1409
}
1350
1410
// The parameters must not have bounds
1351
1411
for param in type_alias_generics. params . iter ( ) {
1352
1412
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 ( ) ,
1355
1415
} ;
1356
1416
if !spans. is_empty ( ) {
1357
- cx. span_lint (
1417
+ let mut err = cx. struct_span_lint (
1358
1418
TYPE_ALIAS_BOUNDS ,
1359
1419
spans,
1360
1420
"bounds on generic parameters are not enforced in type aliases" ,
1361
1421
) ;
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 ( ) ;
1362
1429
}
1363
1430
}
1364
1431
}
0 commit comments