Save colon span to suggest bounds.
This commit is contained in:
parent
03bbb98019
commit
74583852e8
13 changed files with 63 additions and 53 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(¶m.attrs, sym::may_dangle),
|
pure_wrt_drop: self.sess.contains_name(¶m.attrs, sym::may_dangle),
|
||||||
kind,
|
kind,
|
||||||
|
colon_span: param.colon_span.map(|s| self.lower_span(s)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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) { ... }
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -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`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue