Skip to content

Commit f9c83ed

Browse files
committed
fix: Fix move_bounds assists not working for lifetimes
1 parent cb18ead commit f9c83ed

File tree

8 files changed

+196
-47
lines changed

8 files changed

+196
-47
lines changed

crates/hir-expand/src/builtin/derive_macro.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Builtin derives.
22
3+
use either::Either;
34
use intern::sym;
45
use itertools::{Itertools, izip};
56
use parser::SyntaxKind;
@@ -1179,10 +1180,10 @@ fn coerce_pointee_expand(
11791180
};
11801181
new_predicates.push(
11811182
make::where_pred(
1182-
make::ty_path(make::path_from_segments(
1183+
Either::Right(make::ty_path(make::path_from_segments(
11831184
[make::path_segment(new_bounds_target)],
11841185
false,
1185-
)),
1186+
))),
11861187
new_bounds,
11871188
)
11881189
.clone_for_update(),
@@ -1245,7 +1246,9 @@ fn coerce_pointee_expand(
12451246
substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM)
12461247
})
12471248
});
1248-
new_predicates.push(make::where_pred(pred_target, new_bounds).clone_for_update());
1249+
new_predicates.push(
1250+
make::where_pred(Either::Right(pred_target), new_bounds).clone_for_update(),
1251+
);
12491252
}
12501253
}
12511254

@@ -1260,10 +1263,10 @@ fn coerce_pointee_expand(
12601263
// Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it.
12611264
where_clause.add_predicate(
12621265
make::where_pred(
1263-
make::ty_path(make::path_from_segments(
1266+
Either::Right(make::ty_path(make::path_from_segments(
12641267
[make::path_segment(make::name_ref(&pointee_param_name.text()))],
12651268
false,
1266-
)),
1269+
))),
12671270
[make::type_bound(make::ty_path(make::path_from_segments(
12681271
[
12691272
make::path_segment(make::name_ref("core")),

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

+40-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use either::Either;
12
use syntax::{
23
ast::{
34
self, AstNode, HasName, HasTypeBounds,
@@ -30,10 +31,11 @@ pub(crate) fn move_bounds_to_where_clause(
3031
) -> Option<()> {
3132
let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?;
3233

33-
let mut type_params = type_param_list.type_or_const_params();
34+
let mut type_params = type_param_list.generic_params();
3435
if type_params.all(|p| match p {
35-
ast::TypeOrConstParam::Type(t) => t.type_bound_list().is_none(),
36-
ast::TypeOrConstParam::Const(_) => true,
36+
ast::GenericParam::TypeParam(t) => t.type_bound_list().is_none(),
37+
ast::GenericParam::LifetimeParam(l) => l.type_bound_list().is_none(),
38+
ast::GenericParam::ConstParam(_) => true,
3739
}) {
3840
return None;
3941
}
@@ -53,20 +55,23 @@ pub(crate) fn move_bounds_to_where_clause(
5355
match parent {
5456
ast::Fn(it) => it.get_or_create_where_clause(),
5557
ast::Trait(it) => it.get_or_create_where_clause(),
58+
ast::TraitAlias(it) => it.get_or_create_where_clause(),
5659
ast::Impl(it) => it.get_or_create_where_clause(),
5760
ast::Enum(it) => it.get_or_create_where_clause(),
5861
ast::Struct(it) => it.get_or_create_where_clause(),
62+
ast::TypeAlias(it) => it.get_or_create_where_clause(),
5963
_ => return,
6064
}
6165
};
6266

63-
for toc_param in type_param_list.type_or_const_params() {
64-
let type_param = match toc_param {
65-
ast::TypeOrConstParam::Type(x) => x,
66-
ast::TypeOrConstParam::Const(_) => continue,
67+
for generic_param in type_param_list.generic_params() {
68+
let param: &dyn HasTypeBounds = match &generic_param {
69+
ast::GenericParam::TypeParam(t) => t,
70+
ast::GenericParam::LifetimeParam(l) => l,
71+
ast::GenericParam::ConstParam(_) => continue,
6772
};
68-
if let Some(tbl) = type_param.type_bound_list() {
69-
if let Some(predicate) = build_predicate(type_param) {
73+
if let Some(tbl) = param.type_bound_list() {
74+
if let Some(predicate) = build_predicate(generic_param) {
7075
where_clause.add_predicate(predicate)
7176
}
7277
tbl.remove()
@@ -76,9 +81,23 @@ pub(crate) fn move_bounds_to_where_clause(
7681
)
7782
}
7883

79-
fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
80-
let path = make::ext::ident_path(&param.name()?.syntax().to_string());
81-
let predicate = make::where_pred(make::ty_path(path), param.type_bound_list()?.bounds());
84+
fn build_predicate(param: ast::GenericParam) -> Option<ast::WherePred> {
85+
let target = match &param {
86+
ast::GenericParam::TypeParam(t) => {
87+
Either::Right(make::ty_path(make::ext::ident_path(&t.name()?.to_string())))
88+
}
89+
ast::GenericParam::LifetimeParam(l) => Either::Left(l.lifetime()?),
90+
ast::GenericParam::ConstParam(_) => return None,
91+
};
92+
let predicate = make::where_pred(
93+
target,
94+
match param {
95+
ast::GenericParam::TypeParam(t) => t.type_bound_list()?,
96+
ast::GenericParam::LifetimeParam(l) => l.type_bound_list()?,
97+
ast::GenericParam::ConstParam(_) => return None,
98+
}
99+
.bounds(),
100+
);
82101
Some(predicate.clone_for_update())
83102
}
84103

@@ -123,4 +142,13 @@ mod tests {
123142
r#"struct Pair<T>(T, T) where T: u32;"#,
124143
);
125144
}
145+
146+
#[test]
147+
fn move_bounds_to_where_clause_trait() {
148+
check_assist(
149+
move_bounds_to_where_clause,
150+
r#"trait T<'a: 'static, $0T: u32> {}"#,
151+
r#"trait T<'a, T> where 'a: 'static, T: u32 {}"#,
152+
);
153+
}
126154
}

crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs

+32-10
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,38 @@ impl server::TokenStream for RaSpanServer {
168168
}
169169

170170
bridge::TokenTree::Literal(literal) => {
171-
let literal = tt::Literal {
172-
symbol: literal.symbol,
173-
suffix: literal.suffix,
174-
span: literal.span,
175-
kind: literal_kind_to_internal(literal.kind),
176-
};
177-
178-
let leaf: tt::Leaf = tt::Leaf::from(literal);
179-
let tree = tt::TokenTree::from(leaf);
180-
TokenStream { token_trees: vec![tree] }
171+
let token_trees =
172+
if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') {
173+
let punct = tt::Punct {
174+
spacing: tt::Spacing::Alone,
175+
span: literal.span,
176+
char: '-' as char,
177+
};
178+
let leaf: tt::Leaf = tt::Leaf::from(punct);
179+
let minus_tree = tt::TokenTree::from(leaf);
180+
181+
let literal = tt::Literal {
182+
symbol: Symbol::intern(symbol),
183+
suffix: literal.suffix,
184+
span: literal.span,
185+
kind: literal_kind_to_internal(literal.kind),
186+
};
187+
let leaf: tt::Leaf = tt::Leaf::from(literal);
188+
let tree = tt::TokenTree::from(leaf);
189+
vec![minus_tree, tree]
190+
} else {
191+
let literal = tt::Literal {
192+
symbol: literal.symbol,
193+
suffix: literal.suffix,
194+
span: literal.span,
195+
kind: literal_kind_to_internal(literal.kind),
196+
};
197+
198+
let leaf: tt::Leaf = tt::Leaf::from(literal);
199+
let tree = tt::TokenTree::from(leaf);
200+
vec![tree]
201+
};
202+
TokenStream { token_trees }
181203
}
182204

183205
bridge::TokenTree::Punct(p) => {

crates/proc-macro-srv/src/server_impl/token_id.rs

+32-10
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,38 @@ impl server::TokenStream for TokenIdServer {
153153
}
154154

155155
bridge::TokenTree::Literal(literal) => {
156-
let literal = Literal {
157-
symbol: literal.symbol,
158-
suffix: literal.suffix,
159-
span: literal.span,
160-
kind: literal_kind_to_internal(literal.kind),
161-
};
162-
163-
let leaf = tt::Leaf::from(literal);
164-
let tree = TokenTree::from(leaf);
165-
TokenStream { token_trees: vec![tree] }
156+
let token_trees =
157+
if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') {
158+
let punct = tt::Punct {
159+
spacing: tt::Spacing::Alone,
160+
span: literal.span,
161+
char: '-' as char,
162+
};
163+
let leaf: tt::Leaf = tt::Leaf::from(punct);
164+
let minus_tree = tt::TokenTree::from(leaf);
165+
166+
let literal = Literal {
167+
symbol: Symbol::intern(symbol),
168+
suffix: literal.suffix,
169+
span: literal.span,
170+
kind: literal_kind_to_internal(literal.kind),
171+
};
172+
let leaf: tt::Leaf = tt::Leaf::from(literal);
173+
let tree = tt::TokenTree::from(leaf);
174+
vec![minus_tree, tree]
175+
} else {
176+
let literal = Literal {
177+
symbol: literal.symbol,
178+
suffix: literal.suffix,
179+
span: literal.span,
180+
kind: literal_kind_to_internal(literal.kind),
181+
};
182+
183+
let leaf: tt::Leaf = tt::Leaf::from(literal);
184+
let tree = tt::TokenTree::from(leaf);
185+
vec![tree]
186+
};
187+
TokenStream { token_trees }
166188
}
167189

168190
bridge::TokenTree::Punct(p) => {

crates/proc-macro-srv/src/server_impl/token_stream.rs

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ impl<S: Copy> TokenStream<S> {
6868
span: ident.span,
6969
}))
7070
}
71+
// Note, we do not have to assemble our `-` punct and literal split into a single
72+
// negative bridge literal here. As the proc-macro docs state
73+
// > Literals created from negative numbers might not survive round-trips through
74+
// > TokenStream or strings and may be broken into two tokens (- and positive
75+
// > literal).
7176
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
7277
result.push(bridge::TokenTree::Literal(bridge::Literal {
7378
span: lit.span,

crates/proc-macro-srv/src/tests/mod.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,17 @@ fn test_fn_like_mk_literals() {
248248
LITERAL Str string 1
249249
LITERAL CStr cstring 1
250250
LITERAL Float 3.14f64 1
251-
LITERAL Float -3.14f64 1
251+
PUNCH - [alone] 1
252+
LITERAL Float 3.14f64 1
253+
LITERAL Float 3.14 1
254+
PUNCH - [alone] 1
252255
LITERAL Float 3.14 1
253-
LITERAL Float -3.14 1
254256
LITERAL Integer 123i64 1
255-
LITERAL Integer -123i64 1
257+
PUNCH - [alone] 1
258+
LITERAL Integer 123i64 1
256259
LITERAL Integer 123 1
257-
LITERAL Integer -123 1"#]],
260+
PUNCH - [alone] 1
261+
LITERAL Integer 123 1"#]],
258262
expect![[r#"
259263
SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
260264
@@ -266,13 +270,17 @@ fn test_fn_like_mk_literals() {
266270
LITERAL Str string 42:[email protected]#ROOT2024
267271
LITERAL CStr cstring 42:[email protected]#ROOT2024
268272
LITERAL Float 3.14f64 42:[email protected]#ROOT2024
269-
LITERAL Float -3.14f64 42:[email protected]#ROOT2024
273+
PUNCH - [alone] 42:[email protected]#ROOT2024
274+
LITERAL Float 3.14f64 42:[email protected]#ROOT2024
275+
LITERAL Float 3.14 42:[email protected]#ROOT2024
276+
PUNCH - [alone] 42:[email protected]#ROOT2024
270277
LITERAL Float 3.14 42:[email protected]#ROOT2024
271-
LITERAL Float -3.14 42:[email protected]#ROOT2024
272278
LITERAL Integer 123i64 42:[email protected]#ROOT2024
273-
LITERAL Integer -123i64 42:[email protected]#ROOT2024
279+
PUNCH - [alone] 42:[email protected]#ROOT2024
280+
LITERAL Integer 123i64 42:[email protected]#ROOT2024
274281
LITERAL Integer 123 42:[email protected]#ROOT2024
275-
LITERAL Integer -123 42:[email protected]#ROOT2024"#]],
282+
PUNCH - [alone] 42:[email protected]#ROOT2024
283+
LITERAL Integer 123 42:[email protected]#ROOT2024"#]],
276284
);
277285
}
278286

@@ -400,7 +408,6 @@ fn test_fn_like_macro_clone_literals() {
400408
);
401409
}
402410

403-
404411
#[test]
405412
fn test_fn_like_macro_negative_literals() {
406413
assert_expand(

crates/syntax/src/ast/edit_in_place.rs

+61
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,67 @@ impl GenericParamsOwnerEdit for ast::Trait {
109109
}
110110
}
111111

112+
impl GenericParamsOwnerEdit for ast::TraitAlias {
113+
fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
114+
match self.generic_param_list() {
115+
Some(it) => it,
116+
None => {
117+
let position = if let Some(name) = self.name() {
118+
Position::after(name.syntax)
119+
} else if let Some(trait_token) = self.trait_token() {
120+
Position::after(trait_token)
121+
} else {
122+
Position::last_child_of(self.syntax())
123+
};
124+
create_generic_param_list(position)
125+
}
126+
}
127+
}
128+
129+
fn get_or_create_where_clause(&self) -> ast::WhereClause {
130+
if self.where_clause().is_none() {
131+
let position = match self.semicolon_token() {
132+
Some(tok) => Position::before(tok),
133+
None => Position::last_child_of(self.syntax()),
134+
};
135+
create_where_clause(position);
136+
}
137+
self.where_clause().unwrap()
138+
}
139+
}
140+
141+
impl GenericParamsOwnerEdit for ast::TypeAlias {
142+
fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
143+
match self.generic_param_list() {
144+
Some(it) => it,
145+
None => {
146+
let position = if let Some(name) = self.name() {
147+
Position::after(name.syntax)
148+
} else if let Some(trait_token) = self.type_token() {
149+
Position::after(trait_token)
150+
} else {
151+
Position::last_child_of(self.syntax())
152+
};
153+
create_generic_param_list(position)
154+
}
155+
}
156+
}
157+
158+
fn get_or_create_where_clause(&self) -> ast::WhereClause {
159+
if self.where_clause().is_none() {
160+
let position = match self.eq_token() {
161+
Some(tok) => Position::before(tok),
162+
None => match self.semicolon_token() {
163+
Some(tok) => Position::before(tok),
164+
None => Position::last_child_of(self.syntax()),
165+
},
166+
};
167+
create_where_clause(position);
168+
}
169+
self.where_clause().unwrap()
170+
}
171+
}
172+
112173
impl GenericParamsOwnerEdit for ast::Struct {
113174
fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
114175
match self.generic_param_list() {

crates/syntax/src/ast/make.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1414
mod quote;
1515

16+
use either::Either;
1617
use itertools::Itertools;
1718
use parser::{Edition, T};
1819
use rowan::NodeOrToken;
@@ -881,7 +882,7 @@ pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::Mat
881882
}
882883

883884
pub fn where_pred(
884-
path: ast::Type,
885+
path: Either<ast::Lifetime, ast::Type>,
885886
bounds: impl IntoIterator<Item = ast::TypeBound>,
886887
) -> ast::WherePred {
887888
let bounds = bounds.into_iter().join(" + ");

0 commit comments

Comments
 (0)