Skip to content

Commit 910c565

Browse files
committed
Auto merge of rust-lang#13376 - DropDemBits:strip-generic-bounds-and-defaults, r=Veykril
internal: Add `GenericParamList::to_generic_args` and `{TypeParam,ConstParam}::remove_default` APIs Also fixes `generate_impl` not removing the default const param value, though it seems that no one has encountered or reported that issue yet 😅 This initially started out as refactoring `utils::generate_impl_text_inner` to understand it better (which was the reason for adding `{TypeParam,ConstParam}::remove_default`), but ended up also finding another place that needed `GenericParamList::to_generic_args`, hence its addition in here.
2 parents 61504c8 + 1015a17 commit 910c565

File tree

5 files changed

+127
-75
lines changed

5 files changed

+127
-75
lines changed

crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs

+2-32
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use ide_db::{
99
search::FileReference,
1010
FxHashSet, RootDatabase,
1111
};
12-
use itertools::{Itertools, Position};
12+
use itertools::Itertools;
1313
use syntax::{
1414
ast::{
1515
self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
@@ -298,37 +298,7 @@ fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList
298298
let name = variant.name()?;
299299
let ty = generics
300300
.filter(|generics| generics.generic_params().count() > 0)
301-
.map(|generics| {
302-
let mut generic_str = String::with_capacity(8);
303-
304-
for (p, more) in generics.generic_params().with_position().map(|p| match p {
305-
Position::First(p) | Position::Middle(p) => (p, true),
306-
Position::Last(p) | Position::Only(p) => (p, false),
307-
}) {
308-
match p {
309-
ast::GenericParam::ConstParam(konst) => {
310-
if let Some(name) = konst.name() {
311-
generic_str.push_str(name.text().as_str());
312-
}
313-
}
314-
ast::GenericParam::LifetimeParam(lt) => {
315-
if let Some(lt) = lt.lifetime() {
316-
generic_str.push_str(lt.text().as_str());
317-
}
318-
}
319-
ast::GenericParam::TypeParam(ty) => {
320-
if let Some(name) = ty.name() {
321-
generic_str.push_str(name.text().as_str());
322-
}
323-
}
324-
}
325-
if more {
326-
generic_str.push_str(", ");
327-
}
328-
}
329-
330-
make::ty(&format!("{}<{}>", &name.text(), &generic_str))
331-
})
301+
.map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args())))
332302
.unwrap_or_else(|| make::ty(&name.text()));
333303

334304
// change from a record to a tuple field list

crates/ide-assists/src/handlers/generate_impl.rs

+13
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ mod tests {
5252

5353
use super::*;
5454

55+
// FIXME: break up into separate test fns
5556
#[test]
5657
fn test_add_impl() {
5758
check_assist(
@@ -134,6 +135,18 @@ mod tests {
134135
}"#,
135136
);
136137

138+
check_assist(
139+
generate_impl,
140+
r#"
141+
struct Defaulted<const N: i32 = 0> {}$0"#,
142+
r#"
143+
struct Defaulted<const N: i32 = 0> {}
144+
145+
impl<const N: i32> Defaulted<N> {
146+
$0
147+
}"#,
148+
);
149+
137150
check_assist(
138151
generate_impl,
139152
r#"pub trait Trait {}

crates/ide-assists/src/utils.rs

+38-38
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
33
use std::ops;
44

5-
use itertools::Itertools;
6-
75
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
86
use hir::{db::HirDatabase, HirDisplay, Semantics};
97
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
@@ -15,7 +13,7 @@ use syntax::{
1513
edit_in_place::{AttrsOwnerEdit, Removable},
1614
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
1715
},
18-
ted, AstNode, AstToken, Direction, SmolStr, SourceFile,
16+
ted, AstNode, AstToken, Direction, SourceFile,
1917
SyntaxKind::*,
2018
SyntaxNode, TextRange, TextSize, T,
2119
};
@@ -424,34 +422,44 @@ pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &
424422
}
425423

426424
fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
427-
let generic_params = adt.generic_param_list();
425+
// Ensure lifetime params are before type & const params
426+
let generic_params = adt.generic_param_list().map(|generic_params| {
427+
let lifetime_params =
428+
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
429+
let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
430+
// remove defaults since they can't be specified in impls
431+
match param {
432+
ast::TypeOrConstParam::Type(param) => {
433+
let param = param.clone_for_update();
434+
param.remove_default();
435+
Some(ast::GenericParam::TypeParam(param))
436+
}
437+
ast::TypeOrConstParam::Const(param) => {
438+
let param = param.clone_for_update();
439+
param.remove_default();
440+
Some(ast::GenericParam::ConstParam(param))
441+
}
442+
}
443+
});
444+
445+
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
446+
});
447+
448+
// FIXME: use syntax::make & mutable AST apis instead
449+
// `trait_text` and `code` can't be opaque blobs of text
428450
let mut buf = String::with_capacity(code.len());
451+
452+
// Copy any cfg attrs from the original adt
429453
buf.push_str("\n\n");
430-
adt.attrs()
431-
.filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
432-
.for_each(|attr| buf.push_str(format!("{}\n", attr).as_str()));
454+
let cfg_attrs = adt
455+
.attrs()
456+
.filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
457+
cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
458+
459+
// `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
433460
buf.push_str("impl");
434461
if let Some(generic_params) = &generic_params {
435-
let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
436-
let toc_params = generic_params.type_or_const_params().map(|toc_param| {
437-
let type_param = match toc_param {
438-
ast::TypeOrConstParam::Type(x) => x,
439-
ast::TypeOrConstParam::Const(x) => return x.syntax().to_string(),
440-
};
441-
let mut buf = String::new();
442-
if let Some(it) = type_param.name() {
443-
format_to!(buf, "{}", it.syntax());
444-
}
445-
if let Some(it) = type_param.colon_token() {
446-
format_to!(buf, "{} ", it);
447-
}
448-
if let Some(it) = type_param.type_bound_list() {
449-
format_to!(buf, "{}", it.syntax());
450-
}
451-
buf
452-
});
453-
let generics = lifetimes.chain(toc_params).format(", ");
454-
format_to!(buf, "<{}>", generics);
462+
format_to!(buf, "{generic_params}");
455463
}
456464
buf.push(' ');
457465
if let Some(trait_text) = trait_text {
@@ -460,23 +468,15 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
460468
}
461469
buf.push_str(&adt.name().unwrap().text());
462470
if let Some(generic_params) = generic_params {
463-
let lifetime_params = generic_params
464-
.lifetime_params()
465-
.filter_map(|it| it.lifetime())
466-
.map(|it| SmolStr::from(it.text()));
467-
let toc_params = generic_params
468-
.type_or_const_params()
469-
.filter_map(|it| it.name())
470-
.map(|it| SmolStr::from(it.text()));
471-
format_to!(buf, "<{}>", lifetime_params.chain(toc_params).format(", "))
471+
format_to!(buf, "{}", generic_params.to_generic_args());
472472
}
473473

474474
match adt.where_clause() {
475475
Some(where_clause) => {
476-
format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
476+
format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
477477
}
478478
None => {
479-
format_to!(buf, " {{\n{}\n}}", code);
479+
format_to!(buf, " {{\n{code}\n}}");
480480
}
481481
}
482482

crates/syntax/src/ast/edit_in_place.rs

+55-1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,24 @@ impl ast::GenericParamList {
235235
}
236236
}
237237
}
238+
239+
/// Constructs a matching [`ast::GenericArgList`]
240+
pub fn to_generic_args(&self) -> ast::GenericArgList {
241+
let args = self.generic_params().filter_map(|param| match param {
242+
ast::GenericParam::LifetimeParam(it) => {
243+
Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?)))
244+
}
245+
ast::GenericParam::TypeParam(it) => {
246+
Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
247+
}
248+
ast::GenericParam::ConstParam(it) => {
249+
// Name-only const params get parsed as `TypeArg`s
250+
Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
251+
}
252+
});
253+
254+
make::generic_arg_list(args)
255+
}
238256
}
239257

240258
impl ast::WhereClause {
@@ -248,6 +266,42 @@ impl ast::WhereClause {
248266
}
249267
}
250268

269+
impl ast::TypeParam {
270+
pub fn remove_default(&self) {
271+
if let Some((eq, last)) = self
272+
.syntax()
273+
.children_with_tokens()
274+
.find(|it| it.kind() == T![=])
275+
.zip(self.syntax().last_child_or_token())
276+
{
277+
ted::remove_all(eq..=last);
278+
279+
// remove any trailing ws
280+
if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
281+
last.detach();
282+
}
283+
}
284+
}
285+
}
286+
287+
impl ast::ConstParam {
288+
pub fn remove_default(&self) {
289+
if let Some((eq, last)) = self
290+
.syntax()
291+
.children_with_tokens()
292+
.find(|it| it.kind() == T![=])
293+
.zip(self.syntax().last_child_or_token())
294+
{
295+
ted::remove_all(eq..=last);
296+
297+
// remove any trailing ws
298+
if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
299+
last.detach();
300+
}
301+
}
302+
}
303+
}
304+
251305
pub trait Removable: AstNode {
252306
fn remove(&self);
253307
}
@@ -264,7 +318,7 @@ impl Removable for ast::TypeBoundList {
264318
impl ast::PathSegment {
265319
pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
266320
if self.generic_arg_list().is_none() {
267-
let arg_list = make::generic_arg_list().clone_for_update();
321+
let arg_list = make::generic_arg_list(empty()).clone_for_update();
268322
ted::append_child(self.syntax(), arg_list.syntax());
269323
}
270324
self.generic_arg_list().unwrap()

crates/syntax/src/ast/make.rs

+19-4
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ pub mod ext {
8888
block_expr(None, None)
8989
}
9090

91+
pub fn ty_name(name: ast::Name) -> ast::Type {
92+
ty_path(ident_path(&format!("{name}")))
93+
}
9194
pub fn ty_bool() -> ast::Type {
9295
ty_path(ident_path("bool"))
9396
}
@@ -160,6 +163,7 @@ pub fn assoc_item_list() -> ast::AssocItemList {
160163
ast_from_text("impl C for D {}")
161164
}
162165

166+
// FIXME: `ty_params` should be `ast::GenericArgList`
163167
pub fn impl_(
164168
ty: ast::Path,
165169
params: Option<ast::GenericParamList>,
@@ -185,10 +189,6 @@ pub fn impl_trait(
185189
ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
186190
}
187191

188-
pub(crate) fn generic_arg_list() -> ast::GenericArgList {
189-
ast_from_text("const S: T<> = ();")
190-
}
191-
192192
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
193193
ast_from_text(&format!("type __ = {name_ref};"))
194194
}
@@ -718,6 +718,21 @@ pub fn generic_param_list(
718718
ast_from_text(&format!("fn f<{args}>() {{ }}"))
719719
}
720720

721+
pub fn type_arg(ty: ast::Type) -> ast::TypeArg {
722+
ast_from_text(&format!("const S: T<{ty}> = ();"))
723+
}
724+
725+
pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
726+
ast_from_text(&format!("const S: T<{lifetime}> = ();"))
727+
}
728+
729+
pub(crate) fn generic_arg_list(
730+
args: impl IntoIterator<Item = ast::GenericArg>,
731+
) -> ast::GenericArgList {
732+
let args = args.into_iter().join(", ");
733+
ast_from_text(&format!("const S: T<{args}> = ();"))
734+
}
735+
721736
pub fn visibility_pub_crate() -> ast::Visibility {
722737
ast_from_text("pub(crate) struct S")
723738
}

0 commit comments

Comments
 (0)