add lint to detect ignored generic bounds; this subsumes the previous 'generic bounds in type aliases are ignored' warning
This commit is contained in:
parent
d1ed6cce6c
commit
86821f7fb6
7 changed files with 279 additions and 78 deletions
|
|
@ -1386,3 +1386,97 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub {
|
|||
self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Lint for trait and lifetime bounds that are (accidentally) accepted by the parser, but
|
||||
/// ignored later.
|
||||
|
||||
pub struct IgnoredGenericBounds;
|
||||
|
||||
declare_lint! {
|
||||
IGNORED_GENERIC_BOUNDS,
|
||||
Warn,
|
||||
"these generic bounds are ignored"
|
||||
}
|
||||
|
||||
impl LintPass for IgnoredGenericBounds {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(IGNORED_GENERIC_BOUNDS)
|
||||
}
|
||||
}
|
||||
|
||||
impl IgnoredGenericBounds {
|
||||
fn ensure_no_param_bounds(
|
||||
cx: &EarlyContext,
|
||||
generics: &Vec<ast::GenericParam>,
|
||||
thing: &'static str,
|
||||
) {
|
||||
for param in generics.iter() {
|
||||
match param {
|
||||
&ast::GenericParam::Lifetime(ref lifetime) => {
|
||||
if !lifetime.bounds.is_empty() {
|
||||
let spans : Vec<_> = lifetime.bounds.iter().map(|b| b.span).collect();
|
||||
cx.span_lint(
|
||||
IGNORED_GENERIC_BOUNDS,
|
||||
spans,
|
||||
format!("bounds on generic lifetime parameters are ignored in {}",
|
||||
thing).as_ref()
|
||||
);
|
||||
}
|
||||
}
|
||||
&ast::GenericParam::Type(ref ty) => {
|
||||
if !ty.bounds.is_empty() {
|
||||
let spans : Vec<_> = ty.bounds.iter().map(|b| b.span()).collect();
|
||||
cx.span_lint(
|
||||
IGNORED_GENERIC_BOUNDS,
|
||||
spans,
|
||||
format!("bounds on generic type parameters are ignored in {}", thing)
|
||||
.as_ref()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for IgnoredGenericBounds {
|
||||
fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
|
||||
match item.node {
|
||||
ast::ItemKind::Ty(_, ref generics) => {
|
||||
if !generics.where_clause.predicates.is_empty() {
|
||||
let spans : Vec<_> = generics.where_clause.predicates.iter()
|
||||
.map(|pred| pred.span()).collect();
|
||||
cx.span_lint(IGNORED_GENERIC_BOUNDS, spans,
|
||||
"where clauses are ignored in type aliases");
|
||||
}
|
||||
IgnoredGenericBounds::ensure_no_param_bounds(cx, &generics.params,
|
||||
"type aliases");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_where_predicate(&mut self, cx: &EarlyContext, p: &ast::WherePredicate) {
|
||||
if let &ast::WherePredicate::BoundPredicate(ref bound_predicate) = p {
|
||||
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
|
||||
IgnoredGenericBounds::ensure_no_param_bounds(cx,
|
||||
&bound_predicate.bound_generic_params, "higher-ranked trait bounds (i.e., `for`)");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_poly_trait_ref(&mut self, cx: &EarlyContext, t: &ast::PolyTraitRef,
|
||||
_: &ast::TraitBoundModifier) {
|
||||
IgnoredGenericBounds::ensure_no_param_bounds(cx, &t.bound_generic_params,
|
||||
"higher-ranked trait bounds (i.e., `for`)");
|
||||
}
|
||||
|
||||
fn check_ty(&mut self, cx: &EarlyContext, ty: &ast::Ty) {
|
||||
match ty.node {
|
||||
ast::TyKind::BareFn(ref fn_ty) => {
|
||||
IgnoredGenericBounds::ensure_no_param_bounds(cx, &fn_ty.generic_params,
|
||||
"higher-ranked function types (i.e., `for`)");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
|
|||
AnonymousParameters,
|
||||
IllegalFloatLiteralPattern,
|
||||
UnusedDocComment,
|
||||
IgnoredGenericBounds,
|
||||
);
|
||||
|
||||
add_early_builtin_with_new!(sess,
|
||||
|
|
|
|||
|
|
@ -355,39 +355,6 @@ fn is_param<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
}
|
||||
}
|
||||
|
||||
fn ensure_no_param_bounds(tcx: TyCtxt,
|
||||
span: Span,
|
||||
generics: &hir::Generics,
|
||||
thing: &'static str) {
|
||||
let mut warn = false;
|
||||
|
||||
for ty_param in generics.ty_params() {
|
||||
if !ty_param.bounds.is_empty() {
|
||||
warn = true;
|
||||
}
|
||||
}
|
||||
|
||||
for lft_param in generics.lifetimes() {
|
||||
if !lft_param.bounds.is_empty() {
|
||||
warn = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !generics.where_clause.predicates.is_empty() {
|
||||
warn = true;
|
||||
}
|
||||
|
||||
if warn {
|
||||
// According to accepted RFC #XXX, we should
|
||||
// eventually accept these, but it will not be
|
||||
// part of this PR. Still, convert to warning to
|
||||
// make bootstrapping easier.
|
||||
span_warn!(tcx.sess, span, E0122,
|
||||
"generic bounds are ignored in {}",
|
||||
thing);
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) {
|
||||
let it = tcx.hir.expect_item(item_id);
|
||||
debug!("convert: item {} with id {}", it.name, it.id);
|
||||
|
|
@ -448,13 +415,7 @@ fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) {
|
|||
convert_variant_ctor(tcx, struct_def.id());
|
||||
}
|
||||
},
|
||||
hir::ItemTy(_, ref generics) => {
|
||||
ensure_no_param_bounds(tcx, it.span, generics, "type aliases");
|
||||
tcx.generics_of(def_id);
|
||||
tcx.type_of(def_id);
|
||||
tcx.predicates_of(def_id);
|
||||
}
|
||||
hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => {
|
||||
hir::ItemTy(..) | hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => {
|
||||
tcx.generics_of(def_id);
|
||||
tcx.type_of(def_id);
|
||||
tcx.predicates_of(def_id);
|
||||
|
|
|
|||
|
|
@ -1524,26 +1524,6 @@ static BAR: _ = "test"; // error, explicitly write out the type instead
|
|||
```
|
||||
"##,
|
||||
|
||||
E0122: r##"
|
||||
An attempt was made to add a generic constraint to a type alias. This constraint
|
||||
is entirely ignored. For backwards compatibility, Rust still allows this with a
|
||||
warning. Consider the example below:
|
||||
|
||||
```
|
||||
trait Foo{}
|
||||
|
||||
type MyType<R: Foo> = (R, ());
|
||||
|
||||
fn main() {
|
||||
let t: MyType<u32>;
|
||||
}
|
||||
```
|
||||
|
||||
We're able to declare a variable of type `MyType<u32>`, despite the fact that
|
||||
`u32` does not implement `Foo`. As a result, one should avoid using generic
|
||||
constraints in concert with type aliases.
|
||||
"##,
|
||||
|
||||
E0124: r##"
|
||||
You declared two fields of a struct with the same name. Erroneous code
|
||||
example:
|
||||
|
|
@ -4758,6 +4738,7 @@ register_diagnostics! {
|
|||
// E0086,
|
||||
// E0103,
|
||||
// E0104,
|
||||
// E0122, // bounds in type aliases are ignored, turned into proper lint
|
||||
// E0123,
|
||||
// E0127,
|
||||
// E0129,
|
||||
|
|
|
|||
|
|
@ -294,6 +294,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)]
|
||||
|
|
@ -404,6 +413,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.
|
||||
///
|
||||
/// E.g. `for<'c> Foo: Send+Clone+'c`
|
||||
|
|
|
|||
|
|
@ -9,12 +9,18 @@
|
|||
// except according to those terms.
|
||||
|
||||
// must-compile-successfully
|
||||
#![allow(dead_code, non_camel_case_types)]
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
type SVec<T: Send> = Vec<T>;
|
||||
type VVec<'b, 'a: 'b> = Vec<&'a i32>;
|
||||
type WVec<'b, T: 'b> = Vec<T>;
|
||||
type SVec<T: Send+Send> = Vec<T>;
|
||||
//~^ WARN bounds on generic type parameters are ignored in type aliases
|
||||
type VVec<'b, 'a: 'b+'b> = Vec<&'a i32>;
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in type aliases
|
||||
type WVec<'b, T: 'b+'b> = Vec<T>;
|
||||
//~^ WARN bounds on generic type parameters are ignored in type aliases
|
||||
type W2Vec<'b, T> where T: 'b, T: 'b = Vec<T>;
|
||||
//~^ WARN where clauses are ignored in type aliases
|
||||
|
||||
fn foo<'a>(y: &'a i32) {
|
||||
// If the bounds above would matter, the code below would be rejected.
|
||||
|
|
@ -26,8 +32,73 @@ fn foo<'a>(y: &'a i32) {
|
|||
|
||||
let mut x : WVec<'static, & 'a i32> = Vec::new();
|
||||
x.push(y);
|
||||
|
||||
let mut x : W2Vec<'static, & 'a i32> = Vec::new();
|
||||
x.push(y);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo(&42);
|
||||
fn bar1<'a, 'b>(
|
||||
x: &'a i32,
|
||||
y: &'b i32,
|
||||
f: for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32)
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types
|
||||
{
|
||||
// If the bound in f's type would matter, the call below would (have to)
|
||||
// be rejected.
|
||||
f(x, y);
|
||||
}
|
||||
|
||||
fn bar2<'a, 'b, F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
|
||||
x: &'a i32,
|
||||
y: &'b i32,
|
||||
f: F)
|
||||
{
|
||||
// If the bound in f's type would matter, the call below would (have to)
|
||||
// be rejected.
|
||||
f(x, y);
|
||||
}
|
||||
|
||||
fn bar3<'a, 'b, F>(
|
||||
x: &'a i32,
|
||||
y: &'b i32,
|
||||
f: F)
|
||||
where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
|
||||
{
|
||||
// If the bound in f's type would matter, the call below would (have to)
|
||||
// be rejected.
|
||||
f(x, y);
|
||||
}
|
||||
|
||||
fn bar4<'a, 'b, F>(
|
||||
x: &'a i32,
|
||||
y: &'b i32,
|
||||
f: F)
|
||||
where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
|
||||
{
|
||||
// If the bound in f's type would matter, the call below would (have to)
|
||||
// be rejected.
|
||||
f(x, y);
|
||||
}
|
||||
|
||||
struct S1<F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(F);
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
|
||||
struct S2<F>(F) where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32;
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
|
||||
struct S3<F>(F) where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32;
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
|
||||
|
||||
struct S_fnty(for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32);
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types
|
||||
|
||||
type T1 = Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>;
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
|
||||
|
||||
fn main() {
|
||||
let _ : Option<for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32> = None;
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked function types
|
||||
let _ : Option<Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>> = None;
|
||||
//~^ WARN bounds on generic lifetime parameters are ignored in higher-ranked trait bounds
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,92 @@
|
|||
warning[E0122]: generic bounds are ignored in type aliases
|
||||
--> $DIR/param-bounds-ignored.rs:15:1
|
||||
warning: bounds on generic type parameters are ignored in type aliases
|
||||
--> $DIR/param-bounds-ignored.rs:16:14
|
||||
|
|
||||
LL | type SVec<T: Send> = Vec<T>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning[E0122]: generic bounds are ignored in type aliases
|
||||
--> $DIR/param-bounds-ignored.rs:16:1
|
||||
LL | type SVec<T: Send+Send> = Vec<T>;
|
||||
| ^^^^ ^^^^
|
||||
|
|
||||
LL | type VVec<'b, 'a: 'b> = Vec<&'a i32>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: #[warn(ignored_generic_bounds)] on by default
|
||||
|
||||
warning[E0122]: generic bounds are ignored in type aliases
|
||||
--> $DIR/param-bounds-ignored.rs:17:1
|
||||
warning: bounds on generic lifetime parameters are ignored in type aliases
|
||||
--> $DIR/param-bounds-ignored.rs:18:19
|
||||
|
|
||||
LL | type WVec<'b, T: 'b> = Vec<T>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | type VVec<'b, 'a: 'b+'b> = Vec<&'a i32>;
|
||||
| ^^ ^^
|
||||
|
||||
warning: bounds on generic type parameters are ignored in type aliases
|
||||
--> $DIR/param-bounds-ignored.rs:20:18
|
||||
|
|
||||
LL | type WVec<'b, T: 'b+'b> = Vec<T>;
|
||||
| ^^ ^^
|
||||
|
||||
warning: where clauses are ignored in type aliases
|
||||
--> $DIR/param-bounds-ignored.rs:22:25
|
||||
|
|
||||
LL | type W2Vec<'b, T> where T: 'b, T: 'b = Vec<T>;
|
||||
| ^^^^^ ^^^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked function types (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:43:22
|
||||
|
|
||||
LL | f: for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32)
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:51:34
|
||||
|
|
||||
LL | fn bar2<'a, 'b, F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:66:28
|
||||
|
|
||||
LL | where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:78:25
|
||||
|
|
||||
LL | where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:86:28
|
||||
|
|
||||
LL | struct S1<F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>(F);
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:88:40
|
||||
|
|
||||
LL | struct S2<F>(F) where F: for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32;
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:90:37
|
||||
|
|
||||
LL | struct S3<F>(F) where for<'xa, 'xb: 'xa> F: Fn(&'xa i32, &'xb i32) -> &'xa i32;
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked function types (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:93:29
|
||||
|
|
||||
LL | struct S_fnty(for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32);
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:96:29
|
||||
|
|
||||
LL | type T1 = Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>;
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked function types (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:100:34
|
||||
|
|
||||
LL | let _ : Option<for<'xa, 'xb: 'xa> fn(&'xa i32, &'xb i32) -> &'xa i32> = None;
|
||||
| ^^^
|
||||
|
||||
warning: bounds on generic lifetime parameters are ignored in higher-ranked trait bounds (i.e., `for`)
|
||||
--> $DIR/param-bounds-ignored.rs:102:38
|
||||
|
|
||||
LL | let _ : Option<Box<for<'xa, 'xb: 'xa> Fn(&'xa i32, &'xb i32) -> &'xa i32>> = None;
|
||||
| ^^^
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue