use syntax::symbol::{sym, kw};
use syntax_pos::{DUMMY_SP, Span, ExpnKind, MultiSpan};
use rustc::hir::def_id::LOCAL_CRATE;
+use syntax_pos::source_map::SourceMap;
use rustc_error_codes::*;
// Missing generic type parameter bound.
let param_name = self_ty.to_string();
let constraint = trait_ref.to_string();
- if generics.suggest_constraining_type_param(
+ if suggest_constraining_type_param(
+ generics,
&mut err,
¶m_name,
&constraint,
}
}
}
+
+/// Suggest restricting a type param with a new bound.
+pub fn suggest_constraining_type_param(
+ generics: &hir::Generics,
+ 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) = generics.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 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!("{}: {}", param_name, constraint),
+ 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!(", {}: {}", 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
+}