Skip to content

Commit 9fb446d

Browse files
committed
Deduplicate type param constraint suggestion code
1 parent 02bc412 commit 9fb446d

12 files changed

+128
-154
lines changed

src/librustc/hir/mod.rs

+75-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ use crate::ty::AdtKind;
1616
use crate::ty::query::Providers;
1717
use crate::util::nodemap::{NodeMap, FxHashSet};
1818

19-
use errors::FatalError;
19+
use errors::{Applicability, DiagnosticBuilder, FatalError};
2020
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
21-
use syntax::source_map::Spanned;
21+
use syntax::source_map::{Spanned, SourceMap};
2222
use syntax::ast::{self, CrateSugar, Ident, Name, NodeId, AsmDialect};
2323
use syntax::ast::{Attribute, Label, LitKind, StrStyle, FloatTy, IntTy, UintTy};
2424
pub use syntax::ast::{Mutability, Constness, Unsafety, Movability, CaptureBy};
@@ -644,6 +644,79 @@ impl Generics {
644644
self.params.iter().map(|p| p.span).collect::<Vec<Span>>().into()
645645
}
646646
}
647+
648+
/// Suggest restricting a type param with a new bound.
649+
pub fn suggest_constraining_type_param(
650+
&self,
651+
err: &mut DiagnosticBuilder<'_>,
652+
param_name: &str,
653+
constraint: &str,
654+
source_map: &SourceMap,
655+
span: Span,
656+
) -> bool {
657+
let restrict_msg = "consider further restricting this bound";
658+
if let Some(param) = self.params.iter().filter(|p| {
659+
p.name.ident().as_str() == param_name
660+
}).next() {
661+
if param_name.starts_with("impl ") {
662+
// `impl Trait` in argument:
663+
// `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
664+
err.span_suggestion(
665+
param.span,
666+
restrict_msg,
667+
// `impl CurrentTrait + MissingTrait`
668+
format!("{} + {}", param_name, constraint),
669+
Applicability::MachineApplicable,
670+
);
671+
} else if self.where_clause.predicates.is_empty() &&
672+
param.bounds.is_empty()
673+
{
674+
// If there are no bounds whatsoever, suggest adding a constraint
675+
// to the type parameter:
676+
// `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
677+
err.span_suggestion(
678+
param.span,
679+
"consider restricting this bound",
680+
format!("{}: {}", param_name, constraint),
681+
Applicability::MachineApplicable,
682+
);
683+
} else if !self.where_clause.predicates.is_empty() {
684+
// There is a `where` clause, so suggest expanding it:
685+
// `fn foo<T>(t: T) where T: Debug {}` →
686+
// `fn foo<T>(t: T) where T: Debug, T: Trait {}`
687+
err.span_suggestion(
688+
self.where_clause.span().unwrap().shrink_to_hi(),
689+
&format!("consider further restricting type parameter `{}`", param_name),
690+
format!(", {}: {}", param_name, constraint),
691+
Applicability::MachineApplicable,
692+
);
693+
} else {
694+
// If there is no `where` clause lean towards constraining to the
695+
// type parameter:
696+
// `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
697+
// `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
698+
let sp = param.span.with_hi(span.hi());
699+
let span = source_map.span_through_char(sp, ':');
700+
if sp != param.span && sp != span {
701+
// Only suggest if we have high certainty that the span
702+
// covers the colon in `foo<T: Trait>`.
703+
err.span_suggestion(
704+
span,
705+
restrict_msg,
706+
format!("{}: {} + ", param_name, constraint),
707+
Applicability::MachineApplicable,
708+
);
709+
} else {
710+
err.span_label(
711+
param.span,
712+
&format!("consider adding a `where {}: {}` bound", param_name, constraint),
713+
);
714+
}
715+
}
716+
return true;
717+
}
718+
false
719+
}
647720
}
648721

649722
/// Synthetic type parameters are converted to another form during lowering; this allows

src/librustc/traits/error_reporting.rs

+16-65
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
10911091
}
10921092
fn suggest_restricting_param_bound(
10931093
&self,
1094-
err: &mut DiagnosticBuilder<'_>,
1094+
mut err: &mut DiagnosticBuilder<'_>,
10951095
trait_ref: &ty::PolyTraitRef<'_>,
10961096
body_id: hir::HirId,
10971097
) {
@@ -1102,7 +1102,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11021102
_ => return,
11031103
};
11041104

1105-
let mut suggest_restriction = |generics: &hir::Generics, msg| {
1105+
let suggest_restriction = |
1106+
generics: &hir::Generics,
1107+
msg,
1108+
err: &mut DiagnosticBuilder<'_>,
1109+
| {
11061110
let span = generics.where_clause.span_for_predicates_or_empty_place();
11071111
if !span.from_expansion() && span.desugaring_kind().is_none() {
11081112
err.span_suggestion(
@@ -1132,7 +1136,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11321136
kind: hir::TraitItemKind::Method(..), ..
11331137
}) if param_ty && self_ty == self.tcx.types.self_param => {
11341138
// Restricting `Self` for a single method.
1135-
suggest_restriction(&generics, "`Self`");
1139+
suggest_restriction(&generics, "`Self`", err);
11361140
return;
11371141
}
11381142

@@ -1154,7 +1158,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11541158
kind: hir::ItemKind::Impl(_, _, _, generics, ..), ..
11551159
}) if projection.is_some() => {
11561160
// Missing associated type bound.
1157-
suggest_restriction(&generics, "the associated type");
1161+
suggest_restriction(&generics, "the associated type", err);
11581162
return;
11591163
}
11601164

@@ -1183,68 +1187,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11831187
hir::Node::ImplItem(hir::ImplItem { generics, span, .. })
11841188
if param_ty => {
11851189
// Missing generic type parameter bound.
1186-
let restrict_msg = "consider further restricting this bound";
11871190
let param_name = self_ty.to_string();
1188-
for param in generics.params.iter().filter(|p| {
1189-
p.name.ident().as_str() == param_name
1190-
}) {
1191-
if param_name.starts_with("impl ") {
1192-
// `impl Trait` in argument:
1193-
// `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
1194-
err.span_suggestion(
1195-
param.span,
1196-
restrict_msg,
1197-
// `impl CurrentTrait + MissingTrait`
1198-
format!("{} + {}", param.name.ident(), trait_ref),
1199-
Applicability::MachineApplicable,
1200-
);
1201-
} else if generics.where_clause.predicates.is_empty() &&
1202-
param.bounds.is_empty()
1203-
{
1204-
// If there are no bounds whatsoever, suggest adding a constraint
1205-
// to the type parameter:
1206-
// `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
1207-
err.span_suggestion(
1208-
param.span,
1209-
"consider restricting this bound",
1210-
format!("{}", trait_ref.to_predicate()),
1211-
Applicability::MachineApplicable,
1212-
);
1213-
} else if !generics.where_clause.predicates.is_empty() {
1214-
// There is a `where` clause, so suggest expanding it:
1215-
// `fn foo<T>(t: T) where T: Debug {}` →
1216-
// `fn foo<T>(t: T) where T: Debug, T: Trait {}`
1217-
err.span_suggestion(
1218-
generics.where_clause.span().unwrap().shrink_to_hi(),
1219-
&format!(
1220-
"consider further restricting type parameter `{}`",
1221-
param_name,
1222-
),
1223-
format!(", {}", trait_ref.to_predicate()),
1224-
Applicability::MachineApplicable,
1225-
);
1226-
} else {
1227-
// If there is no `where` clause lean towards constraining to the
1228-
// type parameter:
1229-
// `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
1230-
// `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
1231-
let sp = param.span.with_hi(span.hi());
1232-
let span = self.tcx.sess.source_map()
1233-
.span_through_char(sp, ':');
1234-
if sp != param.span && sp != span {
1235-
// Only suggest if we have high certainty that the span
1236-
// covers the colon in `foo<T: Trait>`.
1237-
err.span_suggestion(span, restrict_msg, format!(
1238-
"{} + ",
1239-
trait_ref.to_predicate(),
1240-
), Applicability::MachineApplicable);
1241-
} else {
1242-
err.span_label(param.span, &format!(
1243-
"consider adding a `where {}` bound",
1244-
trait_ref.to_predicate(),
1245-
));
1246-
}
1247-
}
1191+
let constraint = trait_ref.to_string();
1192+
if generics.suggest_constraining_type_param(
1193+
&mut err,
1194+
&param_name,
1195+
&constraint,
1196+
self.tcx.sess.source_map(),
1197+
*span,
1198+
) {
12481199
return;
12491200
}
12501201
}

src/librustc_mir/borrow_check/conflict_errors.rs

+7-57
Original file line numberDiff line numberDiff line change
@@ -233,63 +233,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
233233
let generics = tcx.generics_of(self.mir_def_id);
234234
let param = generics.type_param(&param_ty, tcx);
235235
let generics = tcx.hir().get_generics(self.mir_def_id).unwrap();
236-
let msg = "consider adding a `Copy` constraint to this type argument";
237-
for param in generics.params.iter().filter(|p| {
238-
p.name.ident().as_str() == param.name.as_str()
239-
}) {
240-
let param_name = param.name.ident().as_str();
241-
if param_name.starts_with("impl ") {
242-
// `impl Trait` in argument:
243-
// `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
244-
err.span_suggestion(
245-
param.span,
246-
msg,
247-
// `impl CurrentTrait + MissingTrait`
248-
format!("{} + Copy", param_name),
249-
Applicability::MachineApplicable,
250-
);
251-
} else if generics.where_clause.predicates.is_empty() &&
252-
param.bounds.is_empty()
253-
{
254-
// If there are no bounds whatsoever, suggest adding a constraint
255-
// to the type parameter:
256-
// `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
257-
err.span_suggestion(
258-
param.span,
259-
msg,
260-
format!("{}: Copy", param_name),
261-
Applicability::MachineApplicable,
262-
);
263-
} else if !generics.where_clause.predicates.is_empty() {
264-
// There is a `where` clause, so suggest expanding it:
265-
// `fn foo<T>(t: T) where T: Debug {}` →
266-
// `fn foo<T>(t: T) where T: Debug, T: Trait {}`
267-
err.span_suggestion(
268-
generics.where_clause.span().unwrap().shrink_to_hi(),
269-
msg,
270-
format!(", {}: Copy", param_name),
271-
Applicability::MachineApplicable,
272-
);
273-
} else {
274-
// If there is no `where` clause lean towards constraining to the
275-
// type parameter:
276-
// `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
277-
// `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
278-
let sp = param.span.with_hi(span.hi());
279-
let span = tcx.sess.source_map()
280-
.span_through_char(sp, ':');
281-
if sp != param.span && sp != span {
282-
// Only suggest if we have high certainty that the span
283-
// covers the colon in `foo<T: Trait>`.
284-
err.span_suggestion(span, msg, format!(
285-
"{}: Copy +",
286-
param_name,
287-
), Applicability::MachineApplicable);
288-
} else {
289-
err.span_label(param.span, msg);
290-
}
291-
}
292-
}
236+
generics.suggest_constraining_type_param(
237+
&mut err,
238+
&param.name.as_str(),
239+
"Copy",
240+
tcx.sess.source_map(),
241+
span,
242+
);
293243
}
294244
let span = if let Some(local) = place.as_local() {
295245
let decl = &self.body.local_decls[local];

0 commit comments

Comments
 (0)