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:
parent
562b44d8c3
commit
0e6d40a3fb
5 changed files with 151 additions and 30 deletions
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue