mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +00:00
Auto merge of #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.
This commit is contained in:
commit
910c565b17
5 changed files with 127 additions and 75 deletions
|
@ -9,7 +9,7 @@ use ide_db::{
|
||||||
search::FileReference,
|
search::FileReference,
|
||||||
FxHashSet, RootDatabase,
|
FxHashSet, RootDatabase,
|
||||||
};
|
};
|
||||||
use itertools::{Itertools, Position};
|
use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
|
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
|
||||||
let name = variant.name()?;
|
let name = variant.name()?;
|
||||||
let ty = generics
|
let ty = generics
|
||||||
.filter(|generics| generics.generic_params().count() > 0)
|
.filter(|generics| generics.generic_params().count() > 0)
|
||||||
.map(|generics| {
|
.map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args())))
|
||||||
let mut generic_str = String::with_capacity(8);
|
|
||||||
|
|
||||||
for (p, more) in generics.generic_params().with_position().map(|p| match p {
|
|
||||||
Position::First(p) | Position::Middle(p) => (p, true),
|
|
||||||
Position::Last(p) | Position::Only(p) => (p, false),
|
|
||||||
}) {
|
|
||||||
match p {
|
|
||||||
ast::GenericParam::ConstParam(konst) => {
|
|
||||||
if let Some(name) = konst.name() {
|
|
||||||
generic_str.push_str(name.text().as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::GenericParam::LifetimeParam(lt) => {
|
|
||||||
if let Some(lt) = lt.lifetime() {
|
|
||||||
generic_str.push_str(lt.text().as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::GenericParam::TypeParam(ty) => {
|
|
||||||
if let Some(name) = ty.name() {
|
|
||||||
generic_str.push_str(name.text().as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if more {
|
|
||||||
generic_str.push_str(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
make::ty(&format!("{}<{}>", &name.text(), &generic_str))
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| make::ty(&name.text()));
|
.unwrap_or_else(|| make::ty(&name.text()));
|
||||||
|
|
||||||
// change from a record to a tuple field list
|
// change from a record to a tuple field list
|
||||||
|
|
|
@ -52,6 +52,7 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
// FIXME: break up into separate test fns
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_impl() {
|
fn test_add_impl() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
@ -134,6 +135,18 @@ mod tests {
|
||||||
}"#,
|
}"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
generate_impl,
|
||||||
|
r#"
|
||||||
|
struct Defaulted<const N: i32 = 0> {}$0"#,
|
||||||
|
r#"
|
||||||
|
struct Defaulted<const N: i32 = 0> {}
|
||||||
|
|
||||||
|
impl<const N: i32> Defaulted<N> {
|
||||||
|
$0
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
|
||||||
check_assist(
|
check_assist(
|
||||||
generate_impl,
|
generate_impl,
|
||||||
r#"pub trait Trait {}
|
r#"pub trait Trait {}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
|
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
|
||||||
use hir::{db::HirDatabase, HirDisplay, Semantics};
|
use hir::{db::HirDatabase, HirDisplay, Semantics};
|
||||||
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
|
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
|
||||||
|
@ -15,7 +13,7 @@ use syntax::{
|
||||||
edit_in_place::{AttrsOwnerEdit, Removable},
|
edit_in_place::{AttrsOwnerEdit, Removable},
|
||||||
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
|
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
|
||||||
},
|
},
|
||||||
ted, AstNode, AstToken, Direction, SmolStr, SourceFile,
|
ted, AstNode, AstToken, Direction, SourceFile,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxNode, TextRange, TextSize, T,
|
SyntaxNode, TextRange, TextSize, T,
|
||||||
};
|
};
|
||||||
|
@ -424,34 +422,44 @@ pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
|
fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
|
||||||
let generic_params = adt.generic_param_list();
|
// Ensure lifetime params are before type & const params
|
||||||
|
let generic_params = adt.generic_param_list().map(|generic_params| {
|
||||||
|
let lifetime_params =
|
||||||
|
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
||||||
|
let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
|
||||||
|
// remove defaults since they can't be specified in impls
|
||||||
|
match param {
|
||||||
|
ast::TypeOrConstParam::Type(param) => {
|
||||||
|
let param = param.clone_for_update();
|
||||||
|
param.remove_default();
|
||||||
|
Some(ast::GenericParam::TypeParam(param))
|
||||||
|
}
|
||||||
|
ast::TypeOrConstParam::Const(param) => {
|
||||||
|
let param = param.clone_for_update();
|
||||||
|
param.remove_default();
|
||||||
|
Some(ast::GenericParam::ConstParam(param))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
|
||||||
|
});
|
||||||
|
|
||||||
|
// FIXME: use syntax::make & mutable AST apis instead
|
||||||
|
// `trait_text` and `code` can't be opaque blobs of text
|
||||||
let mut buf = String::with_capacity(code.len());
|
let mut buf = String::with_capacity(code.len());
|
||||||
|
|
||||||
|
// Copy any cfg attrs from the original adt
|
||||||
buf.push_str("\n\n");
|
buf.push_str("\n\n");
|
||||||
adt.attrs()
|
let cfg_attrs = adt
|
||||||
.filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
|
.attrs()
|
||||||
.for_each(|attr| buf.push_str(format!("{}\n", attr).as_str()));
|
.filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
|
||||||
|
cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
|
||||||
|
|
||||||
|
// `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
|
||||||
buf.push_str("impl");
|
buf.push_str("impl");
|
||||||
if let Some(generic_params) = &generic_params {
|
if let Some(generic_params) = &generic_params {
|
||||||
let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
|
format_to!(buf, "{generic_params}");
|
||||||
let toc_params = generic_params.type_or_const_params().map(|toc_param| {
|
|
||||||
let type_param = match toc_param {
|
|
||||||
ast::TypeOrConstParam::Type(x) => x,
|
|
||||||
ast::TypeOrConstParam::Const(x) => return x.syntax().to_string(),
|
|
||||||
};
|
|
||||||
let mut buf = String::new();
|
|
||||||
if let Some(it) = type_param.name() {
|
|
||||||
format_to!(buf, "{}", it.syntax());
|
|
||||||
}
|
|
||||||
if let Some(it) = type_param.colon_token() {
|
|
||||||
format_to!(buf, "{} ", it);
|
|
||||||
}
|
|
||||||
if let Some(it) = type_param.type_bound_list() {
|
|
||||||
format_to!(buf, "{}", it.syntax());
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
});
|
|
||||||
let generics = lifetimes.chain(toc_params).format(", ");
|
|
||||||
format_to!(buf, "<{}>", generics);
|
|
||||||
}
|
}
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
if let Some(trait_text) = trait_text {
|
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
|
||||||
}
|
}
|
||||||
buf.push_str(&adt.name().unwrap().text());
|
buf.push_str(&adt.name().unwrap().text());
|
||||||
if let Some(generic_params) = generic_params {
|
if let Some(generic_params) = generic_params {
|
||||||
let lifetime_params = generic_params
|
format_to!(buf, "{}", generic_params.to_generic_args());
|
||||||
.lifetime_params()
|
|
||||||
.filter_map(|it| it.lifetime())
|
|
||||||
.map(|it| SmolStr::from(it.text()));
|
|
||||||
let toc_params = generic_params
|
|
||||||
.type_or_const_params()
|
|
||||||
.filter_map(|it| it.name())
|
|
||||||
.map(|it| SmolStr::from(it.text()));
|
|
||||||
format_to!(buf, "<{}>", lifetime_params.chain(toc_params).format(", "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match adt.where_clause() {
|
match adt.where_clause() {
|
||||||
Some(where_clause) => {
|
Some(where_clause) => {
|
||||||
format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
|
format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
format_to!(buf, " {{\n{}\n}}", code);
|
format_to!(buf, " {{\n{code}\n}}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -235,6 +235,24 @@ impl ast::GenericParamList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a matching [`ast::GenericArgList`]
|
||||||
|
pub fn to_generic_args(&self) -> ast::GenericArgList {
|
||||||
|
let args = self.generic_params().filter_map(|param| match param {
|
||||||
|
ast::GenericParam::LifetimeParam(it) => {
|
||||||
|
Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?)))
|
||||||
|
}
|
||||||
|
ast::GenericParam::TypeParam(it) => {
|
||||||
|
Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
|
||||||
|
}
|
||||||
|
ast::GenericParam::ConstParam(it) => {
|
||||||
|
// Name-only const params get parsed as `TypeArg`s
|
||||||
|
Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
make::generic_arg_list(args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::WhereClause {
|
impl ast::WhereClause {
|
||||||
|
@ -248,6 +266,42 @@ impl ast::WhereClause {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ast::TypeParam {
|
||||||
|
pub fn remove_default(&self) {
|
||||||
|
if let Some((eq, last)) = self
|
||||||
|
.syntax()
|
||||||
|
.children_with_tokens()
|
||||||
|
.find(|it| it.kind() == T![=])
|
||||||
|
.zip(self.syntax().last_child_or_token())
|
||||||
|
{
|
||||||
|
ted::remove_all(eq..=last);
|
||||||
|
|
||||||
|
// remove any trailing ws
|
||||||
|
if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
|
||||||
|
last.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ast::ConstParam {
|
||||||
|
pub fn remove_default(&self) {
|
||||||
|
if let Some((eq, last)) = self
|
||||||
|
.syntax()
|
||||||
|
.children_with_tokens()
|
||||||
|
.find(|it| it.kind() == T![=])
|
||||||
|
.zip(self.syntax().last_child_or_token())
|
||||||
|
{
|
||||||
|
ted::remove_all(eq..=last);
|
||||||
|
|
||||||
|
// remove any trailing ws
|
||||||
|
if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
|
||||||
|
last.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Removable: AstNode {
|
pub trait Removable: AstNode {
|
||||||
fn remove(&self);
|
fn remove(&self);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +318,7 @@ impl Removable for ast::TypeBoundList {
|
||||||
impl ast::PathSegment {
|
impl ast::PathSegment {
|
||||||
pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
|
pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
|
||||||
if self.generic_arg_list().is_none() {
|
if self.generic_arg_list().is_none() {
|
||||||
let arg_list = make::generic_arg_list().clone_for_update();
|
let arg_list = make::generic_arg_list(empty()).clone_for_update();
|
||||||
ted::append_child(self.syntax(), arg_list.syntax());
|
ted::append_child(self.syntax(), arg_list.syntax());
|
||||||
}
|
}
|
||||||
self.generic_arg_list().unwrap()
|
self.generic_arg_list().unwrap()
|
||||||
|
|
|
@ -88,6 +88,9 @@ pub mod ext {
|
||||||
block_expr(None, None)
|
block_expr(None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ty_name(name: ast::Name) -> ast::Type {
|
||||||
|
ty_path(ident_path(&format!("{name}")))
|
||||||
|
}
|
||||||
pub fn ty_bool() -> ast::Type {
|
pub fn ty_bool() -> ast::Type {
|
||||||
ty_path(ident_path("bool"))
|
ty_path(ident_path("bool"))
|
||||||
}
|
}
|
||||||
|
@ -160,6 +163,7 @@ pub fn assoc_item_list() -> ast::AssocItemList {
|
||||||
ast_from_text("impl C for D {}")
|
ast_from_text("impl C for D {}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: `ty_params` should be `ast::GenericArgList`
|
||||||
pub fn impl_(
|
pub fn impl_(
|
||||||
ty: ast::Path,
|
ty: ast::Path,
|
||||||
params: Option<ast::GenericParamList>,
|
params: Option<ast::GenericParamList>,
|
||||||
|
@ -185,10 +189,6 @@ pub fn impl_trait(
|
||||||
ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
|
ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generic_arg_list() -> ast::GenericArgList {
|
|
||||||
ast_from_text("const S: T<> = ();")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
|
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
|
||||||
ast_from_text(&format!("type __ = {name_ref};"))
|
ast_from_text(&format!("type __ = {name_ref};"))
|
||||||
}
|
}
|
||||||
|
@ -718,6 +718,21 @@ pub fn generic_param_list(
|
||||||
ast_from_text(&format!("fn f<{args}>() {{ }}"))
|
ast_from_text(&format!("fn f<{args}>() {{ }}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_arg(ty: ast::Type) -> ast::TypeArg {
|
||||||
|
ast_from_text(&format!("const S: T<{ty}> = ();"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
|
||||||
|
ast_from_text(&format!("const S: T<{lifetime}> = ();"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generic_arg_list(
|
||||||
|
args: impl IntoIterator<Item = ast::GenericArg>,
|
||||||
|
) -> ast::GenericArgList {
|
||||||
|
let args = args.into_iter().join(", ");
|
||||||
|
ast_from_text(&format!("const S: T<{args}> = ();"))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn visibility_pub_crate() -> ast::Visibility {
|
pub fn visibility_pub_crate() -> ast::Visibility {
|
||||||
ast_from_text("pub(crate) struct S")
|
ast_from_text("pub(crate) struct S")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue