Unnamed repository; edit this file 'description' to name the repository.
fix: Fix `move_bounds` assists not working for lifetimes
Lukas Wirth 12 months ago
parent cb18ead · commit f9c83ed
-rw-r--r--crates/hir-expand/src/builtin/derive_macro.rs13
-rw-r--r--crates/ide-assists/src/handlers/move_bounds.rs52
-rw-r--r--crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs42
-rw-r--r--crates/proc-macro-srv/src/server_impl/token_id.rs42
-rw-r--r--crates/proc-macro-srv/src/server_impl/token_stream.rs5
-rw-r--r--crates/proc-macro-srv/src/tests/mod.rs25
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs61
-rw-r--r--crates/syntax/src/ast/make.rs3
8 files changed, 196 insertions, 47 deletions
diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs
index 68283b916d..d135584a08 100644
--- a/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/crates/hir-expand/src/builtin/derive_macro.rs
@@ -1,5 +1,6 @@
//! Builtin derives.
+use either::Either;
use intern::sym;
use itertools::{Itertools, izip};
use parser::SyntaxKind;
@@ -1179,10 +1180,10 @@ fn coerce_pointee_expand(
};
new_predicates.push(
make::where_pred(
- make::ty_path(make::path_from_segments(
+ Either::Right(make::ty_path(make::path_from_segments(
[make::path_segment(new_bounds_target)],
false,
- )),
+ ))),
new_bounds,
)
.clone_for_update(),
@@ -1245,7 +1246,9 @@ fn coerce_pointee_expand(
substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM)
})
});
- new_predicates.push(make::where_pred(pred_target, new_bounds).clone_for_update());
+ new_predicates.push(
+ make::where_pred(Either::Right(pred_target), new_bounds).clone_for_update(),
+ );
}
}
@@ -1260,10 +1263,10 @@ fn coerce_pointee_expand(
// Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it.
where_clause.add_predicate(
make::where_pred(
- make::ty_path(make::path_from_segments(
+ Either::Right(make::ty_path(make::path_from_segments(
[make::path_segment(make::name_ref(&pointee_param_name.text()))],
false,
- )),
+ ))),
[make::type_bound(make::ty_path(make::path_from_segments(
[
make::path_segment(make::name_ref("core")),
diff --git a/crates/ide-assists/src/handlers/move_bounds.rs b/crates/ide-assists/src/handlers/move_bounds.rs
index 7e8735bd7a..a9df6f6fc3 100644
--- a/crates/ide-assists/src/handlers/move_bounds.rs
+++ b/crates/ide-assists/src/handlers/move_bounds.rs
@@ -1,3 +1,4 @@
+use either::Either;
use syntax::{
ast::{
self, AstNode, HasName, HasTypeBounds,
@@ -30,10 +31,11 @@ pub(crate) fn move_bounds_to_where_clause(
) -> Option<()> {
let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?;
- let mut type_params = type_param_list.type_or_const_params();
+ let mut type_params = type_param_list.generic_params();
if type_params.all(|p| match p {
- ast::TypeOrConstParam::Type(t) => t.type_bound_list().is_none(),
- ast::TypeOrConstParam::Const(_) => true,
+ ast::GenericParam::TypeParam(t) => t.type_bound_list().is_none(),
+ ast::GenericParam::LifetimeParam(l) => l.type_bound_list().is_none(),
+ ast::GenericParam::ConstParam(_) => true,
}) {
return None;
}
@@ -53,20 +55,23 @@ pub(crate) fn move_bounds_to_where_clause(
match parent {
ast::Fn(it) => it.get_or_create_where_clause(),
ast::Trait(it) => it.get_or_create_where_clause(),
+ ast::TraitAlias(it) => it.get_or_create_where_clause(),
ast::Impl(it) => it.get_or_create_where_clause(),
ast::Enum(it) => it.get_or_create_where_clause(),
ast::Struct(it) => it.get_or_create_where_clause(),
+ ast::TypeAlias(it) => it.get_or_create_where_clause(),
_ => return,
}
};
- for toc_param in type_param_list.type_or_const_params() {
- let type_param = match toc_param {
- ast::TypeOrConstParam::Type(x) => x,
- ast::TypeOrConstParam::Const(_) => continue,
+ for generic_param in type_param_list.generic_params() {
+ let param: &dyn HasTypeBounds = match &generic_param {
+ ast::GenericParam::TypeParam(t) => t,
+ ast::GenericParam::LifetimeParam(l) => l,
+ ast::GenericParam::ConstParam(_) => continue,
};
- if let Some(tbl) = type_param.type_bound_list() {
- if let Some(predicate) = build_predicate(type_param) {
+ if let Some(tbl) = param.type_bound_list() {
+ if let Some(predicate) = build_predicate(generic_param) {
where_clause.add_predicate(predicate)
}
tbl.remove()
@@ -76,9 +81,23 @@ pub(crate) fn move_bounds_to_where_clause(
)
}
-fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
- let path = make::ext::ident_path(&param.name()?.syntax().to_string());
- let predicate = make::where_pred(make::ty_path(path), param.type_bound_list()?.bounds());
+fn build_predicate(param: ast::GenericParam) -> Option<ast::WherePred> {
+ let target = match &param {
+ ast::GenericParam::TypeParam(t) => {
+ Either::Right(make::ty_path(make::ext::ident_path(&t.name()?.to_string())))
+ }
+ ast::GenericParam::LifetimeParam(l) => Either::Left(l.lifetime()?),
+ ast::GenericParam::ConstParam(_) => return None,
+ };
+ let predicate = make::where_pred(
+ target,
+ match param {
+ ast::GenericParam::TypeParam(t) => t.type_bound_list()?,
+ ast::GenericParam::LifetimeParam(l) => l.type_bound_list()?,
+ ast::GenericParam::ConstParam(_) => return None,
+ }
+ .bounds(),
+ );
Some(predicate.clone_for_update())
}
@@ -123,4 +142,13 @@ mod tests {
r#"struct Pair<T>(T, T) where T: u32;"#,
);
}
+
+ #[test]
+ fn move_bounds_to_where_clause_trait() {
+ check_assist(
+ move_bounds_to_where_clause,
+ r#"trait T<'a: 'static, $0T: u32> {}"#,
+ r#"trait T<'a, T> where 'a: 'static, T: u32 {}"#,
+ );
+ }
}
diff --git a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index 47555a5db2..64b40e7b94 100644
--- a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -168,16 +168,38 @@ impl server::TokenStream for RaSpanServer {
}
bridge::TokenTree::Literal(literal) => {
- let literal = tt::Literal {
- symbol: literal.symbol,
- suffix: literal.suffix,
- span: literal.span,
- kind: literal_kind_to_internal(literal.kind),
- };
-
- let leaf: tt::Leaf = tt::Leaf::from(literal);
- let tree = tt::TokenTree::from(leaf);
- TokenStream { token_trees: vec![tree] }
+ let token_trees =
+ if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') {
+ let punct = tt::Punct {
+ spacing: tt::Spacing::Alone,
+ span: literal.span,
+ char: '-' as char,
+ };
+ let leaf: tt::Leaf = tt::Leaf::from(punct);
+ let minus_tree = tt::TokenTree::from(leaf);
+
+ let literal = tt::Literal {
+ symbol: Symbol::intern(symbol),
+ suffix: literal.suffix,
+ span: literal.span,
+ kind: literal_kind_to_internal(literal.kind),
+ };
+ let leaf: tt::Leaf = tt::Leaf::from(literal);
+ let tree = tt::TokenTree::from(leaf);
+ vec![minus_tree, tree]
+ } else {
+ let literal = tt::Literal {
+ symbol: literal.symbol,
+ suffix: literal.suffix,
+ span: literal.span,
+ kind: literal_kind_to_internal(literal.kind),
+ };
+
+ let leaf: tt::Leaf = tt::Leaf::from(literal);
+ let tree = tt::TokenTree::from(leaf);
+ vec![tree]
+ };
+ TokenStream { token_trees }
}
bridge::TokenTree::Punct(p) => {
diff --git a/crates/proc-macro-srv/src/server_impl/token_id.rs b/crates/proc-macro-srv/src/server_impl/token_id.rs
index c002be4be6..24a67bf45c 100644
--- a/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -153,16 +153,38 @@ impl server::TokenStream for TokenIdServer {
}
bridge::TokenTree::Literal(literal) => {
- let literal = Literal {
- symbol: literal.symbol,
- suffix: literal.suffix,
- span: literal.span,
- kind: literal_kind_to_internal(literal.kind),
- };
-
- let leaf = tt::Leaf::from(literal);
- let tree = TokenTree::from(leaf);
- TokenStream { token_trees: vec![tree] }
+ let token_trees =
+ if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') {
+ let punct = tt::Punct {
+ spacing: tt::Spacing::Alone,
+ span: literal.span,
+ char: '-' as char,
+ };
+ let leaf: tt::Leaf = tt::Leaf::from(punct);
+ let minus_tree = tt::TokenTree::from(leaf);
+
+ let literal = Literal {
+ symbol: Symbol::intern(symbol),
+ suffix: literal.suffix,
+ span: literal.span,
+ kind: literal_kind_to_internal(literal.kind),
+ };
+ let leaf: tt::Leaf = tt::Leaf::from(literal);
+ let tree = tt::TokenTree::from(leaf);
+ vec![minus_tree, tree]
+ } else {
+ let literal = Literal {
+ symbol: literal.symbol,
+ suffix: literal.suffix,
+ span: literal.span,
+ kind: literal_kind_to_internal(literal.kind),
+ };
+
+ let leaf: tt::Leaf = tt::Leaf::from(literal);
+ let tree = tt::TokenTree::from(leaf);
+ vec![tree]
+ };
+ TokenStream { token_trees }
}
bridge::TokenTree::Punct(p) => {
diff --git a/crates/proc-macro-srv/src/server_impl/token_stream.rs b/crates/proc-macro-srv/src/server_impl/token_stream.rs
index 4946a4f2a6..072557913c 100644
--- a/crates/proc-macro-srv/src/server_impl/token_stream.rs
+++ b/crates/proc-macro-srv/src/server_impl/token_stream.rs
@@ -68,6 +68,11 @@ impl<S: Copy> TokenStream<S> {
span: ident.span,
}))
}
+ // Note, we do not have to assemble our `-` punct and literal split into a single
+ // negative bridge literal here. As the proc-macro docs state
+ // > Literals created from negative numbers might not survive round-trips through
+ // > TokenStream or strings and may be broken into two tokens (- and positive
+ // > literal).
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
result.push(bridge::TokenTree::Literal(bridge::Literal {
span: lit.span,
diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs
index 7a0ac01c50..36cd675b9a 100644
--- a/crates/proc-macro-srv/src/tests/mod.rs
+++ b/crates/proc-macro-srv/src/tests/mod.rs
@@ -248,13 +248,17 @@ fn test_fn_like_mk_literals() {
LITERAL Str string 1
LITERAL CStr cstring 1
LITERAL Float 3.14f64 1
- LITERAL Float -3.14f64 1
+ PUNCH - [alone] 1
+ LITERAL Float 3.14f64 1
+ LITERAL Float 3.14 1
+ PUNCH - [alone] 1
LITERAL Float 3.14 1
- LITERAL Float -3.14 1
LITERAL Integer 123i64 1
- LITERAL Integer -123i64 1
+ PUNCH - [alone] 1
+ LITERAL Integer 123i64 1
LITERAL Integer 123 1
- LITERAL Integer -123 1"#]],
+ PUNCH - [alone] 1
+ LITERAL Integer 123 1"#]],
expect![[r#"
SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
@@ -266,13 +270,17 @@ fn test_fn_like_mk_literals() {
LITERAL Str string 42:[email protected]#ROOT2024
LITERAL CStr cstring 42:[email protected]#ROOT2024
LITERAL Float 3.14f64 42:[email protected]#ROOT2024
- LITERAL Float -3.14f64 42:[email protected]#ROOT2024
+ PUNCH - [alone] 42:[email protected]#ROOT2024
+ LITERAL Float 3.14f64 42:[email protected]#ROOT2024
+ LITERAL Float 3.14 42:[email protected]#ROOT2024
+ PUNCH - [alone] 42:[email protected]#ROOT2024
LITERAL Float 3.14 42:[email protected]#ROOT2024
- LITERAL Float -3.14 42:[email protected]#ROOT2024
LITERAL Integer 123i64 42:[email protected]#ROOT2024
- LITERAL Integer -123i64 42:[email protected]#ROOT2024
+ PUNCH - [alone] 42:[email protected]#ROOT2024
+ LITERAL Integer 123i64 42:[email protected]#ROOT2024
LITERAL Integer 123 42:[email protected]#ROOT2024
- LITERAL Integer -123 42:[email protected]#ROOT2024"#]],
+ PUNCH - [alone] 42:[email protected]#ROOT2024
+ LITERAL Integer 123 42:[email protected]#ROOT2024"#]],
);
}
@@ -400,7 +408,6 @@ fn test_fn_like_macro_clone_literals() {
);
}
-
#[test]
fn test_fn_like_macro_negative_literals() {
assert_expand(
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index da0bfd4f37..e60243f2c9 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -109,6 +109,67 @@ impl GenericParamsOwnerEdit for ast::Trait {
}
}
+impl GenericParamsOwnerEdit for ast::TraitAlias {
+ fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
+ match self.generic_param_list() {
+ Some(it) => it,
+ None => {
+ let position = if let Some(name) = self.name() {
+ Position::after(name.syntax)
+ } else if let Some(trait_token) = self.trait_token() {
+ Position::after(trait_token)
+ } else {
+ Position::last_child_of(self.syntax())
+ };
+ create_generic_param_list(position)
+ }
+ }
+ }
+
+ fn get_or_create_where_clause(&self) -> ast::WhereClause {
+ if self.where_clause().is_none() {
+ let position = match self.semicolon_token() {
+ Some(tok) => Position::before(tok),
+ None => Position::last_child_of(self.syntax()),
+ };
+ create_where_clause(position);
+ }
+ self.where_clause().unwrap()
+ }
+}
+
+impl GenericParamsOwnerEdit for ast::TypeAlias {
+ fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
+ match self.generic_param_list() {
+ Some(it) => it,
+ None => {
+ let position = if let Some(name) = self.name() {
+ Position::after(name.syntax)
+ } else if let Some(trait_token) = self.type_token() {
+ Position::after(trait_token)
+ } else {
+ Position::last_child_of(self.syntax())
+ };
+ create_generic_param_list(position)
+ }
+ }
+ }
+
+ fn get_or_create_where_clause(&self) -> ast::WhereClause {
+ if self.where_clause().is_none() {
+ let position = match self.eq_token() {
+ Some(tok) => Position::before(tok),
+ None => match self.semicolon_token() {
+ Some(tok) => Position::before(tok),
+ None => Position::last_child_of(self.syntax()),
+ },
+ };
+ create_where_clause(position);
+ }
+ self.where_clause().unwrap()
+ }
+}
+
impl GenericParamsOwnerEdit for ast::Struct {
fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
match self.generic_param_list() {
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 596f73e0b1..fab4cb287c 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -13,6 +13,7 @@
mod quote;
+use either::Either;
use itertools::Itertools;
use parser::{Edition, T};
use rowan::NodeOrToken;
@@ -881,7 +882,7 @@ pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::Mat
}
pub fn where_pred(
- path: ast::Type,
+ path: Either<ast::Lifetime, ast::Type>,
bounds: impl IntoIterator<Item = ast::TypeBound>,
) -> ast::WherePred {
let bounds = bounds.into_iter().join(" + ");