aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/extract_assignment.rs325
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs29
-rw-r--r--crates/assists/src/utils.rs2
-rw-r--r--crates/hir/src/attrs.rs28
-rw-r--r--crates/hir/src/from_id.rs28
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/hir_def/src/attr.rs36
-rw-r--r--crates/hir_def/src/lib.rs22
-rw-r--r--crates/hir_def/src/path/lower.rs2
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/hygiene.rs151
-rw-r--r--crates/hir_expand/src/lib.rs11
-rw-r--r--crates/hir_ty/src/tests/macros.rs31
-rw-r--r--crates/hir_ty/src/tests/regression.rs18
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs2
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs20
-rw-r--r--crates/mbe/src/parser.rs11
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/syntax/src/ast/edit.rs4
20 files changed, 648 insertions, 83 deletions
diff --git a/crates/assists/src/handlers/extract_assignment.rs b/crates/assists/src/handlers/extract_assignment.rs
new file mode 100644
index 000000000..281cf5d24
--- /dev/null
+++ b/crates/assists/src/handlers/extract_assignment.rs
@@ -0,0 +1,325 @@
1use hir::AsName;
2use syntax::{
3 ast::{self, edit::AstNodeEdit, make},
4 AstNode,
5};
6use test_utils::mark;
7
8use crate::{
9 assist_context::{AssistContext, Assists},
10 AssistId, AssistKind,
11};
12
13// Assist: extract_assignment
14//
15// Extracts variable assigment to outside an if or match statement.
16//
17// ```
18// fn main() {
19// let mut foo = 6;
20//
21// if true {
22// <|>foo = 5;
23// } else {
24// foo = 4;
25// }
26// }
27// ```
28// ->
29// ```
30// fn main() {
31// let mut foo = 6;
32//
33// foo = if true {
34// 5
35// } else {
36// 4
37// };
38// }
39// ```
40pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 let name = ctx.find_node_at_offset::<ast::NameRef>()?.as_name();
42
43 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
44 (
45 ast::Expr::cast(if_expr.syntax().to_owned())?,
46 exprify_if(&if_expr, &name)?.indent(if_expr.indent_level()),
47 )
48 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
49 (ast::Expr::cast(match_expr.syntax().to_owned())?, exprify_match(&match_expr, &name)?)
50 } else {
51 return None;
52 };
53
54 let expr_stmt = make::expr_stmt(new_stmt);
55
56 acc.add(
57 AssistId("extract_assignment", AssistKind::RefactorExtract),
58 "Extract assignment",
59 old_stmt.syntax().text_range(),
60 move |edit| {
61 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name, expr_stmt));
62 },
63 )
64}
65
66fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::Expr> {
67 let new_arm_list = match_expr
68 .match_arm_list()?
69 .arms()
70 .map(|arm| {
71 if let ast::Expr::BlockExpr(block) = arm.expr()? {
72 let new_block = exprify_block(&block, name)?.indent(block.indent_level());
73 Some(arm.replace_descendant(block, new_block))
74 } else {
75 None
76 }
77 })
78 .collect::<Option<Vec<_>>>()?;
79 let new_arm_list = match_expr
80 .match_arm_list()?
81 .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list));
82 Some(make::expr_match(match_expr.expr()?, new_arm_list))
83}
84
85fn exprify_if(statement: &ast::IfExpr, name: &hir::Name) -> Option<ast::Expr> {
86 let then_branch = exprify_block(&statement.then_branch()?, name)?;
87 let else_branch = match statement.else_branch()? {
88 ast::ElseBranch::Block(ref block) => ast::ElseBranch::Block(exprify_block(block, name)?),
89 ast::ElseBranch::IfExpr(expr) => {
90 mark::hit!(test_extract_assigment_chained_if);
91 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
92 exprify_if(&expr, name)?.syntax().to_owned(),
93 )?)
94 }
95 };
96 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch)))
97}
98
99fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockExpr> {
100 if block.expr().is_some() {
101 return None;
102 }
103
104 let mut stmts: Vec<_> = block.statements().collect();
105 let stmt = stmts.pop()?;
106
107 if let ast::Stmt::ExprStmt(stmt) = stmt {
108 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
109 if expr.op_kind()? == ast::BinOp::Assignment
110 && &expr.lhs()?.name_ref()?.as_name() == name
111 {
112 // The last statement in the block is an assignment to the name we want
113 return Some(make::block_expr(stmts, Some(expr.rhs()?)));
114 }
115 }
116 }
117 None
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 use crate::tests::{check_assist, check_assist_not_applicable};
125
126 #[test]
127 fn test_extract_assignment_if() {
128 check_assist(
129 extract_assigment,
130 r#"
131fn foo() {
132 let mut a = 1;
133
134 if true {
135 <|>a = 2;
136 } else {
137 a = 3;
138 }
139}"#,
140 r#"
141fn foo() {
142 let mut a = 1;
143
144 a = if true {
145 2
146 } else {
147 3
148 };
149}"#,
150 );
151 }
152
153 #[test]
154 fn test_extract_assignment_match() {
155 check_assist(
156 extract_assigment,
157 r#"
158fn foo() {
159 let mut a = 1;
160
161 match 1 {
162 1 => {
163 <|>a = 2;
164 },
165 2 => {
166 a = 3;
167 },
168 3 => {
169 a = 4;
170 }
171 }
172}"#,
173 r#"
174fn foo() {
175 let mut a = 1;
176
177 a = match 1 {
178 1 => {
179 2
180 },
181 2 => {
182 3
183 },
184 3 => {
185 4
186 }
187 };
188}"#,
189 );
190 }
191
192 #[test]
193 fn test_extract_assignment_not_last_not_applicable() {
194 check_assist_not_applicable(
195 extract_assigment,
196 r#"
197fn foo() {
198 let mut a = 1;
199
200 if true {
201 <|>a = 2;
202 b = a;
203 } else {
204 a = 3;
205 }
206}"#,
207 )
208 }
209
210 #[test]
211 fn test_extract_assignment_chained_if() {
212 mark::check!(test_extract_assigment_chained_if);
213 check_assist(
214 extract_assigment,
215 r#"
216fn foo() {
217 let mut a = 1;
218
219 if true {
220 <|>a = 2;
221 } else if false {
222 a = 3;
223 } else {
224 a = 4;
225 }
226}"#,
227 r#"
228fn foo() {
229 let mut a = 1;
230
231 a = if true {
232 2
233 } else if false {
234 3
235 } else {
236 4
237 };
238}"#,
239 );
240 }
241
242 #[test]
243 fn test_extract_assigment_retains_stmts() {
244 check_assist(
245 extract_assigment,
246 r#"
247fn foo() {
248 let mut a = 1;
249
250 if true {
251 let b = 2;
252 <|>a = 2;
253 } else {
254 let b = 3;
255 a = 3;
256 }
257}"#,
258 r#"
259fn foo() {
260 let mut a = 1;
261
262 a = if true {
263 let b = 2;
264 2
265 } else {
266 let b = 3;
267 3
268 };
269}"#,
270 )
271 }
272
273 #[test]
274 fn extract_assignment_let_stmt_not_applicable() {
275 check_assist_not_applicable(
276 extract_assigment,
277 r#"
278fn foo() {
279 let mut a = 1;
280
281 let b = if true {
282 <|>a = 2
283 } else {
284 a = 3
285 };
286}"#,
287 )
288 }
289
290 #[test]
291 fn extract_assignment_if_missing_assigment_not_applicable() {
292 check_assist_not_applicable(
293 extract_assigment,
294 r#"
295fn foo() {
296 let mut a = 1;
297
298 if true {
299 <|>a = 2;
300 } else {}
301}"#,
302 )
303 }
304
305 #[test]
306 fn extract_assignment_match_missing_assigment_not_applicable() {
307 check_assist_not_applicable(
308 extract_assigment,
309 r#"
310fn foo() {
311 let mut a = 1;
312
313 match 1 {
314 1 => {
315 <|>a = 2;
316 },
317 2 => {
318 a = 3;
319 },
320 3 => {},
321 }
322}"#,
323 )
324 }
325}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index fdec886e9..212464f85 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -116,6 +116,7 @@ mod handlers {
116 mod convert_integer_literal; 116 mod convert_integer_literal;
117 mod early_return; 117 mod early_return;
118 mod expand_glob_import; 118 mod expand_glob_import;
119 mod extract_assignment;
119 mod extract_module_to_file; 120 mod extract_module_to_file;
120 mod extract_struct_from_enum_variant; 121 mod extract_struct_from_enum_variant;
121 mod extract_variable; 122 mod extract_variable;
@@ -167,6 +168,7 @@ mod handlers {
167 convert_integer_literal::convert_integer_literal, 168 convert_integer_literal::convert_integer_literal,
168 early_return::convert_to_guarded_return, 169 early_return::convert_to_guarded_return,
169 expand_glob_import::expand_glob_import, 170 expand_glob_import::expand_glob_import,
171 extract_assignment::extract_assigment,
170 extract_module_to_file::extract_module_to_file, 172 extract_module_to_file::extract_module_to_file,
171 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 173 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
172 extract_variable::extract_variable, 174 extract_variable::extract_variable,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index d3dfe24e7..b91a816e8 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -238,6 +238,35 @@ fn qux(bar: Bar, baz: Baz) {}
238} 238}
239 239
240#[test] 240#[test]
241fn doctest_extract_assignment() {
242 check_doc_test(
243 "extract_assignment",
244 r#####"
245fn main() {
246 let mut foo = 6;
247
248 if true {
249 <|>foo = 5;
250 } else {
251 foo = 4;
252 }
253}
254"#####,
255 r#####"
256fn main() {
257 let mut foo = 6;
258
259 foo = if true {
260 5
261 } else {
262 4
263 };
264}
265"#####,
266 )
267}
268
269#[test]
241fn doctest_extract_module_to_file() { 270fn doctest_extract_module_to_file() {
242 check_doc_test( 271 check_doc_test(
243 "extract_module_to_file", 272 "extract_module_to_file",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 7c159b5ba..b05596446 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -94,7 +94,7 @@ pub fn filter_assoc_items(
94 ast::AssocItem::MacroCall(_) => None, 94 ast::AssocItem::MacroCall(_) => None,
95 } 95 }
96 .is_some() 96 .is_some()
97 }; 97 }
98 98
99 items 99 items
100 .iter() 100 .iter()
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index d32ce37ed..99fb65bac 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -3,15 +3,15 @@ use hir_def::{
3 attr::{Attrs, Documentation}, 3 attr::{Attrs, Documentation},
4 path::ModPath, 4 path::ModPath,
5 resolver::HasResolver, 5 resolver::HasResolver,
6 AttrDefId, ModuleDefId, 6 AttrDefId, GenericParamId, ModuleDefId,
7}; 7};
8use hir_expand::hygiene::Hygiene; 8use hir_expand::hygiene::Hygiene;
9use hir_ty::db::HirDatabase; 9use hir_ty::db::HirDatabase;
10use syntax::ast; 10use syntax::ast;
11 11
12use crate::{ 12use crate::{
13 Adt, Const, Enum, Field, Function, MacroDef, Module, ModuleDef, Static, Struct, Trait, 13 Adt, Const, ConstParam, Enum, Field, Function, GenericParam, LifetimeParam, MacroDef, Module,
14 TypeAlias, Union, Variant, 14 ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
15}; 15};
16 16
17pub trait HasAttrs { 17pub trait HasAttrs {
@@ -62,25 +62,27 @@ impl_has_attrs![
62 (Function, FunctionId), 62 (Function, FunctionId),
63 (Adt, AdtId), 63 (Adt, AdtId),
64 (Module, ModuleId), 64 (Module, ModuleId),
65 (GenericParam, GenericParamId),
65]; 66];
66 67
67macro_rules! impl_has_attrs_adt { 68macro_rules! impl_has_attrs_enum {
68 ($($adt:ident),*) => {$( 69 ($($variant:ident),* for $enum:ident) => {$(
69 impl HasAttrs for $adt { 70 impl HasAttrs for $variant {
70 fn attrs(self, db: &dyn HirDatabase) -> Attrs { 71 fn attrs(self, db: &dyn HirDatabase) -> Attrs {
71 Adt::$adt(self).attrs(db) 72 $enum::$variant(self).attrs(db)
72 } 73 }
73 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { 74 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
74 Adt::$adt(self).docs(db) 75 $enum::$variant(self).docs(db)
75 } 76 }
76 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> { 77 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
77 Adt::$adt(self).resolve_doc_path(db, link, ns) 78 $enum::$variant(self).resolve_doc_path(db, link, ns)
78 } 79 }
79 } 80 }
80 )*}; 81 )*};
81} 82}
82 83
83impl_has_attrs_adt![Struct, Union, Enum]; 84impl_has_attrs_enum![Struct, Union, Enum for Adt];
85impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
84 86
85fn resolve_doc_path( 87fn resolve_doc_path(
86 db: &dyn HirDatabase, 88 db: &dyn HirDatabase,
@@ -99,6 +101,12 @@ fn resolve_doc_path(
99 AttrDefId::TraitId(it) => it.resolver(db.upcast()), 101 AttrDefId::TraitId(it) => it.resolver(db.upcast()),
100 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()), 102 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()),
101 AttrDefId::ImplId(it) => it.resolver(db.upcast()), 103 AttrDefId::ImplId(it) => it.resolver(db.upcast()),
104 AttrDefId::GenericParamId(it) => match it {
105 GenericParamId::TypeParamId(it) => it.parent,
106 GenericParamId::LifetimeParamId(it) => it.parent,
107 GenericParamId::ConstParamId(it) => it.parent,
108 }
109 .resolver(db.upcast()),
102 AttrDefId::MacroDefId(_) => return None, 110 AttrDefId::MacroDefId(_) => return None,
103 }; 111 };
104 let path = ast::Path::parse(link).ok()?; 112 let path = ast::Path::parse(link).ok()?;
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index 2422887e3..3e47a5e9d 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -6,13 +6,13 @@
6use hir_def::{ 6use hir_def::{
7 expr::{LabelId, PatId}, 7 expr::{LabelId, PatId},
8 item_scope::ItemInNs, 8 item_scope::ItemInNs,
9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, ModuleDefId, 9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
10 VariantId, 10 ModuleDefId, VariantId,
11}; 11};
12 12
13use crate::{ 13use crate::{
14 Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, 14 code_model::GenericParam, Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local,
15 VariantDef, 15 MacroDef, ModuleDef, Variant, VariantDef,
16}; 16};
17 17
18macro_rules! from_id { 18macro_rules! from_id {
@@ -68,6 +68,26 @@ impl From<Adt> for AdtId {
68 } 68 }
69} 69}
70 70
71impl From<GenericParamId> for GenericParam {
72 fn from(id: GenericParamId) -> Self {
73 match id {
74 GenericParamId::TypeParamId(it) => GenericParam::TypeParam(it.into()),
75 GenericParamId::LifetimeParamId(it) => GenericParam::LifetimeParam(it.into()),
76 GenericParamId::ConstParamId(it) => GenericParam::ConstParam(it.into()),
77 }
78 }
79}
80
81impl From<GenericParam> for GenericParamId {
82 fn from(id: GenericParam) -> Self {
83 match id {
84 GenericParam::TypeParam(it) => GenericParamId::TypeParamId(it.id),
85 GenericParam::LifetimeParam(it) => GenericParamId::LifetimeParamId(it.id),
86 GenericParam::ConstParam(it) => GenericParamId::ConstParamId(it.id),
87 }
88 }
89}
90
71impl From<EnumVariantId> for Variant { 91impl From<EnumVariantId> for Variant {
72 fn from(id: EnumVariantId) -> Self { 92 fn from(id: EnumVariantId) -> Self {
73 Variant { parent: id.parent.into(), id: id.local_id } 93 Variant { parent: id.parent.into(), id: id.local_id }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 8ac27e2dd..769945c47 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -35,8 +35,9 @@ pub use crate::{
35 code_model::{ 35 code_model::{
36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, 36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const,
37 ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, 37 ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function,
38 GenericDef, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, 38 GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
39 ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, 39 Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union,
40 Variant, VariantDef,
40 }, 41 },
41 has_source::HasSource, 42 has_source::HasSource,
42 semantics::{PathResolution, Semantics, SemanticsScope}, 43 semantics::{PathResolution, Semantics, SemanticsScope},
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 042e119b1..6b79e7bad 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -21,7 +21,7 @@ use crate::{
21 nameres::ModuleSource, 21 nameres::ModuleSource,
22 path::{ModPath, PathKind}, 22 path::{ModPath, PathKind},
23 src::HasChildSource, 23 src::HasChildSource,
24 AdtId, AttrDefId, Lookup, 24 AdtId, AttrDefId, GenericParamId, Lookup,
25}; 25};
26 26
27/// Holds documentation 27/// Holds documentation
@@ -235,6 +235,25 @@ impl Attrs {
235 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), 235 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
236 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), 236 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
237 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), 237 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
238 AttrDefId::GenericParamId(it) => match it {
239 GenericParamId::TypeParamId(it) => {
240 let src = it.parent.child_source(db);
241 RawAttrs::from_attrs_owner(
242 db,
243 src.with_value(
244 src.value[it.local_id].as_ref().either(|it| it as _, |it| it as _),
245 ),
246 )
247 }
248 GenericParamId::LifetimeParamId(it) => {
249 let src = it.parent.child_source(db);
250 RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
251 }
252 GenericParamId::ConstParamId(it) => {
253 let src = it.parent.child_source(db);
254 RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
255 }
256 },
238 }; 257 };
239 258
240 raw_attrs.filter(db, def.krate(db)) 259 raw_attrs.filter(db, def.krate(db))
@@ -260,14 +279,13 @@ impl Attrs {
260 } 279 }
261 280
262 pub fn docs(&self) -> Option<Documentation> { 281 pub fn docs(&self) -> Option<Documentation> {
263 let docs = self 282 let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? {
264 .by_key("doc") 283 AttrInput::Literal(s) => Some(s),
265 .attrs() 284 AttrInput::TokenTree(_) => None,
266 .flat_map(|attr| match attr.input.as_ref()? { 285 });
267 AttrInput::Literal(s) => Some(s), 286 // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the
268 AttrInput::TokenTree(_) => None, 287 // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524).
269 }) 288 let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n"))
270 .intersperse(&SmolStr::new_inline("\n"))
271 .map(|it| it.as_str()) 289 .map(|it| it.as_str())
272 .collect::<String>(); 290 .collect::<String>();
273 if docs.is_empty() { 291 if docs.is_empty() {
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 25f460504..211cb2faf 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -261,6 +261,15 @@ pub enum AdtId {
261} 261}
262impl_from!(StructId, UnionId, EnumId for AdtId); 262impl_from!(StructId, UnionId, EnumId for AdtId);
263 263
264/// A generic param
265#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
266pub enum GenericParamId {
267 TypeParamId(TypeParamId),
268 LifetimeParamId(LifetimeParamId),
269 ConstParamId(ConstParamId),
270}
271impl_from!(TypeParamId, LifetimeParamId, ConstParamId for GenericParamId);
272
264/// The defs which can be visible in the module. 273/// The defs which can be visible in the module.
265#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
266pub enum ModuleDefId { 275pub enum ModuleDefId {
@@ -357,6 +366,7 @@ pub enum AttrDefId {
357 TypeAliasId(TypeAliasId), 366 TypeAliasId(TypeAliasId),
358 MacroDefId(MacroDefId), 367 MacroDefId(MacroDefId),
359 ImplId(ImplId), 368 ImplId(ImplId),
369 GenericParamId(GenericParamId),
360} 370}
361 371
362impl_from!( 372impl_from!(
@@ -370,7 +380,8 @@ impl_from!(
370 TraitId, 380 TraitId,
371 TypeAliasId, 381 TypeAliasId,
372 MacroDefId, 382 MacroDefId,
373 ImplId 383 ImplId,
384 GenericParamId
374 for AttrDefId 385 for AttrDefId
375); 386);
376 387
@@ -495,6 +506,15 @@ impl AttrDefId {
495 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, 506 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate,
496 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, 507 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
497 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, 508 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate,
509 AttrDefId::GenericParamId(it) => {
510 match it {
511 GenericParamId::TypeParamId(it) => it.parent,
512 GenericParamId::LifetimeParamId(it) => it.parent,
513 GenericParamId::ConstParamId(it) => it.parent,
514 }
515 .module(db)
516 .krate
517 }
498 // FIXME: `MacroDefId` should store the defining module, then this can implement 518 // FIXME: `MacroDefId` should store the defining module, then this can implement
499 // `HasModule` 519 // `HasModule`
500 AttrDefId::MacroDefId(it) => it.krate, 520 AttrDefId::MacroDefId(it) => it.krate,
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 8a01e6eea..9518ac109 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -123,7 +123,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
123 // We follow what it did anyway :) 123 // We follow what it did anyway :)
124 if segments.len() == 1 && kind == PathKind::Plain { 124 if segments.len() == 1 && kind == PathKind::Plain {
125 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { 125 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
126 if let Some(crate_id) = hygiene.local_inner_macros() { 126 if let Some(crate_id) = hygiene.local_inner_macros(path) {
127 kind = PathKind::DollarCrate(crate_id); 127 kind = PathKind::DollarCrate(crate_id);
128 } 128 }
129 } 129 }
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 06f0a3ed9..0a0d021e0 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -404,7 +404,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
404 TRY_EXPR => FragmentKind::Expr, 404 TRY_EXPR => FragmentKind::Expr,
405 TUPLE_EXPR => FragmentKind::Expr, 405 TUPLE_EXPR => FragmentKind::Expr,
406 PAREN_EXPR => FragmentKind::Expr, 406 PAREN_EXPR => FragmentKind::Expr,
407 407 ARRAY_EXPR => FragmentKind::Expr,
408 FOR_EXPR => FragmentKind::Expr, 408 FOR_EXPR => FragmentKind::Expr,
409 PATH_EXPR => FragmentKind::Expr, 409 PATH_EXPR => FragmentKind::Expr,
410 CLOSURE_EXPR => FragmentKind::Expr, 410 CLOSURE_EXPR => FragmentKind::Expr,
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 7ab0a5e52..6042e15b2 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -2,30 +2,94 @@
2//! 2//!
3//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at 3//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
4//! this moment, this is horribly incomplete and handles only `$crate`. 4//! this moment, this is horribly incomplete and handles only `$crate`.
5use std::sync::Arc;
6
7use arena::{Arena, Idx};
5use base_db::CrateId; 8use base_db::CrateId;
6use either::Either; 9use either::Either;
7use syntax::ast; 10use mbe::Origin;
11use syntax::{ast, AstNode};
8 12
9use crate::{ 13use crate::{
10 db::AstDatabase, 14 db::AstDatabase,
11 name::{AsName, Name}, 15 name::{AsName, Name},
12 HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, 16 ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
13}; 17};
14 18
15#[derive(Clone, Debug)] 19#[derive(Clone, Debug)]
16pub struct Hygiene { 20pub struct Hygiene {
17 // This is what `$crate` expands to 21 frames: Option<Arc<HygieneFrames>>,
18 def_crate: Option<CrateId>, 22}
23
24impl Hygiene {
25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
26 Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) }
27 }
28
29 pub fn new_unhygienic() -> Hygiene {
30 Hygiene { frames: None }
31 }
32
33 // FIXME: this should just return name
34 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
35 if let Some(frames) = &self.frames {
36 if name_ref.text() == "$crate" {
37 if let Some(krate) = frames.root_crate(&name_ref) {
38 return Either::Right(krate);
39 }
40 }
41 }
42
43 Either::Left(name_ref.as_name())
44 }
45
46 pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> {
47 let frames = self.frames.as_ref()?;
48
49 let mut token = path.syntax().first_token()?;
50 let mut current = frames.first();
51
52 while let Some((frame, data)) =
53 current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
54 {
55 let (mapped, origin) = data;
56 if origin == Origin::Def {
57 return if frame.local_inner { frame.krate } else { None };
58 }
59 current = Some(&frames.0[frame.call_site?]);
60 token = mapped.value;
61 }
62 None
63 }
64}
65
66#[derive(Default, Debug)]
67struct HygieneFrames(Arena<HygieneFrame>);
68
69#[derive(Clone, Debug)]
70struct HygieneFrame {
71 expansion: Option<ExpansionInfo>,
19 72
20 // Indicate this is a local inner macro 73 // Indicate this is a local inner macro
21 local_inner: bool, 74 local_inner: bool,
75 krate: Option<CrateId>,
76
77 call_site: Option<Idx<HygieneFrame>>,
78 def_site: Option<Idx<HygieneFrame>>,
22} 79}
23 80
24impl Hygiene { 81impl HygieneFrames {
25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { 82 fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self {
26 let (def_crate, local_inner) = match file_id.0 { 83 let mut frames = HygieneFrames::default();
84 frames.add(db, file_id);
85 frames
86 }
87
88 fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option<Idx<HygieneFrame>> {
89 let (krate, local_inner) = match file_id.0 {
27 HirFileIdRepr::FileId(_) => (None, false), 90 HirFileIdRepr::FileId(_) => (None, false),
28 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { 91 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
92 MacroCallId::EagerMacro(_id) => (None, false),
29 MacroCallId::LazyMacro(id) => { 93 MacroCallId::LazyMacro(id) => {
30 let loc = db.lookup_intern_macro(id); 94 let loc = db.lookup_intern_macro(id);
31 match loc.def.kind { 95 match loc.def.kind {
@@ -36,31 +100,68 @@ impl Hygiene {
36 MacroDefKind::ProcMacro(_) => (None, false), 100 MacroDefKind::ProcMacro(_) => (None, false),
37 } 101 }
38 } 102 }
39 MacroCallId::EagerMacro(_id) => (None, false),
40 }, 103 },
41 }; 104 };
42 Hygiene { def_crate, local_inner }
43 }
44 105
45 pub fn new_unhygienic() -> Hygiene { 106 let expansion = file_id.expansion_info(db);
46 Hygiene { def_crate: None, local_inner: false } 107 let expansion = match expansion {
108 None => {
109 return Some(self.0.alloc(HygieneFrame {
110 expansion: None,
111 local_inner,
112 krate,
113 call_site: None,
114 def_site: None,
115 }));
116 }
117 Some(it) => it,
118 };
119
120 let def_site = expansion.def.clone();
121 let call_site = expansion.arg.file_id;
122 let idx = self.0.alloc(HygieneFrame {
123 expansion: Some(expansion),
124 local_inner,
125 krate,
126 call_site: None,
127 def_site: None,
128 });
129
130 self.0[idx].call_site = self.add(db, call_site);
131 self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id));
132
133 Some(idx)
47 } 134 }
48 135
49 // FIXME: this should just return name 136 fn first(&self) -> Option<&HygieneFrame> {
50 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { 137 self.0.iter().next().map(|it| it.1)
51 if let Some(def_crate) = self.def_crate {
52 if name_ref.text() == "$crate" {
53 return Either::Right(def_crate);
54 }
55 }
56 Either::Left(name_ref.as_name())
57 } 138 }
58 139
59 pub fn local_inner_macros(&self) -> Option<CrateId> { 140 fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> {
60 if self.local_inner { 141 let mut token = name_ref.syntax().first_token()?;
61 self.def_crate 142 let first = self.first()?;
62 } else { 143 let mut result = first.krate;
63 None 144 let mut current = Some(first);
145
146 while let Some((frame, (mapped, origin))) =
147 current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
148 {
149 result = frame.krate;
150
151 let site = match origin {
152 Origin::Def => frame.def_site,
153 Origin::Call => frame.call_site,
154 };
155
156 let site = match site {
157 None => break,
158 Some(it) => it,
159 };
160
161 current = Some(&self.0[site]);
162 token = mapped.value;
64 } 163 }
164
165 result
65 } 166 }
66} 167}
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 3fa1b1d77..5b6734a5f 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -340,11 +340,8 @@ impl ExpansionInfo {
340 Some(self.expanded.with_value(token)) 340 Some(self.expanded.with_value(token))
341 } 341 }
342 342
343 pub fn map_token_up( 343 pub fn map_token_up(&self, token: &SyntaxToken) -> Option<(InFile<SyntaxToken>, Origin)> {
344 &self, 344 let token_id = self.exp_map.token_by_range(token.text_range())?;
345 token: InFile<&SyntaxToken>,
346 ) -> Option<(InFile<SyntaxToken>, Origin)> {
347 let token_id = self.exp_map.token_by_range(token.value.text_range())?;
348 345
349 let (token_id, origin) = self.macro_def.0.map_id_up(token_id); 346 let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
350 let (token_map, tt) = match origin { 347 let (token_map, tt) = match origin {
@@ -359,7 +356,7 @@ impl ExpansionInfo {
359 ), 356 ),
360 }; 357 };
361 358
362 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; 359 let range = token_map.range_by_token(token_id)?.by_kind(token.kind())?;
363 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) 360 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start())
364 .into_token()?; 361 .into_token()?;
365 Some((tt.with_value(token), origin)) 362 Some((tt.with_value(token), origin))
@@ -495,7 +492,7 @@ fn ascend_call_token(
495 expansion: &ExpansionInfo, 492 expansion: &ExpansionInfo,
496 token: InFile<SyntaxToken>, 493 token: InFile<SyntaxToken>,
497) -> Option<InFile<SyntaxToken>> { 494) -> Option<InFile<SyntaxToken>> {
498 let (mapped, origin) = expansion.map_token_up(token.as_ref())?; 495 let (mapped, origin) = expansion.map_token_up(&token.value)?;
499 if origin != Origin::Call { 496 if origin != Origin::Call {
500 return None; 497 return None;
501 } 498 }
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index a7656b864..23b79abc4 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -371,6 +371,37 @@ expand!();
371} 371}
372 372
373#[test] 373#[test]
374fn infer_macro_with_dollar_crate_in_def_site() {
375 check_types(
376 r#"
377//- /main.rs crate:main deps:foo
378use foo::expand;
379
380macro_rules! list {
381 ($($tt:tt)*) => { $($tt)* }
382}
383
384fn test() {
385 let r = expand!();
386 r;
387 //^ u128
388}
389
390//- /lib.rs crate:foo
391#[macro_export]
392macro_rules! expand {
393 () => { list!($crate::m!()) };
394}
395
396#[macro_export]
397macro_rules! m {
398 () => { 0u128 };
399}
400"#,
401 );
402}
403
404#[test]
374fn infer_type_value_non_legacy_macro_use_as() { 405fn infer_type_value_non_legacy_macro_use_as() {
375 check_infer( 406 check_infer(
376 r#" 407 r#"
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 307a257b1..cffe8630b 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -326,6 +326,24 @@ fn infer_paren_macro_call() {
326} 326}
327 327
328#[test] 328#[test]
329fn infer_array_macro_call() {
330 check_infer(
331 r#"
332 macro_rules! bar { () => {0u32} }
333 fn test() {
334 let a = [bar!()];
335 }
336 "#,
337 expect![[r#"
338 !0..4 '0u32': u32
339 44..69 '{ ...()]; }': ()
340 54..55 'a': [u32; _]
341 58..66 '[bar!()]': [u32; _]
342 "#]],
343 );
344}
345
346#[test]
329fn bug_1030() { 347fn bug_1030() {
330 check_infer( 348 check_infer(
331 r#" 349 r#"
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index ab5f87c48..385b46601 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -150,7 +150,7 @@ fn match_subtree(
150 res.add_err(err!("leftover tokens")); 150 res.add_err(err!("leftover tokens"));
151 } 151 }
152 } 152 }
153 Op::Var { name, kind } => { 153 Op::Var { name, kind, .. } => {
154 let kind = match kind { 154 let kind = match kind {
155 Some(k) => k, 155 Some(k) => k,
156 None => { 156 None => {
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs
index 720531237..57f3f104d 100644
--- a/crates/mbe/src/mbe_expander/transcriber.rs
+++ b/crates/mbe/src/mbe_expander/transcriber.rs
@@ -100,8 +100,8 @@ fn expand_subtree(
100 err = err.or(e); 100 err = err.or(e);
101 arena.push(tt.into()); 101 arena.push(tt.into());
102 } 102 }
103 Op::Var { name, .. } => { 103 Op::Var { name, id, .. } => {
104 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name); 104 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id);
105 err = err.or(e); 105 err = err.or(e);
106 push_fragment(arena, fragment); 106 push_fragment(arena, fragment);
107 } 107 }
@@ -118,12 +118,10 @@ fn expand_subtree(
118 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } 118 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err }
119} 119}
120 120
121fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { 121fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> {
122 if v == "crate" { 122 if v == "crate" {
123 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. 123 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
124 let tt = 124 let tt = tt::Leaf::from(tt::Ident { text: "$crate".into(), id }).into();
125 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
126 .into();
127 ExpandResult::ok(Fragment::Tokens(tt)) 125 ExpandResult::ok(Fragment::Tokens(tt))
128 } else if !ctx.bindings.contains(v) { 126 } else if !ctx.bindings.contains(v) {
129 // Note that it is possible to have a `$var` inside a macro which is not bound. 127 // Note that it is possible to have a `$var` inside a macro which is not bound.
@@ -142,14 +140,8 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
142 let tt = tt::Subtree { 140 let tt = tt::Subtree {
143 delimiter: None, 141 delimiter: None,
144 token_trees: vec![ 142 token_trees: vec![
145 tt::Leaf::from(tt::Punct { 143 tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(),
146 char: '$', 144 tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(),
147 spacing: tt::Spacing::Alone,
148 id: tt::TokenId::unspecified(),
149 })
150 .into(),
151 tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() })
152 .into(),
153 ], 145 ],
154 } 146 }
155 .into(); 147 .into();
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 2f3ebc831..77cc739b6 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -8,7 +8,7 @@ use crate::{tt_iter::TtIter, ExpandError, MetaTemplate};
8 8
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub(crate) enum Op { 10pub(crate) enum Op {
11 Var { name: SmolStr, kind: Option<SmolStr> }, 11 Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
12 Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> }, 12 Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
13 Leaf(tt::Leaf), 13 Leaf(tt::Leaf),
14 Subtree(MetaTemplate), 14 Subtree(MetaTemplate),
@@ -106,18 +106,21 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
106 } 106 }
107 let name = UNDERSCORE.clone(); 107 let name = UNDERSCORE.clone();
108 let kind = eat_fragment_kind(src, mode)?; 108 let kind = eat_fragment_kind(src, mode)?;
109 Op::Var { name, kind } 109 let id = punct.id;
110 Op::Var { name, kind, id }
110 } 111 }
111 tt::Leaf::Ident(ident) => { 112 tt::Leaf::Ident(ident) => {
112 let name = ident.text.clone(); 113 let name = ident.text.clone();
113 let kind = eat_fragment_kind(src, mode)?; 114 let kind = eat_fragment_kind(src, mode)?;
114 Op::Var { name, kind } 115 let id = ident.id;
116 Op::Var { name, kind, id }
115 } 117 }
116 tt::Leaf::Literal(lit) => { 118 tt::Leaf::Literal(lit) => {
117 if is_boolean_literal(&lit) { 119 if is_boolean_literal(&lit) {
118 let name = lit.text.clone(); 120 let name = lit.text.clone();
119 let kind = eat_fragment_kind(src, mode)?; 121 let kind = eat_fragment_kind(src, mode)?;
120 Op::Var { name, kind } 122 let id = lit.id;
123 Op::Var { name, kind, id }
121 } else { 124 } else {
122 bail!("bad var 2"); 125 bail!("bad var 2");
123 } 126 }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 1db5b4e7d..685a9fdf0 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -679,7 +679,7 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json:
679 for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) { 679 for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) {
680 fn key(f: &str) -> &str { 680 fn key(f: &str) -> &str {
681 f.splitn(2, "_").next().unwrap() 681 f.splitn(2, "_").next().unwrap()
682 }; 682 }
683 assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2); 683 assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2);
684 } 684 }
685 685
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 77233ab31..824ebf41c 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -220,7 +220,7 @@ impl ast::RecordExprFieldList {
220 InsertPosition::After($anchor.syntax().clone().into()) 220 InsertPosition::After($anchor.syntax().clone().into())
221 } 221 }
222 }; 222 };
223 }; 223 }
224 224
225 let position = match position { 225 let position = match position {
226 InsertPosition::First => after_l_curly!(), 226 InsertPosition::First => after_l_curly!(),
@@ -533,7 +533,7 @@ impl ast::GenericParamList {
533 InsertPosition::After($anchor.syntax().clone().into()) 533 InsertPosition::After($anchor.syntax().clone().into())
534 } 534 }
535 }; 535 };
536 }; 536 }
537 537
538 let position = match self.generic_params().last() { 538 let position = match self.generic_params().last() {
539 Some(it) => after_field!(it), 539 Some(it) => after_field!(it),