Skip to content

Account for incorrect impl Foo<const N: ty> {} syntax #85346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 25, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -405,6 +405,21 @@ pub struct GenericParam {
pub kind: GenericParamKind,
}

impl GenericParam {
pub fn span(&self) -> Span {
match &self.kind {
GenericParamKind::Lifetime | GenericParamKind::Type { default: None } => {
self.ident.span
}
GenericParamKind::Type { default: Some(ty) } => self.ident.span.to(ty.span),
GenericParamKind::Const { kw_span, default: Some(default), .. } => {
kw_span.to(default.value.span)
}
GenericParamKind::Const { kw_span, default: None, ty } => kw_span.to(ty.span),
}
}
}

/// Represents lifetime, type and const parameters attached to a declaration of
/// a function, enum, trait, etc.
#[derive(Clone, Encodable, Decodable, Debug)]
79 changes: 73 additions & 6 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -6,9 +6,11 @@ use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Lit, LitKind, TokenKind};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec};
use rustc_ast::{BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item};
use rustc_ast::{ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, TyKind};
use rustc_ast::{
AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Mutability, Param, Pat,
PatKind, Path, PathSegment, QSelf, Ty, TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{pluralize, struct_span_err};
@@ -662,7 +664,7 @@ impl<'a> Parser<'a> {
let snapshot = self.clone();
self.bump();
let lo = self.token.span;
match self.parse_angle_args() {
match self.parse_angle_args(None) {
Ok(args) => {
let span = lo.to(self.prev_token.span);
// Detect trailing `>` like in `x.collect::Vec<_>>()`.
@@ -719,7 +721,7 @@ impl<'a> Parser<'a> {
let x = self.parse_seq_to_before_end(
&token::Gt,
SeqSep::trailing_allowed(token::Comma),
|p| p.parse_generic_arg(),
|p| p.parse_generic_arg(None),
);
match x {
Ok((_, _, false)) => {
@@ -1103,7 +1105,7 @@ impl<'a> Parser<'a> {
self.expect(&token::ModSep)?;

let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None };
self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?;
path.span = ty_span.to(self.prev_token.span);

let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty));
@@ -1909,6 +1911,71 @@ impl<'a> Parser<'a> {
Ok(expr)
}

fn recover_const_param_decl(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Option<GenericArg>> {
let snapshot = self.clone();
let param = match self.parse_const_param(vec![]) {
Ok(param) => param,
Err(mut err) => {
err.cancel();
*self = snapshot;
return Err(err);
}
};
let mut err =
self.struct_span_err(param.span(), "unexpected `const` parameter declaration");
err.span_label(param.span(), "expected a `const` expression, not a parameter declaration");
if let (Some(generics), Ok(snippet)) =
(ty_generics, self.sess.source_map().span_to_snippet(param.span()))
{
let (span, sugg) = match &generics.params[..] {
[] => (generics.span, format!("<{}>", snippet)),
[.., generic] => (generic.span().shrink_to_hi(), format!(", {}", snippet)),
};
err.multipart_suggestion(
"`const` parameters must be declared for the `impl`",
vec![(span, sugg), (param.span(), param.ident.to_string())],
Applicability::MachineApplicable,
);
}
let value = self.mk_expr_err(param.span());
err.emit();
return Ok(Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })));
}

pub fn recover_const_param_declaration(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Option<GenericArg>> {
// We have to check for a few different cases.
if let Ok(arg) = self.recover_const_param_decl(ty_generics) {
return Ok(arg);
}

// We haven't consumed `const` yet.
let start = self.token.span;
self.bump(); // `const`

// Detect and recover from the old, pre-RFC2000 syntax for const generics.
let mut err = self
.struct_span_err(start, "expected lifetime, type, or constant, found keyword `const`");
if self.check_const_arg() {
err.span_suggestion_verbose(
start.until(self.token.span),
"the `const` keyword is only needed in the definition of the type",
String::new(),
Applicability::MaybeIncorrect,
);
err.emit();
Ok(Some(GenericArg::Const(self.parse_const_arg()?)))
} else {
let after_kw_const = self.token.span;
self.recover_const_arg(after_kw_const, err).map(Some)
}
}

/// Try to recover from possible generic const argument without `{` and `}`.
///
/// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
@@ -1150,7 +1150,7 @@ impl<'a> Parser<'a> {
}

let fn_span_lo = self.token.span;
let mut segment = self.parse_path_segment(PathStyle::Expr)?;
let mut segment = self.parse_path_segment(PathStyle::Expr, None)?;
self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]);
self.check_turbofish_missing_angle_brackets(&mut segment);

5 changes: 4 additions & 1 deletion compiler/rustc_parse/src/parser/generics.rs
Original file line number Diff line number Diff line change
@@ -48,7 +48,10 @@ impl<'a> Parser<'a> {
})
}

fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
crate fn parse_const_param(
&mut self,
preceding_attrs: Vec<Attribute>,
) -> PResult<'a, GenericParam> {
let const_span = self.token.span;

self.expect_keyword(kw::Const)?;
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
@@ -514,7 +514,7 @@ impl<'a> Parser<'a> {
tokens: None,
})
} else {
self.parse_ty()?
self.parse_ty_with_generics_recovery(&generics)?
};

// If `for` is missing we try to recover.
88 changes: 49 additions & 39 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
@@ -3,10 +3,11 @@ use super::{Parser, TokenType};
use crate::maybe_whole;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Token};
use rustc_ast::{self as ast, AngleBracketedArg, AngleBracketedArgs, ParenthesizedArgs};
use rustc_ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
use rustc_ast::{GenericArg, GenericArgs};
use rustc_ast::{Path, PathSegment, QSelf};
use rustc_ast::{
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocTyConstraint,
AssocTyConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
Path, PathSegment, QSelf,
};
use rustc_errors::{pluralize, Applicability, PResult};
use rustc_span::source_map::{BytePos, Span};
use rustc_span::symbol::{kw, sym, Ident};
@@ -78,7 +79,7 @@ impl<'a> Parser<'a> {
}

let qself = QSelf { ty, path_span, position: path.segments.len() };
self.parse_path_segments(&mut path.segments, style)?;
self.parse_path_segments(&mut path.segments, style, None)?;

Ok((
qself,
@@ -119,6 +120,10 @@ impl<'a> Parser<'a> {
true
}

pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> {
self.parse_path_inner(style, None)
}

/// Parses simple paths.
///
/// `path = [::] segment+`
@@ -129,7 +134,11 @@ impl<'a> Parser<'a> {
/// `a::b::C::<D>` (with disambiguator)
/// `Fn(Args)` (without disambiguator)
/// `Fn::(Args)` (with disambiguator)
pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> {
pub(super) fn parse_path_inner(
&mut self,
style: PathStyle,
ty_generics: Option<&Generics>,
) -> PResult<'a, Path> {
maybe_whole!(self, NtPath, |path| {
if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some())
{
@@ -152,7 +161,7 @@ impl<'a> Parser<'a> {
if self.eat(&token::ModSep) {
segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
}
self.parse_path_segments(&mut segments, style)?;
self.parse_path_segments(&mut segments, style, ty_generics)?;

Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None })
}
@@ -161,9 +170,10 @@ impl<'a> Parser<'a> {
&mut self,
segments: &mut Vec<PathSegment>,
style: PathStyle,
ty_generics: Option<&Generics>,
) -> PResult<'a, ()> {
loop {
let segment = self.parse_path_segment(style)?;
let segment = self.parse_path_segment(style, ty_generics)?;
if style == PathStyle::Expr {
// In order to check for trailing angle brackets, we must have finished
// recursing (`parse_path_segment` can indirectly call this function),
@@ -191,7 +201,11 @@ impl<'a> Parser<'a> {
}
}

pub(super) fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> {
pub(super) fn parse_path_segment(
&mut self,
style: PathStyle,
ty_generics: Option<&Generics>,
) -> PResult<'a, PathSegment> {
let ident = self.parse_path_segment_ident()?;
let is_args_start = |token: &Token| {
matches!(
@@ -229,18 +243,21 @@ impl<'a> Parser<'a> {
let lo = self.token.span;
let args = if self.eat_lt() {
// `<'a, T, A = U>`
let args =
self.parse_angle_args_with_leading_angle_bracket_recovery(style, lo)?;
let args = self.parse_angle_args_with_leading_angle_bracket_recovery(
style,
lo,
ty_generics,
)?;
self.expect_gt()?;
let span = lo.to(self.prev_token.span);
AngleBracketedArgs { args, span }.into()
} else {
// `(T, U) -> R`
let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?;
let inputs_span = lo.to(self.prev_token.span);
let span = ident.span.to(self.prev_token.span);
let output =
self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?;
let span = ident.span.to(self.prev_token.span);
ParenthesizedArgs { span, inputs, inputs_span, output }.into()
};

@@ -275,6 +292,7 @@ impl<'a> Parser<'a> {
&mut self,
style: PathStyle,
lo: Span,
ty_generics: Option<&Generics>,
) -> PResult<'a, Vec<AngleBracketedArg>> {
// We need to detect whether there are extra leading left angle brackets and produce an
// appropriate error and suggestion. This cannot be implemented by looking ahead at
@@ -350,7 +368,7 @@ impl<'a> Parser<'a> {
let snapshot = if is_first_invocation { Some(self.clone()) } else { None };

debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)");
match self.parse_angle_args() {
match self.parse_angle_args(ty_generics) {
Ok(args) => Ok(args),
Err(mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
// Swap `self` with our backup of the parser state before attempting to parse
@@ -403,7 +421,7 @@ impl<'a> Parser<'a> {
.emit();

// Try again without unmatched angle bracket characters.
self.parse_angle_args()
self.parse_angle_args(ty_generics)
}
}
Err(e) => Err(e),
@@ -412,9 +430,12 @@ impl<'a> Parser<'a> {

/// Parses (possibly empty) list of generic arguments / associated item constraints,
/// possibly including trailing comma.
pub(super) fn parse_angle_args(&mut self) -> PResult<'a, Vec<AngleBracketedArg>> {
pub(super) fn parse_angle_args(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Vec<AngleBracketedArg>> {
let mut args = Vec::new();
while let Some(arg) = self.parse_angle_arg()? {
while let Some(arg) = self.parse_angle_arg(ty_generics)? {
args.push(arg);
if !self.eat(&token::Comma) {
if !self.token.kind.should_end_const_arg() {
@@ -431,9 +452,12 @@ impl<'a> Parser<'a> {
}

/// Parses a single argument in the angle arguments `<...>` of a path segment.
fn parse_angle_arg(&mut self) -> PResult<'a, Option<AngleBracketedArg>> {
fn parse_angle_arg(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Option<AngleBracketedArg>> {
let lo = self.token.span;
let arg = self.parse_generic_arg()?;
let arg = self.parse_generic_arg(ty_generics)?;
match arg {
Some(arg) => {
if self.check(&token::Colon) | self.check(&token::Eq) {
@@ -476,7 +500,7 @@ impl<'a> Parser<'a> {
/// That is, parse `<term>` in `Item = <term>`.
/// Right now, this only admits types in `<term>`.
fn parse_assoc_equality_term(&mut self, ident: Ident, eq: Span) -> PResult<'a, P<ast::Ty>> {
let arg = self.parse_generic_arg()?;
let arg = self.parse_generic_arg(None)?;
let span = ident.span.to(self.prev_token.span);
match arg {
Some(GenericArg::Type(ty)) => return Ok(ty),
@@ -563,7 +587,10 @@ impl<'a> Parser<'a> {

/// Parse a generic argument in a path segment.
/// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`.
pub(super) fn parse_generic_arg(&mut self) -> PResult<'a, Option<GenericArg>> {
pub(super) fn parse_generic_arg(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Option<GenericArg>> {
let start = self.token.span;
let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
// Parse lifetime argument.
@@ -580,25 +607,8 @@ impl<'a> Parser<'a> {
return self.recover_const_arg(start, err).map(Some);
}
}
} else if self.eat_keyword_noexpect(kw::Const) {
// Detect and recover from the old, pre-RFC2000 syntax for const generics.
let mut err = self.struct_span_err(
start,
"expected lifetime, type, or constant, found keyword `const`",
);
if self.check_const_arg() {
err.span_suggestion_verbose(
start.until(self.token.span),
"the `const` keyword is only needed in the definition of the type",
String::new(),
Applicability::MaybeIncorrect,
);
err.emit();
GenericArg::Const(self.parse_const_arg()?)
} else {
let after_kw_const = self.token.span;
return self.recover_const_arg(after_kw_const, err).map(Some);
}
} else if self.token.is_keyword(kw::Const) {
return self.recover_const_param_declaration(ty_generics);
} else {
return Ok(None);
};
38 changes: 32 additions & 6 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
@@ -4,9 +4,10 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};

use rustc_ast::ptr::P;
use rustc_ast::token::{self, Token, TokenKind};
use rustc_ast::{self as ast, BareFnTy, FnRetTy, GenericParam, Lifetime, MutTy, Ty, TyKind};
use rustc_ast::{GenericBound, GenericBounds, MacCall, Mutability};
use rustc_ast::{PolyTraitRef, TraitBoundModifier, TraitObjectSyntax};
use rustc_ast::{
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
};
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym};
@@ -98,6 +99,20 @@ impl<'a> Parser<'a> {
AllowCVariadic::No,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
None,
)
}

pub(super) fn parse_ty_with_generics_recovery(
&mut self,
ty_params: &Generics,
) -> PResult<'a, P<Ty>> {
self.parse_ty_common(
AllowPlus::Yes,
AllowCVariadic::No,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
Some(ty_params),
)
}

@@ -110,6 +125,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::Yes,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
None,
)
}

@@ -125,6 +141,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::No,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
None,
)
}

@@ -135,6 +152,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::Yes,
RecoverQPath::Yes,
RecoverReturnSign::OnlyFatArrow,
None,
)
}

@@ -152,6 +170,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::No,
recover_qpath,
recover_return_sign,
None,
)?;
FnRetTy::Ty(ty)
} else if recover_return_sign.can_recover(&self.token.kind) {
@@ -171,6 +190,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::No,
recover_qpath,
recover_return_sign,
None,
)?;
FnRetTy::Ty(ty)
} else {
@@ -184,6 +204,7 @@ impl<'a> Parser<'a> {
allow_c_variadic: AllowCVariadic,
recover_qpath: RecoverQPath,
recover_return_sign: RecoverReturnSign,
ty_generics: Option<&Generics>,
) -> PResult<'a, P<Ty>> {
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
@@ -233,7 +254,7 @@ impl<'a> Parser<'a> {
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
TyKind::Path(Some(qself), path)
} else if self.check_path() {
self.parse_path_start_ty(lo, allow_plus)?
self.parse_path_start_ty(lo, allow_plus, ty_generics)?
} else if self.can_begin_bound() {
self.parse_bare_trait_object(lo, allow_plus)?
} else if self.eat(&token::DotDotDot) {
@@ -512,9 +533,14 @@ impl<'a> Parser<'a> {
/// 1. a type macro, `mac!(...)`,
/// 2. a bare trait object, `B0 + ... + Bn`,
/// 3. or a path, `path::to::MyType`.
fn parse_path_start_ty(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> {
fn parse_path_start_ty(
&mut self,
lo: Span,
allow_plus: AllowPlus,
ty_generics: Option<&Generics>,
) -> PResult<'a, TyKind> {
// Simple path
let path = self.parse_path(PathStyle::Type)?;
let path = self.parse_path_inner(PathStyle::Type, ty_generics)?;
if self.eat(&token::Not) {
// Macro invocation in type position
Ok(TyKind::MacCall(MacCall {
15 changes: 15 additions & 0 deletions src/test/ui/parser/const-param-decl-on-type-instead-of-impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
struct NInts<const N: usize>([u8; N]);
impl NInts<const N: usize> {} //~ ERROR unexpected `const` parameter declaration

fn main() {
let _: () = 42; //~ ERROR mismatched types
}

fn banana(a: <T<const N: usize>>::BAR) {}
//~^ ERROR unexpected `const` parameter declaration
//~| ERROR cannot find type `T` in this scope
fn chaenomeles() {
path::path::Struct::<const N: usize>()
//~^ ERROR unexpected `const` parameter declaration
//~| ERROR failed to resolve: use of undeclared crate or module `path`
}
47 changes: 47 additions & 0 deletions src/test/ui/parser/const-param-decl-on-type-instead-of-impl.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
error: unexpected `const` parameter declaration
--> $DIR/const-param-decl-on-type-instead-of-impl.rs:2:12
|
LL | impl NInts<const N: usize> {}
| ^^^^^^^^^^^^^^ expected a `const` expression, not a parameter declaration
|
help: `const` parameters must be declared for the `impl`
|
LL | impl<const N: usize> NInts<N> {}
| ++++++++++++++++ ~

error: unexpected `const` parameter declaration
--> $DIR/const-param-decl-on-type-instead-of-impl.rs:8:17
|
LL | fn banana(a: <T<const N: usize>>::BAR) {}
| ^^^^^^^^^^^^^^ expected a `const` expression, not a parameter declaration

error: unexpected `const` parameter declaration
--> $DIR/const-param-decl-on-type-instead-of-impl.rs:12:26
|
LL | path::path::Struct::<const N: usize>()
| ^^^^^^^^^^^^^^ expected a `const` expression, not a parameter declaration

error[E0433]: failed to resolve: use of undeclared crate or module `path`
--> $DIR/const-param-decl-on-type-instead-of-impl.rs:12:5
|
LL | path::path::Struct::<const N: usize>()
| ^^^^ use of undeclared crate or module `path`

error[E0412]: cannot find type `T` in this scope
--> $DIR/const-param-decl-on-type-instead-of-impl.rs:8:15
|
LL | fn banana(a: <T<const N: usize>>::BAR) {}
| ^ not found in this scope

error[E0308]: mismatched types
--> $DIR/const-param-decl-on-type-instead-of-impl.rs:5:17
|
LL | let _: () = 42;
| -- ^^ expected `()`, found integer
| |
| expected due to this

error: aborting due to 6 previous errors

Some errors have detailed explanations: E0308, E0412, E0433.
For more information about an error, try `rustc --explain E0308`.
12 changes: 2 additions & 10 deletions src/tools/rustfmt/src/items.rs
Original file line number Diff line number Diff line change
@@ -2236,26 +2236,18 @@ fn rewrite_fn_base(
result.push_str(&param_indent.to_string_with_newline(context.config));
}

// Skip `pub(crate)`.
let lo_after_visibility = get_bytepos_after_visibility(fn_sig.visibility, span);
// A conservative estimation, the goal is to be over all parens in generics
let params_start = fn_sig
.generics
.params
.last()
.map_or(lo_after_visibility, |param| param.span().hi());
let params_end = if fd.inputs.is_empty() {
context
.snippet_provider
.span_after(mk_sp(params_start, span.hi()), ")")
.span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), ")")
} else {
let last_span = mk_sp(fd.inputs[fd.inputs.len() - 1].span().hi(), span.hi());
context.snippet_provider.span_after(last_span, ")")
};
let params_span = mk_sp(
context
.snippet_provider
.span_after(mk_sp(params_start, span.hi()), "("),
.span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), "("),
params_end,
);
let param_str = rewrite_params(