1
Fork 0

Save colon span to suggest bounds.

This commit is contained in:
Camille GILLOT 2022-04-28 21:59:41 +02:00
parent 03bbb98019
commit 74583852e8
13 changed files with 63 additions and 53 deletions

View file

@ -397,6 +397,7 @@ pub struct GenericParam {
pub bounds: GenericBounds, pub bounds: GenericBounds,
pub is_placeholder: bool, pub is_placeholder: bool,
pub kind: GenericParamKind, pub kind: GenericParamKind,
pub colon_span: Option<Span>,
} }
impl GenericParam { impl GenericParam {

View file

@ -867,9 +867,12 @@ pub fn noop_flat_map_generic_param<T: MutVisitor>(
mut param: GenericParam, mut param: GenericParam,
vis: &mut T, vis: &mut T,
) -> SmallVec<[GenericParam; 1]> { ) -> SmallVec<[GenericParam; 1]> {
let GenericParam { id, ident, attrs, bounds, kind, is_placeholder: _ } = &mut param; let GenericParam { id, ident, attrs, bounds, kind, colon_span, is_placeholder: _ } = &mut param;
vis.visit_id(id); vis.visit_id(id);
vis.visit_ident(ident); vis.visit_ident(ident);
if let Some(ref mut colon_span) = colon_span {
vis.visit_span(colon_span);
}
visit_thin_attrs(attrs, vis); visit_thin_attrs(attrs, vis);
visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis)); visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis));
match kind { match kind {

View file

@ -707,6 +707,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: self.lower_span(ident.span), span: self.lower_span(ident.span),
pure_wrt_drop: false, pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind }, kind: hir::GenericParamKind::Lifetime { kind },
colon_span: None,
}) })
} }
@ -1304,6 +1305,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
pure_wrt_drop: false, pure_wrt_drop: false,
span: self.lower_span(span), span: self.lower_span(span),
kind: hir::GenericParamKind::Type { default: None, synthetic: true }, kind: hir::GenericParamKind::Type { default: None, synthetic: true },
colon_span: None,
}); });
if let Some(preds) = self.lower_generic_bound_predicate( if let Some(preds) = self.lower_generic_bound_predicate(
ident, ident,
@ -1396,6 +1398,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span, span,
pure_wrt_drop: false, pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind }, kind: hir::GenericParamKind::Lifetime { kind },
colon_span: None,
} }
}, },
)); ));
@ -1735,6 +1738,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span, span,
pure_wrt_drop: false, pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind }, kind: hir::GenericParamKind::Lifetime { kind },
colon_span: None,
} }
})); }));
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
@ -2006,6 +2010,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: self.lower_span(param.span()), span: self.lower_span(param.span()),
pure_wrt_drop: self.sess.contains_name(&param.attrs, sym::may_dangle), pure_wrt_drop: self.sess.contains_name(&param.attrs, sym::may_dangle),
kind, kind,
colon_span: param.colon_span.map(|s| self.lower_span(s)),
} }
} }

View file

@ -95,6 +95,7 @@ fn dummy_annotatable() -> Annotatable {
bounds: Default::default(), bounds: Default::default(),
is_placeholder: false, is_placeholder: false,
kind: GenericParamKind::Lifetime, kind: GenericParamKind::Lifetime,
colon_span: None,
}) })
} }

View file

@ -113,6 +113,7 @@ impl<'a> ExtCtxt<'a> {
bounds, bounds,
kind: ast::GenericParamKind::Type { default }, kind: ast::GenericParamKind::Type { default },
is_placeholder: false, is_placeholder: false,
colon_span: None,
} }
} }

View file

@ -149,6 +149,7 @@ pub fn placeholder(
ident, ident,
is_placeholder: true, is_placeholder: true,
kind: ast::GenericParamKind::Lifetime, kind: ast::GenericParamKind::Lifetime,
colon_span: None,
} }
}]), }]),
AstFragmentKind::Params => AstFragment::Params(smallvec![ast::Param { AstFragmentKind::Params => AstFragment::Params(smallvec![ast::Param {

View file

@ -17,7 +17,7 @@ use rustc_error_messages::MultiSpan;
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
use rustc_macros::HashStable_Generic; use rustc_macros::HashStable_Generic;
use rustc_span::hygiene::MacroKind; use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP}; use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass; use rustc_target::asm::InlineAsmRegOrRegClass;
@ -499,6 +499,7 @@ pub struct GenericParam<'hir> {
pub span: Span, pub span: Span,
pub pure_wrt_drop: bool, pub pure_wrt_drop: bool,
pub kind: GenericParamKind<'hir>, pub kind: GenericParamKind<'hir>,
pub colon_span: Option<Span>,
} }
impl<'hir> GenericParam<'hir> { impl<'hir> GenericParam<'hir> {
@ -515,40 +516,6 @@ impl<'hir> GenericParam<'hir> {
pub fn is_elided_lifetime(&self) -> bool { pub fn is_elided_lifetime(&self) -> bool {
matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided }) matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided })
} }
/// Returns the span of `:` after a generic parameter.
///
/// For example:
///
/// ```text
/// fn a<T:>()
/// ^
/// | here
/// here |
/// v
/// fn b<T :>()
///
/// fn c<T
///
/// :>()
/// ^
/// |
/// here
/// ```
pub fn colon_span_for_suggestions(&self, source_map: &SourceMap) -> Option<Span> {
let sp = source_map
.span_extend_while(self.span.shrink_to_hi(), |c| c.is_whitespace() || c == ':')
.ok()?;
let snippet = source_map.span_to_snippet(sp).ok()?;
let offset = snippet.find(':')?;
let colon_sp = sp
.with_lo(BytePos(sp.lo().0 + offset as u32))
.with_hi(BytePos(sp.lo().0 + (offset + ':'.len_utf8()) as u32));
Some(colon_sp)
}
} }
#[derive(Default)] #[derive(Default)]

View file

@ -363,6 +363,19 @@ pub fn suggest_constraining_type_params<'a>(
continue; continue;
} }
// If user has provided a colon, don't suggest adding another:
//
// fn foo<T:>(t: T) { ... }
// - insert: consider restricting this type parameter with `T: Foo`
if let Some(colon_span) = param.colon_span {
suggestions.push((
colon_span.shrink_to_hi(),
format!(" {}", constraint),
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
));
continue;
}
// If user hasn't provided any bounds, suggest adding a new one: // If user hasn't provided any bounds, suggest adding a new one:
// //
// fn foo<T>(t: T) { ... } // fn foo<T>(t: T) { ... }

View file

@ -30,8 +30,10 @@ impl<'a> Parser<'a> {
let ident = self.parse_ident()?; let ident = self.parse_ident()?;
// Parse optional colon and param bounds. // Parse optional colon and param bounds.
let mut colon_span = None;
let bounds = if self.eat(&token::Colon) { let bounds = if self.eat(&token::Colon) {
self.parse_generic_bounds(Some(self.prev_token.span))? colon_span = Some(self.prev_token.span);
self.parse_generic_bounds(colon_span)?
} else { } else {
Vec::new() Vec::new()
}; };
@ -45,6 +47,7 @@ impl<'a> Parser<'a> {
bounds, bounds,
kind: GenericParamKind::Type { default }, kind: GenericParamKind::Type { default },
is_placeholder: false, is_placeholder: false,
colon_span,
}) })
} }
@ -69,6 +72,7 @@ impl<'a> Parser<'a> {
bounds: Vec::new(), bounds: Vec::new(),
kind: GenericParamKind::Const { ty, kw_span: const_span, default }, kind: GenericParamKind::Const { ty, kw_span: const_span, default },
is_placeholder: false, is_placeholder: false,
colon_span: None,
}) })
} }
@ -97,10 +101,10 @@ impl<'a> Parser<'a> {
let param = if this.check_lifetime() { let param = if this.check_lifetime() {
let lifetime = this.expect_lifetime(); let lifetime = this.expect_lifetime();
// Parse lifetime parameter. // Parse lifetime parameter.
let bounds = if this.eat(&token::Colon) { let (colon_span, bounds) = if this.eat(&token::Colon) {
this.parse_lt_param_bounds() (Some(this.prev_token.span), this.parse_lt_param_bounds())
} else { } else {
Vec::new() (None, Vec::new())
}; };
Some(ast::GenericParam { Some(ast::GenericParam {
ident: lifetime.ident, ident: lifetime.ident,
@ -109,6 +113,7 @@ impl<'a> Parser<'a> {
bounds, bounds,
kind: ast::GenericParamKind::Lifetime, kind: ast::GenericParamKind::Lifetime,
is_placeholder: false, is_placeholder: false,
colon_span,
}) })
} else if this.check_keyword(kw::Const) { } else if this.check_keyword(kw::Const) {
// Parse const parameter. // Parse const parameter.

View file

@ -1868,18 +1868,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// instead we suggest `T: Foo + Bar` in that case. // instead we suggest `T: Foo + Bar` in that case.
match hir.get(id) { match hir.get(id) {
Node::GenericParam(param) => { Node::GenericParam(param) => {
let impl_trait = matches!( enum Introducer {
param.kind, Plus,
hir::GenericParamKind::Type { synthetic: true, .. }, Colon,
); Nothing,
}
let ast_generics = hir.get_generics(id.owner).unwrap(); let ast_generics = hir.get_generics(id.owner).unwrap();
let (sp, has_bounds) = if let Some(span) = let (sp, mut introducer) = if let Some(span) =
ast_generics.bounds_span_for_suggestions(def_id) ast_generics.bounds_span_for_suggestions(def_id)
{ {
(span, true) (span, Introducer::Plus)
} else if let Some(colon_span) = param.colon_span {
(colon_span.shrink_to_hi(), Introducer::Nothing)
} else { } else {
(hir.span(id).shrink_to_hi(), false) (param.span.shrink_to_hi(), Introducer::Colon)
}; };
if matches!(
param.kind,
hir::GenericParamKind::Type { synthetic: true, .. },
) {
introducer = Introducer::Plus
}
let trait_def_ids: FxHashSet<DefId> = ast_generics let trait_def_ids: FxHashSet<DefId> = ast_generics
.bounds_for_param(def_id) .bounds_for_param(def_id)
.flat_map(|bp| bp.bounds.iter()) .flat_map(|bp| bp.bounds.iter())
@ -1895,7 +1904,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
candidates.iter().map(|t| { candidates.iter().map(|t| {
format!( format!(
"{} {}", "{} {}",
if has_bounds || impl_trait { " +" } else { ":" }, match introducer {
Introducer::Plus => " +",
Introducer::Colon => ":",
Introducer::Nothing => "",
},
self.tcx.def_path_str(t.def_id), self.tcx.def_path_str(t.def_id),
) )
}), }),

View file

@ -70,7 +70,7 @@ where
} }
#[rustfmt::skip] #[rustfmt::skip]
fn existing_colon<T: Copy:>(t: T) { fn existing_colon<T: Copy>(t: T) {
//~^ HELP consider restricting type parameter `T` //~^ HELP consider restricting type parameter `T`
[t, t]; //~ use of moved value: `t` [t, t]; //~ use of moved value: `t`
} }

View file

@ -171,8 +171,8 @@ LL | [t, t];
| |
help: consider restricting type parameter `T` help: consider restricting type parameter `T`
| |
LL | fn existing_colon<T: Copy:>(t: T) { LL | fn existing_colon<T: Copy>(t: T) {
| ++++++ | ++++
error: aborting due to 11 previous errors error: aborting due to 11 previous errors

View file

@ -7,8 +7,8 @@ LL | t.clone();
= help: items from traits can only be used if the type parameter is bounded by the trait = help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `clone`, perhaps you need to restrict type parameter `T` with it: help: the following trait defines an item `clone`, perhaps you need to restrict type parameter `T` with it:
| |
LL | fn foo<T: Clone:>(t: T) { LL | fn foo<T: Clone>(t: T) {
| +++++++ | +++++
error: aborting due to previous error error: aborting due to previous error