Skip to content

Commit

Permalink
type_alias_bounds lint: If the type alias uses an associated type wit…
Browse files Browse the repository at this point in the history
…hout "as", suggest to use the "as" form instead.

This is necessary to get rid of the type bound, and hence silence the warning.
  • Loading branch information
RalfJung committed Mar 10, 2018
1 parent 562b44d commit 0e6d40a
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 29 deletions.
19 changes: 19 additions & 0 deletions src/librustc/hir/mod.rs
Expand Up @@ -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)]
Expand Down Expand Up @@ -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 {
Expand Down
83 changes: 75 additions & 8 deletions src/librustc_lint/builtin.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_lint/lib.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -139,6 +138,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
MutableTransmutes,
UnionsWithDropFields,
UnreachablePub,
TypeAliasBounds,
);

add_builtin_with_new!(sess,
Expand Down
17 changes: 11 additions & 6 deletions src/test/ui/type-alias-bounds.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {}
58 changes: 44 additions & 14 deletions src/test/ui/type-alias-bounds.stderr
@@ -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
| ^^^^^^^^
|
= 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[E0220]: associated type `Assoc` not found for `U`
--> $DIR/type-alias-bounds.rs:60:14
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
| ^^^^^
|
LL | type T3<U> = U::Assoc;
| ^^^^^^^^ associated type `Assoc` not found
= help: the bound will not be checked when the type alias is used, and should be removed

error: aborting due to previous error
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"

0 comments on commit 0e6d40a

Please sign in to comment.