Deduplicate type param constraint suggestion code

This commit is contained in:
Esteban Küber 2019-11-19 17:11:55 -08:00
parent 02bc412d19
commit 9fb446d472
12 changed files with 128 additions and 154 deletions

View File

@ -16,9 +16,9 @@ use crate::ty::AdtKind;
use crate::ty::query::Providers;
use crate::util::nodemap::{NodeMap, FxHashSet};
use errors::FatalError;
use errors::{Applicability, DiagnosticBuilder, FatalError};
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
use syntax::source_map::Spanned;
use syntax::source_map::{Spanned, SourceMap};
use syntax::ast::{self, CrateSugar, Ident, Name, NodeId, AsmDialect};
use syntax::ast::{Attribute, Label, LitKind, StrStyle, FloatTy, IntTy, UintTy};
pub use syntax::ast::{Mutability, Constness, Unsafety, Movability, CaptureBy};
@ -644,6 +644,79 @@ impl Generics {
self.params.iter().map(|p| p.span).collect::<Vec<Span>>().into()
}
}
/// Suggest restricting a type param with a new bound.
pub fn suggest_constraining_type_param(
&self,
err: &mut DiagnosticBuilder<'_>,
param_name: &str,
constraint: &str,
source_map: &SourceMap,
span: Span,
) -> bool {
let restrict_msg = "consider further restricting this bound";
if let Some(param) = self.params.iter().filter(|p| {
p.name.ident().as_str() == param_name
}).next() {
if param_name.starts_with("impl ") {
// `impl Trait` in argument:
// `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
err.span_suggestion(
param.span,
restrict_msg,
// `impl CurrentTrait + MissingTrait`
format!("{} + {}", param_name, constraint),
Applicability::MachineApplicable,
);
} else if self.where_clause.predicates.is_empty() &&
param.bounds.is_empty()
{
// If there are no bounds whatsoever, suggest adding a constraint
// to the type parameter:
// `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
err.span_suggestion(
param.span,
"consider restricting this bound",
format!("{}: {}", param_name, constraint),
Applicability::MachineApplicable,
);
} else if !self.where_clause.predicates.is_empty() {
// There is a `where` clause, so suggest expanding it:
// `fn foo<T>(t: T) where T: Debug {}` →
// `fn foo<T>(t: T) where T: Debug, T: Trait {}`
err.span_suggestion(
self.where_clause.span().unwrap().shrink_to_hi(),
&format!("consider further restricting type parameter `{}`", param_name),
format!(", {}: {}", param_name, constraint),
Applicability::MachineApplicable,
);
} else {
// If there is no `where` clause lean towards constraining to the
// type parameter:
// `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
// `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
let sp = param.span.with_hi(span.hi());
let span = source_map.span_through_char(sp, ':');
if sp != param.span && sp != span {
// Only suggest if we have high certainty that the span
// covers the colon in `foo<T: Trait>`.
err.span_suggestion(
span,
restrict_msg,
format!("{}: {} + ", param_name, constraint),
Applicability::MachineApplicable,
);
} else {
err.span_label(
param.span,
&format!("consider adding a `where {}: {}` bound", param_name, constraint),
);
}
}
return true;
}
false
}
}
/// Synthetic type parameters are converted to another form during lowering; this allows

View File

@ -1091,7 +1091,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
fn suggest_restricting_param_bound(
&self,
err: &mut DiagnosticBuilder<'_>,
mut err: &mut DiagnosticBuilder<'_>,
trait_ref: &ty::PolyTraitRef<'_>,
body_id: hir::HirId,
) {
@ -1102,7 +1102,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
_ => return,
};
let mut suggest_restriction = |generics: &hir::Generics, msg| {
let suggest_restriction = |
generics: &hir::Generics,
msg,
err: &mut DiagnosticBuilder<'_>,
| {
let span = generics.where_clause.span_for_predicates_or_empty_place();
if !span.from_expansion() && span.desugaring_kind().is_none() {
err.span_suggestion(
@ -1132,7 +1136,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
kind: hir::TraitItemKind::Method(..), ..
}) if param_ty && self_ty == self.tcx.types.self_param => {
// Restricting `Self` for a single method.
suggest_restriction(&generics, "`Self`");
suggest_restriction(&generics, "`Self`", err);
return;
}
@ -1154,7 +1158,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
kind: hir::ItemKind::Impl(_, _, _, generics, ..), ..
}) if projection.is_some() => {
// Missing associated type bound.
suggest_restriction(&generics, "the associated type");
suggest_restriction(&generics, "the associated type", err);
return;
}
@ -1183,68 +1187,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
hir::Node::ImplItem(hir::ImplItem { generics, span, .. })
if param_ty => {
// Missing generic type parameter bound.
let restrict_msg = "consider further restricting this bound";
let param_name = self_ty.to_string();
for param in generics.params.iter().filter(|p| {
p.name.ident().as_str() == param_name
}) {
if param_name.starts_with("impl ") {
// `impl Trait` in argument:
// `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
err.span_suggestion(
param.span,
restrict_msg,
// `impl CurrentTrait + MissingTrait`
format!("{} + {}", param.name.ident(), trait_ref),
Applicability::MachineApplicable,
);
} else if generics.where_clause.predicates.is_empty() &&
param.bounds.is_empty()
{
// If there are no bounds whatsoever, suggest adding a constraint
// to the type parameter:
// `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
err.span_suggestion(
param.span,
"consider restricting this bound",
format!("{}", trait_ref.to_predicate()),
Applicability::MachineApplicable,
);
} else if !generics.where_clause.predicates.is_empty() {
// There is a `where` clause, so suggest expanding it:
// `fn foo<T>(t: T) where T: Debug {}` →
// `fn foo<T>(t: T) where T: Debug, T: Trait {}`
err.span_suggestion(
generics.where_clause.span().unwrap().shrink_to_hi(),
&format!(
"consider further restricting type parameter `{}`",
param_name,
),
format!(", {}", trait_ref.to_predicate()),
Applicability::MachineApplicable,
);
} else {
// If there is no `where` clause lean towards constraining to the
// type parameter:
// `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
// `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
let sp = param.span.with_hi(span.hi());
let span = self.tcx.sess.source_map()
.span_through_char(sp, ':');
if sp != param.span && sp != span {
// Only suggest if we have high certainty that the span
// covers the colon in `foo<T: Trait>`.
err.span_suggestion(span, restrict_msg, format!(
"{} + ",
trait_ref.to_predicate(),
), Applicability::MachineApplicable);
} else {
err.span_label(param.span, &format!(
"consider adding a `where {}` bound",
trait_ref.to_predicate(),
));
}
}
let constraint = trait_ref.to_string();
if generics.suggest_constraining_type_param(
&mut err,
&param_name,
&constraint,
self.tcx.sess.source_map(),
*span,
) {
return;
}
}

View File

@ -233,63 +233,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let generics = tcx.generics_of(self.mir_def_id);
let param = generics.type_param(&param_ty, tcx);
let generics = tcx.hir().get_generics(self.mir_def_id).unwrap();
let msg = "consider adding a `Copy` constraint to this type argument";
for param in generics.params.iter().filter(|p| {
p.name.ident().as_str() == param.name.as_str()
}) {
let param_name = param.name.ident().as_str();
if param_name.starts_with("impl ") {
// `impl Trait` in argument:
// `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
err.span_suggestion(
param.span,
msg,
// `impl CurrentTrait + MissingTrait`
format!("{} + Copy", param_name),
Applicability::MachineApplicable,
);
} else if generics.where_clause.predicates.is_empty() &&
param.bounds.is_empty()
{
// If there are no bounds whatsoever, suggest adding a constraint
// to the type parameter:
// `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
err.span_suggestion(
param.span,
msg,
format!("{}: Copy", param_name),
Applicability::MachineApplicable,
);
} else if !generics.where_clause.predicates.is_empty() {
// There is a `where` clause, so suggest expanding it:
// `fn foo<T>(t: T) where T: Debug {}` →
// `fn foo<T>(t: T) where T: Debug, T: Trait {}`
err.span_suggestion(
generics.where_clause.span().unwrap().shrink_to_hi(),
msg,
format!(", {}: Copy", param_name),
Applicability::MachineApplicable,
);
} else {
// If there is no `where` clause lean towards constraining to the
// type parameter:
// `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
// `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
let sp = param.span.with_hi(span.hi());
let span = tcx.sess.source_map()
.span_through_char(sp, ':');
if sp != param.span && sp != span {
// Only suggest if we have high certainty that the span
// covers the colon in `foo<T: Trait>`.
err.span_suggestion(span, msg, format!(
"{}: Copy +",
param_name,
), Applicability::MachineApplicable);
} else {
err.span_label(param.span, msg);
}
}
}
generics.suggest_constraining_type_param(
&mut err,
&param.name.as_str(),
"Copy",
tcx.sess.source_map(),
span,
);
}
let span = if let Some(local) = place.as_local() {
let decl = &self.body.local_decls[local];

View File

@ -4,7 +4,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn add<A: Add<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs + rhs;
| --- value moved here
LL | drop(lhs);
@ -16,7 +16,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn add<A: Add<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs + rhs;
| --- value moved here
LL | drop(lhs);
@ -29,7 +29,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn sub<A: Sub<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs - rhs;
| --- value moved here
LL | drop(lhs);
@ -41,7 +41,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn sub<A: Sub<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs - rhs;
| --- value moved here
LL | drop(lhs);
@ -54,7 +54,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn mul<A: Mul<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs * rhs;
| --- value moved here
LL | drop(lhs);
@ -66,7 +66,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn mul<A: Mul<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs * rhs;
| --- value moved here
LL | drop(lhs);
@ -79,7 +79,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn div<A: Div<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs / rhs;
| --- value moved here
LL | drop(lhs);
@ -91,7 +91,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn div<A: Div<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs / rhs;
| --- value moved here
LL | drop(lhs);
@ -104,7 +104,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn rem<A: Rem<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs % rhs;
| --- value moved here
LL | drop(lhs);
@ -116,7 +116,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn rem<A: Rem<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs % rhs;
| --- value moved here
LL | drop(lhs);
@ -129,7 +129,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn bitand<A: BitAnd<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs & rhs;
| --- value moved here
LL | drop(lhs);
@ -141,7 +141,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn bitand<A: BitAnd<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs & rhs;
| --- value moved here
LL | drop(lhs);
@ -154,7 +154,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn bitor<A: BitOr<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs | rhs;
| --- value moved here
LL | drop(lhs);
@ -166,7 +166,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn bitor<A: BitOr<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs | rhs;
| --- value moved here
LL | drop(lhs);
@ -179,7 +179,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn bitxor<A: BitXor<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs ^ rhs;
| --- value moved here
LL | drop(lhs);
@ -191,7 +191,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn bitxor<A: BitXor<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs ^ rhs;
| --- value moved here
LL | drop(lhs);
@ -204,7 +204,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn shl<A: Shl<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs << rhs;
| --- value moved here
LL | drop(lhs);
@ -216,7 +216,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn shl<A: Shl<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs << rhs;
| --- value moved here
LL | drop(lhs);
@ -229,7 +229,7 @@ error[E0382]: use of moved value: `lhs`
LL | fn shr<A: Shr<B, Output=()>, B>(lhs: A, rhs: B) {
| -- --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `A: Copy +`
| help: consider further restricting this bound: `A: Copy +`
LL | lhs >> rhs;
| --- value moved here
LL | drop(lhs);
@ -241,7 +241,7 @@ error[E0382]: use of moved value: `rhs`
LL | fn shr<A: Shr<B, Output=()>, B>(lhs: A, rhs: B) {
| - --- move occurs because `rhs` has type `B`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `B: Copy`
| help: consider restricting this bound: `B: Copy`
LL | lhs >> rhs;
| --- value moved here
LL | drop(lhs);

View File

@ -4,7 +4,7 @@ error[E0382]: use of moved value: `x`
LL | fn double_move<T: Add<Output=()>>(x: T) {
| -- - move occurs because `x` has type `T`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `T: Copy +`
| help: consider further restricting this bound: `T: Copy +`
LL | x
| - value moved here
LL | +
@ -17,7 +17,7 @@ error[E0382]: borrow of moved value: `x`
LL | fn move_then_borrow<T: Add<Output=()> + Clone>(x: T) {
| -- - move occurs because `x` has type `T`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `T: Copy +`
| help: consider further restricting this bound: `T: Copy +`
LL | x
| - value moved here
LL | +

View File

@ -22,7 +22,7 @@ error[E0382]: use of moved value: `f`
LL | fn c<F:FnOnce(isize, isize) -> isize>(f: F) {
| -- - move occurs because `f` has type `F`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `F: Copy +`
| help: consider further restricting this bound: `F: Copy +`
LL | f(1, 2);
| - value moved here
LL | f(1, 2);

View File

@ -7,7 +7,7 @@ LL | where B : for<'ccx> Bar<'ccx>
| ------------------- required by this bound in `want_bar_for_any_ccx`
...
LL | where B : Qux
| - help: consider further restricting type parameter `B`: `, for<'ccx> B: Bar<'ccx>`
| - help: consider further restricting type parameter `B`: `, B: for<'ccx> Bar<'ccx>`
...
LL | want_bar_for_any_ccx(b);
| ^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B`

View File

@ -2,7 +2,7 @@ error[E0277]: the trait bound `for<'tcx> F: Foo<'tcx>` is not satisfied
--> $DIR/hrtb-higher-ranker-supertraits.rs:18:26
|
LL | where F : Foo<'x>
| - help: consider further restricting type parameter `F`: `, for<'tcx> F: Foo<'tcx>`
| - help: consider further restricting type parameter `F`: `, F: for<'tcx> Foo<'tcx>`
...
LL | want_foo_for_any_tcx(f);
| ^ the trait `for<'tcx> Foo<'tcx>` is not implemented for `F`
@ -16,7 +16,7 @@ error[E0277]: the trait bound `for<'ccx> B: Bar<'ccx>` is not satisfied
--> $DIR/hrtb-higher-ranker-supertraits.rs:35:26
|
LL | where B : Bar<'x>
| - help: consider further restricting type parameter `B`: `, for<'ccx> B: Bar<'ccx>`
| - help: consider further restricting type parameter `B`: `, B: for<'ccx> Bar<'ccx>`
...
LL | want_bar_for_any_ccx(b);
| ^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B`

View File

@ -18,7 +18,7 @@ pub mod bar {
mod baz {
use bar;
use Foo;
pub fn baz<T: Copy + Foo>(x: T) -> T {
pub fn baz<T: Copy + Foo>(x: T) -> T {
if 0 == 1 {
bar::bar(x.zero())
} else {

View File

@ -4,7 +4,7 @@ error[E0382]: use of moved value: `x`
LL | pub fn baz<T: Foo>(x: T) -> T {
| -- - move occurs because `x` has type `T`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `T: Copy +`
| help: consider further restricting this bound: `T: Copy +`
LL | if 0 == 1 {
LL | bar::bar(x.zero())
| - value moved here

View File

@ -4,7 +4,7 @@ error[E0382]: use of moved value: `blk`
LL | fn foo<F:FnOnce()>(blk: F) {
| -- --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `F: Copy +`
| help: consider further restricting this bound: `F: Copy +`
LL | blk();
| --- value moved here
LL | blk();

View File

@ -4,7 +4,7 @@ error[E0382]: borrow of moved value: `x`
LL | fn move_then_borrow<T: Not<Output=T> + Clone>(x: T) {
| -- - move occurs because `x` has type `T`, which does not implement the `Copy` trait
| |
| help: consider adding a `Copy` constraint to this type argument: `T: Copy +`
| help: consider further restricting this bound: `T: Copy +`
LL | !x;
| - value moved here
LL |