1
Fork 0

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.
This commit is contained in:
Ralf Jung 2018-03-10 13:32:11 +01:00
parent 562b44d8c3
commit 0e6d40a3fb
5 changed files with 151 additions and 30 deletions

View file

@ -395,6 +395,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)]
@ -570,6 +579,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, eg `for<'c> Foo: Send+Clone+'c`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct WhereBoundPredicate {

View file

@ -46,6 +46,7 @@ use syntax::attr;
use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes};
use syntax_pos::{BytePos, Span, SyntaxContext};
use syntax::symbol::keywords;
use syntax::errors::DiagnosticBuilder;
use rustc::hir::{self, PatKind};
use rustc::hir::intravisit::FnKind;
@ -1334,31 +1335,97 @@ impl LintPass for TypeAliasBounds {
}
}
impl EarlyLintPass for TypeAliasBounds {
fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
let type_alias_generics = match item.node {
ast::ItemKind::Ty(_, ref generics) => generics,
impl TypeAliasBounds {
fn is_type_variable_assoc(qpath: &hir::QPath) -> bool {
match *qpath {
hir::QPath::TypeRelative(ref ty, _) => {
// If this is a type variable, we found a `T::Assoc`.
match ty.node {
hir::TyPath(hir::QPath::Resolved(None, ref path)) => {
match path.def {
Def::TyParam(_) => true,
_ => false
}
}
_ => false
}
}
hir::QPath::Resolved(..) => false,
}
}
fn suggest_changing_assoc_types(ty: &hir::Ty, err: &mut DiagnosticBuilder) {
// Access to associates types should use `<T as Bound>::Assoc`, which does not need a
// bound. Let's see of this type does that.
// We use an AST visitor to walk the type.
use rustc::hir::intravisit::{self, Visitor};
use syntax::ast::NodeId;
struct WalkAssocTypes<'a, 'db> where 'db: 'a {
err: &'a mut DiagnosticBuilder<'db>
}
impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> {
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'v>
{
intravisit::NestedVisitorMap::None
}
fn visit_qpath(&mut self, qpath: &'v hir::QPath, id: NodeId, span: Span) {
if TypeAliasBounds::is_type_variable_assoc(qpath) {
self.err.span_help(span,
"use absolute paths (i.e., <T as Trait>::Assoc) to refer to associated \
types in type aliases");
}
intravisit::walk_qpath(self, qpath, id, span)
}
}
// Let's go for a walk!
let mut visitor = WalkAssocTypes { err };
visitor.visit_ty(ty);
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
let (ty, type_alias_generics) = match item.node {
hir::ItemTy(ref ty, ref generics) => (&*ty, generics),
_ => return,
};
let mut suggested_changing_assoc_types = false;
// There must not be a where clause
if !type_alias_generics.where_clause.predicates.is_empty() {
let spans : Vec<_> = type_alias_generics.where_clause.predicates.iter()
.map(|pred| pred.span()).collect();
cx.span_lint(TYPE_ALIAS_BOUNDS, spans,
let mut err = cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans,
"where clauses are not enforced in type aliases");
err.help("the clause will not be checked when the type alias is used, \
and should be removed");
if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true;
}
err.emit();
}
// The parameters must not have bounds
for param in type_alias_generics.params.iter() {
let spans : Vec<_> = match param {
&ast::GenericParam::Lifetime(ref l) => l.bounds.iter().map(|b| b.span).collect(),
&ast::GenericParam::Type(ref ty) => ty.bounds.iter().map(|b| b.span()).collect(),
&hir::GenericParam::Lifetime(ref l) => l.bounds.iter().map(|b| b.span).collect(),
&hir::GenericParam::Type(ref ty) => ty.bounds.iter().map(|b| b.span()).collect(),
};
if !spans.is_empty() {
cx.span_lint(
let mut err = cx.struct_span_lint(
TYPE_ALIAS_BOUNDS,
spans,
"bounds on generic parameters are not enforced in type aliases",
);
err.help("the bound will not be checked when the type alias is used, \
and should be removed");
if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true;
}
err.emit();
}
}
}

View file

@ -109,7 +109,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
UnusedImportBraces,
AnonymousParameters,
UnusedDocComment,
TypeAliasBounds,
);
add_early_builtin_with_new!(sess,
@ -139,6 +138,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
MutableTransmutes,
UnionsWithDropFields,
UnreachablePub,
TypeAliasBounds,
);
add_builtin_with_new!(sess,

View file

@ -10,6 +10,7 @@
// Test ignored_generic_bounds lint warning about bounds in type aliases
// must-compile-successfully
#![allow(dead_code)]
use std::rc::Rc;
@ -53,12 +54,16 @@ type MySendable<T> = Sendable<T>; // no error here!
// However, bounds *are* taken into account when accessing associated types
trait Bound { type Assoc; }
type T1<U: Bound> = U::Assoc;
//~^ WARN bounds on generic parameters are not enforced in type aliases
type T2<U> where U: Bound = U::Assoc;
//~^ WARN where clauses are not enforced in type aliases
type T3<U> = U::Assoc;
//~^ ERROR associated type `Assoc` not found for `U`
type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases
type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases
// This errors
// type T3<U> = U::Assoc;
// Do this instead
type T4<U> = <U as Bound>::Assoc;
// Make sure the help about associatd types is not shown incorrectly
type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases
type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases
fn main() {}

View file

@ -1,53 +1,83 @@
warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:17:14
--> $DIR/type-alias-bounds.rs:18:14
|
LL | type SVec<T: Send+Send> = Vec<T>;
| ^^^^ ^^^^
|
= note: #[warn(type_alias_bounds)] on by default
= help: the bound will not be checked when the type alias is used, and should be removed
warning: where clauses are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:19:21
--> $DIR/type-alias-bounds.rs:20:21
|
LL | type S2Vec<T> where T: Send = Vec<T>;
| ^^^^^^^
|
= help: the clause will not be checked when the type alias is used, and should be removed
warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:21:19
--> $DIR/type-alias-bounds.rs:22:19
|
LL | type VVec<'b, 'a: 'b+'b> = (&'b u32, Vec<&'a i32>);
| ^^ ^^
|
= help: the bound will not be checked when the type alias is used, and should be removed
warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:23:18
--> $DIR/type-alias-bounds.rs:24:18
|
LL | type WVec<'b, T: 'b+'b> = (&'b u32, Vec<T>);
| ^^ ^^
|
= help: the bound will not be checked when the type alias is used, and should be removed
warning: where clauses are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:25:25
--> $DIR/type-alias-bounds.rs:26:25
|
LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
| ^^^^^ ^^^^^
|
= help: the clause will not be checked when the type alias is used, and should be removed
warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:56:12
--> $DIR/type-alias-bounds.rs:57:12
|
LL | type T1<U: Bound> = U::Assoc;
LL | type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases
| ^^^^^
|
= help: the bound will not be checked when the type alias is used, and should be removed
help: use absolute paths (i.e., <T as Trait>::Assoc) to refer to associated types in type aliases
--> $DIR/type-alias-bounds.rs:57:21
|
LL | type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases
| ^^^^^^^^
warning: where clauses are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:58:18
|
LL | type T2<U> where U: Bound = U::Assoc;
LL | type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases
| ^^^^^^^^
error[E0220]: associated type `Assoc` not found for `U`
--> $DIR/type-alias-bounds.rs:60:14
|
LL | type T3<U> = U::Assoc;
| ^^^^^^^^ associated type `Assoc` not found
= help: the clause will not be checked when the type alias is used, and should be removed
help: use absolute paths (i.e., <T as Trait>::Assoc) to refer to associated types in type aliases
--> $DIR/type-alias-bounds.rs:58:29
|
LL | type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases
| ^^^^^^^^
error: aborting due to previous error
warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:66:12
|
LL | type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases
| ^^^^^
|
= help: the bound will not be checked when the type alias is used, and should be removed
warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:67:12
|
LL | type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases
| ^^^^^
|
= help: the bound will not be checked when the type alias is used, and should be removed
If you want more information on this error, try using "rustc --explain E0220"