diff options
Diffstat (limited to 'crates')
102 files changed, 2148 insertions, 716 deletions
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs index 69499ea32..4f59d39a9 100644 --- a/crates/assists/src/assist_context.rs +++ b/crates/assists/src/assist_context.rs | |||
@@ -4,10 +4,10 @@ use std::mem; | |||
4 | 4 | ||
5 | use algo::find_covering_element; | 5 | use algo::find_covering_element; |
6 | use hir::Semantics; | 6 | use hir::Semantics; |
7 | use ide_db::base_db::{FileId, FileRange}; | 7 | use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange}; |
8 | use ide_db::{ | 8 | use ide_db::{ |
9 | label::Label, | 9 | label::Label, |
10 | source_change::{SourceChange, SourceFileEdit}, | 10 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, |
11 | RootDatabase, | 11 | RootDatabase, |
12 | }; | 12 | }; |
13 | use syntax::{ | 13 | use syntax::{ |
@@ -19,7 +19,7 @@ use text_edit::{TextEdit, TextEditBuilder}; | |||
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | assist_config::{AssistConfig, SnippetCap}, | 21 | assist_config::{AssistConfig, SnippetCap}, |
22 | Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist, | 22 | Assist, AssistId, AssistKind, GroupLabel, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 25 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
@@ -105,46 +105,23 @@ impl<'a> AssistContext<'a> { | |||
105 | pub(crate) struct Assists { | 105 | pub(crate) struct Assists { |
106 | resolve: bool, | 106 | resolve: bool, |
107 | file: FileId, | 107 | file: FileId, |
108 | buf: Vec<(Assist, Option<SourceChange>)>, | 108 | buf: Vec<Assist>, |
109 | allowed: Option<Vec<AssistKind>>, | 109 | allowed: Option<Vec<AssistKind>>, |
110 | } | 110 | } |
111 | 111 | ||
112 | impl Assists { | 112 | impl Assists { |
113 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { | 113 | pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists { |
114 | Assists { | 114 | Assists { |
115 | resolve: true, | 115 | resolve, |
116 | file: ctx.frange.file_id, | 116 | file: ctx.frange.file_id, |
117 | buf: Vec::new(), | 117 | buf: Vec::new(), |
118 | allowed: ctx.config.allowed.clone(), | 118 | allowed: ctx.config.allowed.clone(), |
119 | } | 119 | } |
120 | } | 120 | } |
121 | 121 | ||
122 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { | 122 | pub(crate) fn finish(mut self) -> Vec<Assist> { |
123 | Assists { | 123 | self.buf.sort_by_key(|assist| assist.target.len()); |
124 | resolve: false, | 124 | self.buf |
125 | file: ctx.frange.file_id, | ||
126 | buf: Vec::new(), | ||
127 | allowed: ctx.config.allowed.clone(), | ||
128 | } | ||
129 | } | ||
130 | |||
131 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { | ||
132 | assert!(!self.resolve); | ||
133 | self.finish() | ||
134 | .into_iter() | ||
135 | .map(|(label, edit)| { | ||
136 | assert!(edit.is_none()); | ||
137 | label | ||
138 | }) | ||
139 | .collect() | ||
140 | } | ||
141 | |||
142 | pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> { | ||
143 | assert!(self.resolve); | ||
144 | self.finish() | ||
145 | .into_iter() | ||
146 | .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() }) | ||
147 | .collect() | ||
148 | } | 125 | } |
149 | 126 | ||
150 | pub(crate) fn add( | 127 | pub(crate) fn add( |
@@ -158,7 +135,7 @@ impl Assists { | |||
158 | return None; | 135 | return None; |
159 | } | 136 | } |
160 | let label = Label::new(label.into()); | 137 | let label = Label::new(label.into()); |
161 | let assist = Assist { id, label, group: None, target }; | 138 | let assist = Assist { id, label, group: None, target, source_change: None }; |
162 | self.add_impl(assist, f) | 139 | self.add_impl(assist, f) |
163 | } | 140 | } |
164 | 141 | ||
@@ -174,11 +151,11 @@ impl Assists { | |||
174 | return None; | 151 | return None; |
175 | } | 152 | } |
176 | let label = Label::new(label.into()); | 153 | let label = Label::new(label.into()); |
177 | let assist = Assist { id, label, group: Some(group.clone()), target }; | 154 | let assist = Assist { id, label, group: Some(group.clone()), target, source_change: None }; |
178 | self.add_impl(assist, f) | 155 | self.add_impl(assist, f) |
179 | } | 156 | } |
180 | 157 | ||
181 | fn add_impl(&mut self, assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 158 | fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
182 | let source_change = if self.resolve { | 159 | let source_change = if self.resolve { |
183 | let mut builder = AssistBuilder::new(self.file); | 160 | let mut builder = AssistBuilder::new(self.file); |
184 | f(&mut builder); | 161 | f(&mut builder); |
@@ -186,16 +163,12 @@ impl Assists { | |||
186 | } else { | 163 | } else { |
187 | None | 164 | None |
188 | }; | 165 | }; |
166 | assist.source_change = source_change.clone(); | ||
189 | 167 | ||
190 | self.buf.push((assist, source_change)); | 168 | self.buf.push(assist); |
191 | Some(()) | 169 | Some(()) |
192 | } | 170 | } |
193 | 171 | ||
194 | fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> { | ||
195 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); | ||
196 | self.buf | ||
197 | } | ||
198 | |||
199 | fn is_allowed(&self, id: &AssistId) -> bool { | 172 | fn is_allowed(&self, id: &AssistId) -> bool { |
200 | match &self.allowed { | 173 | match &self.allowed { |
201 | Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), | 174 | Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), |
@@ -209,6 +182,7 @@ pub(crate) struct AssistBuilder { | |||
209 | file_id: FileId, | 182 | file_id: FileId, |
210 | is_snippet: bool, | 183 | is_snippet: bool, |
211 | source_file_edits: Vec<SourceFileEdit>, | 184 | source_file_edits: Vec<SourceFileEdit>, |
185 | file_system_edits: Vec<FileSystemEdit>, | ||
212 | } | 186 | } |
213 | 187 | ||
214 | impl AssistBuilder { | 188 | impl AssistBuilder { |
@@ -218,6 +192,7 @@ impl AssistBuilder { | |||
218 | file_id, | 192 | file_id, |
219 | is_snippet: false, | 193 | is_snippet: false, |
220 | source_file_edits: Vec::default(), | 194 | source_file_edits: Vec::default(), |
195 | file_system_edits: Vec::default(), | ||
221 | } | 196 | } |
222 | } | 197 | } |
223 | 198 | ||
@@ -282,12 +257,17 @@ impl AssistBuilder { | |||
282 | algo::diff(&node, &new).into_text_edit(&mut self.edit); | 257 | algo::diff(&node, &new).into_text_edit(&mut self.edit); |
283 | } | 258 | } |
284 | } | 259 | } |
260 | pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { | ||
261 | let file_system_edit = | ||
262 | FileSystemEdit::CreateFile { dst: dst.clone(), initial_contents: content.into() }; | ||
263 | self.file_system_edits.push(file_system_edit); | ||
264 | } | ||
285 | 265 | ||
286 | fn finish(mut self) -> SourceChange { | 266 | fn finish(mut self) -> SourceChange { |
287 | self.commit(); | 267 | self.commit(); |
288 | SourceChange { | 268 | SourceChange { |
289 | source_file_edits: mem::take(&mut self.source_file_edits), | 269 | source_file_edits: mem::take(&mut self.source_file_edits), |
290 | file_system_edits: Default::default(), | 270 | file_system_edits: mem::take(&mut self.file_system_edits), |
291 | is_snippet: self.is_snippet, | 271 | is_snippet: self.is_snippet, |
292 | } | 272 | } |
293 | } | 273 | } |
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index e413505d3..7df05b841 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs | |||
@@ -15,7 +15,7 @@ use crate::{ | |||
15 | // | 15 | // |
16 | // ``` | 16 | // ``` |
17 | // trait Trait<T> { | 17 | // trait Trait<T> { |
18 | // Type X; | 18 | // type X; |
19 | // fn foo(&self) -> T; | 19 | // fn foo(&self) -> T; |
20 | // fn bar(&self) {} | 20 | // fn bar(&self) {} |
21 | // } | 21 | // } |
@@ -27,14 +27,16 @@ use crate::{ | |||
27 | // -> | 27 | // -> |
28 | // ``` | 28 | // ``` |
29 | // trait Trait<T> { | 29 | // trait Trait<T> { |
30 | // Type X; | 30 | // type X; |
31 | // fn foo(&self) -> T; | 31 | // fn foo(&self) -> T; |
32 | // fn bar(&self) {} | 32 | // fn bar(&self) {} |
33 | // } | 33 | // } |
34 | // | 34 | // |
35 | // impl Trait<u32> for () { | 35 | // impl Trait<u32> for () { |
36 | // $0type X; | ||
37 | // | ||
36 | // fn foo(&self) -> u32 { | 38 | // fn foo(&self) -> u32 { |
37 | // ${0:todo!()} | 39 | // todo!() |
38 | // } | 40 | // } |
39 | // } | 41 | // } |
40 | // ``` | 42 | // ``` |
@@ -54,13 +56,13 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
54 | // | 56 | // |
55 | // ``` | 57 | // ``` |
56 | // trait Trait { | 58 | // trait Trait { |
57 | // Type X; | 59 | // type X; |
58 | // fn foo(&self); | 60 | // fn foo(&self); |
59 | // fn bar(&self) {} | 61 | // fn bar(&self) {} |
60 | // } | 62 | // } |
61 | // | 63 | // |
62 | // impl Trait for () { | 64 | // impl Trait for () { |
63 | // Type X = (); | 65 | // type X = (); |
64 | // fn foo(&self) {}<|> | 66 | // fn foo(&self) {}<|> |
65 | // | 67 | // |
66 | // } | 68 | // } |
@@ -68,13 +70,13 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
68 | // -> | 70 | // -> |
69 | // ``` | 71 | // ``` |
70 | // trait Trait { | 72 | // trait Trait { |
71 | // Type X; | 73 | // type X; |
72 | // fn foo(&self); | 74 | // fn foo(&self); |
73 | // fn bar(&self) {} | 75 | // fn bar(&self) {} |
74 | // } | 76 | // } |
75 | // | 77 | // |
76 | // impl Trait for () { | 78 | // impl Trait for () { |
77 | // Type X = (); | 79 | // type X = (); |
78 | // fn foo(&self) {} | 80 | // fn foo(&self) {} |
79 | // | 81 | // |
80 | // $0fn bar(&self) {} | 82 | // $0fn bar(&self) {} |
diff --git a/crates/assists/src/handlers/extract_module_to_file.rs b/crates/assists/src/handlers/extract_module_to_file.rs new file mode 100644 index 000000000..50bf67ef7 --- /dev/null +++ b/crates/assists/src/handlers/extract_module_to_file.rs | |||
@@ -0,0 +1,133 @@ | |||
1 | use ast::edit::IndentLevel; | ||
2 | use ide_db::base_db::AnchoredPathBuf; | ||
3 | use syntax::{ | ||
4 | ast::{self, edit::AstNodeEdit, NameOwner}, | ||
5 | AstNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
9 | |||
10 | // Assist: extract_module_to_file | ||
11 | // | ||
12 | // This assist extract module to file. | ||
13 | // | ||
14 | // ``` | ||
15 | // mod foo {<|> | ||
16 | // fn t() {} | ||
17 | // } | ||
18 | // ``` | ||
19 | // -> | ||
20 | // ``` | ||
21 | // mod foo; | ||
22 | // ``` | ||
23 | pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
24 | let module_ast = ctx.find_node_at_offset::<ast::Module>()?; | ||
25 | let module_name = module_ast.name()?; | ||
26 | |||
27 | let module_def = ctx.sema.to_def(&module_ast)?; | ||
28 | let parent_module = module_def.parent(ctx.db())?; | ||
29 | |||
30 | let module_items = module_ast.item_list()?; | ||
31 | let target = module_ast.syntax().text_range(); | ||
32 | let anchor_file_id = ctx.frange.file_id; | ||
33 | |||
34 | acc.add( | ||
35 | AssistId("extract_module_to_file", AssistKind::RefactorExtract), | ||
36 | "Extract module to file", | ||
37 | target, | ||
38 | |builder| { | ||
39 | let path = { | ||
40 | let dir = match parent_module.name(ctx.db()) { | ||
41 | Some(name) if !parent_module.is_mod_rs(ctx.db()) => format!("{}/", name), | ||
42 | _ => String::new(), | ||
43 | }; | ||
44 | format!("./{}{}.rs", dir, module_name) | ||
45 | }; | ||
46 | let contents = { | ||
47 | let items = module_items.dedent(IndentLevel(1)).to_string(); | ||
48 | let mut items = | ||
49 | items.trim_start_matches('{').trim_end_matches('}').trim().to_string(); | ||
50 | if !items.is_empty() { | ||
51 | items.push('\n'); | ||
52 | } | ||
53 | items | ||
54 | }; | ||
55 | |||
56 | builder.replace(target, format!("mod {};", module_name)); | ||
57 | |||
58 | let dst = AnchoredPathBuf { anchor: anchor_file_id, path }; | ||
59 | builder.create_file(dst, contents); | ||
60 | }, | ||
61 | ) | ||
62 | } | ||
63 | |||
64 | #[cfg(test)] | ||
65 | mod tests { | ||
66 | use crate::tests::check_assist; | ||
67 | |||
68 | use super::*; | ||
69 | |||
70 | #[test] | ||
71 | fn extract_from_root() { | ||
72 | check_assist( | ||
73 | extract_module_to_file, | ||
74 | r#" | ||
75 | mod tests {<|> | ||
76 | #[test] fn t() {} | ||
77 | } | ||
78 | "#, | ||
79 | r#" | ||
80 | //- /main.rs | ||
81 | mod tests; | ||
82 | //- /tests.rs | ||
83 | #[test] fn t() {} | ||
84 | "#, | ||
85 | ); | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn extract_from_submodule() { | ||
90 | check_assist( | ||
91 | extract_module_to_file, | ||
92 | r#" | ||
93 | //- /main.rs | ||
94 | mod submod; | ||
95 | //- /submod.rs | ||
96 | mod inner<|> { | ||
97 | fn f() {} | ||
98 | } | ||
99 | fn g() {} | ||
100 | "#, | ||
101 | r#" | ||
102 | //- /submod.rs | ||
103 | mod inner; | ||
104 | fn g() {} | ||
105 | //- /submod/inner.rs | ||
106 | fn f() {} | ||
107 | "#, | ||
108 | ); | ||
109 | } | ||
110 | |||
111 | #[test] | ||
112 | fn extract_from_mod_rs() { | ||
113 | check_assist( | ||
114 | extract_module_to_file, | ||
115 | r#" | ||
116 | //- /main.rs | ||
117 | mod submodule; | ||
118 | //- /submodule/mod.rs | ||
119 | mod inner<|> { | ||
120 | fn f() {} | ||
121 | } | ||
122 | fn g() {} | ||
123 | "#, | ||
124 | r#" | ||
125 | //- /submodule/mod.rs | ||
126 | mod inner; | ||
127 | fn g() {} | ||
128 | //- /submodule/inner.rs | ||
129 | fn f() {} | ||
130 | "#, | ||
131 | ); | ||
132 | } | ||
133 | } | ||
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs index 91e2f5c8c..f9c33b3f7 100644 --- a/crates/assists/src/handlers/invert_if.rs +++ b/crates/assists/src/handlers/invert_if.rs | |||
@@ -78,6 +78,15 @@ mod tests { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | #[test] | 80 | #[test] |
81 | fn invert_if_remove_not_parentheses() { | ||
82 | check_assist( | ||
83 | invert_if, | ||
84 | "fn f() { i<|>f !(x == 3 || x == 4 || x == 5) { 3 * 2 } else { 1 } }", | ||
85 | "fn f() { if x == 3 || x == 4 || x == 5 { 1 } else { 3 * 2 } }", | ||
86 | ) | ||
87 | } | ||
88 | |||
89 | #[test] | ||
81 | fn invert_if_remove_inequality() { | 90 | fn invert_if_remove_inequality() { |
82 | check_assist( | 91 | check_assist( |
83 | invert_if, | 92 | invert_if, |
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs index 1ff5e92b0..f72dd49ed 100644 --- a/crates/assists/src/handlers/remove_unused_param.rs +++ b/crates/assists/src/handlers/remove_unused_param.rs | |||
@@ -2,9 +2,10 @@ use ide_db::{defs::Definition, search::Reference}; | |||
2 | use syntax::{ | 2 | use syntax::{ |
3 | algo::find_node_at_range, | 3 | algo::find_node_at_range, |
4 | ast::{self, ArgListOwner}, | 4 | ast::{self, ArgListOwner}, |
5 | AstNode, SyntaxNode, TextRange, T, | 5 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | 7 | use test_utils::mark; |
8 | use SyntaxKind::WHITESPACE; | ||
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists, | 11 | assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists, |
@@ -56,7 +57,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
56 | "Remove unused parameter", | 57 | "Remove unused parameter", |
57 | param.syntax().text_range(), | 58 | param.syntax().text_range(), |
58 | |builder| { | 59 | |builder| { |
59 | builder.delete(range_with_coma(param.syntax())); | 60 | builder.delete(range_to_remove(param.syntax())); |
60 | for usage in fn_def.usages(&ctx.sema).all() { | 61 | for usage in fn_def.usages(&ctx.sema).all() { |
61 | process_usage(ctx, builder, usage, param_position); | 62 | process_usage(ctx, builder, usage, param_position); |
62 | } | 63 | } |
@@ -80,19 +81,34 @@ fn process_usage( | |||
80 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; | 81 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; |
81 | 82 | ||
82 | builder.edit_file(usage.file_range.file_id); | 83 | builder.edit_file(usage.file_range.file_id); |
83 | builder.delete(range_with_coma(arg.syntax())); | 84 | builder.delete(range_to_remove(arg.syntax())); |
84 | 85 | ||
85 | Some(()) | 86 | Some(()) |
86 | } | 87 | } |
87 | 88 | ||
88 | fn range_with_coma(node: &SyntaxNode) -> TextRange { | 89 | fn range_to_remove(node: &SyntaxNode) -> TextRange { |
89 | let up_to = next_prev().find_map(|dir| { | 90 | let up_to_comma = next_prev().find_map(|dir| { |
90 | node.siblings_with_tokens(dir) | 91 | node.siblings_with_tokens(dir) |
91 | .filter_map(|it| it.into_token()) | 92 | .filter_map(|it| it.into_token()) |
92 | .find(|it| it.kind() == T![,]) | 93 | .find(|it| it.kind() == T![,]) |
94 | .map(|it| (dir, it)) | ||
93 | }); | 95 | }); |
94 | let up_to = up_to.map_or(node.text_range(), |it| it.text_range()); | 96 | if let Some((dir, token)) = up_to_comma { |
95 | node.text_range().cover(up_to) | 97 | if node.next_sibling().is_some() { |
98 | let up_to_space = token | ||
99 | .siblings_with_tokens(dir) | ||
100 | .skip(1) | ||
101 | .take_while(|it| it.kind() == WHITESPACE) | ||
102 | .last() | ||
103 | .and_then(|it| it.into_token()); | ||
104 | return node | ||
105 | .text_range() | ||
106 | .cover(up_to_space.map_or(token.text_range(), |it| it.text_range())); | ||
107 | } | ||
108 | node.text_range().cover(token.text_range()) | ||
109 | } else { | ||
110 | node.text_range() | ||
111 | } | ||
96 | } | 112 | } |
97 | 113 | ||
98 | #[cfg(test)] | 114 | #[cfg(test)] |
@@ -119,6 +135,57 @@ fn b() { foo(9, ) } | |||
119 | } | 135 | } |
120 | 136 | ||
121 | #[test] | 137 | #[test] |
138 | fn remove_unused_first_param() { | ||
139 | check_assist( | ||
140 | remove_unused_param, | ||
141 | r#" | ||
142 | fn foo(<|>x: i32, y: i32) { y; } | ||
143 | fn a() { foo(1, 2) } | ||
144 | fn b() { foo(1, 2,) } | ||
145 | "#, | ||
146 | r#" | ||
147 | fn foo(y: i32) { y; } | ||
148 | fn a() { foo(2) } | ||
149 | fn b() { foo(2,) } | ||
150 | "#, | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn remove_unused_single_param() { | ||
156 | check_assist( | ||
157 | remove_unused_param, | ||
158 | r#" | ||
159 | fn foo(<|>x: i32) { 0; } | ||
160 | fn a() { foo(1) } | ||
161 | fn b() { foo(1, ) } | ||
162 | "#, | ||
163 | r#" | ||
164 | fn foo() { 0; } | ||
165 | fn a() { foo() } | ||
166 | fn b() { foo( ) } | ||
167 | "#, | ||
168 | ); | ||
169 | } | ||
170 | |||
171 | #[test] | ||
172 | fn remove_unused_surrounded_by_parms() { | ||
173 | check_assist( | ||
174 | remove_unused_param, | ||
175 | r#" | ||
176 | fn foo(x: i32, <|>y: i32, z: i32) { x; } | ||
177 | fn a() { foo(1, 2, 3) } | ||
178 | fn b() { foo(1, 2, 3,) } | ||
179 | "#, | ||
180 | r#" | ||
181 | fn foo(x: i32, z: i32) { x; } | ||
182 | fn a() { foo(1, 3) } | ||
183 | fn b() { foo(1, 3,) } | ||
184 | "#, | ||
185 | ); | ||
186 | } | ||
187 | |||
188 | #[test] | ||
122 | fn remove_unused_qualified_call() { | 189 | fn remove_unused_qualified_call() { |
123 | check_assist( | 190 | check_assist( |
124 | remove_unused_param, | 191 | remove_unused_param, |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 6e736ccb3..fdec886e9 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -73,45 +73,32 @@ pub struct Assist { | |||
73 | /// Target ranges are used to sort assists: the smaller the target range, | 73 | /// Target ranges are used to sort assists: the smaller the target range, |
74 | /// the more specific assist is, and so it should be sorted first. | 74 | /// the more specific assist is, and so it should be sorted first. |
75 | pub target: TextRange, | 75 | pub target: TextRange, |
76 | } | 76 | /// Computing source change sometimes is much more costly then computing the |
77 | 77 | /// other fields. Additionally, the actual change is not required to show | |
78 | #[derive(Debug, Clone)] | 78 | /// the lightbulb UI, it only is needed when the user tries to apply an |
79 | pub struct ResolvedAssist { | 79 | /// assist. So, we compute it lazily: the API allow requesting assists with |
80 | pub assist: Assist, | 80 | /// or without source change. We could (and in fact, used to) distinguish |
81 | pub source_change: SourceChange, | 81 | /// between resolved and unresolved assists at the type level, but this is |
82 | /// cumbersome, especially if you want to embed an assist into another data | ||
83 | /// structure, such as a diagnostic. | ||
84 | pub source_change: Option<SourceChange>, | ||
82 | } | 85 | } |
83 | 86 | ||
84 | impl Assist { | 87 | impl Assist { |
85 | /// Return all the assists applicable at the given position. | 88 | /// Return all the assists applicable at the given position. |
86 | /// | 89 | pub fn get( |
87 | /// Assists are returned in the "unresolved" state, that is only labels are | ||
88 | /// returned, without actual edits. | ||
89 | pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> { | ||
90 | let sema = Semantics::new(db); | ||
91 | let ctx = AssistContext::new(sema, config, range); | ||
92 | let mut acc = Assists::new_unresolved(&ctx); | ||
93 | handlers::all().iter().for_each(|handler| { | ||
94 | handler(&mut acc, &ctx); | ||
95 | }); | ||
96 | acc.finish_unresolved() | ||
97 | } | ||
98 | |||
99 | /// Return all the assists applicable at the given position. | ||
100 | /// | ||
101 | /// Assists are returned in the "resolved" state, that is with edit fully | ||
102 | /// computed. | ||
103 | pub fn resolved( | ||
104 | db: &RootDatabase, | 90 | db: &RootDatabase, |
105 | config: &AssistConfig, | 91 | config: &AssistConfig, |
92 | resolve: bool, | ||
106 | range: FileRange, | 93 | range: FileRange, |
107 | ) -> Vec<ResolvedAssist> { | 94 | ) -> Vec<Assist> { |
108 | let sema = Semantics::new(db); | 95 | let sema = Semantics::new(db); |
109 | let ctx = AssistContext::new(sema, config, range); | 96 | let ctx = AssistContext::new(sema, config, range); |
110 | let mut acc = Assists::new_resolved(&ctx); | 97 | let mut acc = Assists::new(&ctx, resolve); |
111 | handlers::all().iter().for_each(|handler| { | 98 | handlers::all().iter().for_each(|handler| { |
112 | handler(&mut acc, &ctx); | 99 | handler(&mut acc, &ctx); |
113 | }); | 100 | }); |
114 | acc.finish_resolved() | 101 | acc.finish() |
115 | } | 102 | } |
116 | } | 103 | } |
117 | 104 | ||
@@ -129,6 +116,7 @@ mod handlers { | |||
129 | mod convert_integer_literal; | 116 | mod convert_integer_literal; |
130 | mod early_return; | 117 | mod early_return; |
131 | mod expand_glob_import; | 118 | mod expand_glob_import; |
119 | mod extract_module_to_file; | ||
132 | mod extract_struct_from_enum_variant; | 120 | mod extract_struct_from_enum_variant; |
133 | mod extract_variable; | 121 | mod extract_variable; |
134 | mod fill_match_arms; | 122 | mod fill_match_arms; |
@@ -179,6 +167,7 @@ mod handlers { | |||
179 | convert_integer_literal::convert_integer_literal, | 167 | convert_integer_literal::convert_integer_literal, |
180 | early_return::convert_to_guarded_return, | 168 | early_return::convert_to_guarded_return, |
181 | expand_glob_import::expand_glob_import, | 169 | expand_glob_import::expand_glob_import, |
170 | extract_module_to_file::extract_module_to_file, | ||
182 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 171 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |
183 | extract_variable::extract_variable, | 172 | extract_variable::extract_variable, |
184 | fill_match_arms::fill_match_arms, | 173 | fill_match_arms::fill_match_arms, |
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs index 709a34d03..21e448fb8 100644 --- a/crates/assists/src/tests.rs +++ b/crates/assists/src/tests.rs | |||
@@ -2,6 +2,7 @@ mod generated; | |||
2 | 2 | ||
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; | 4 | use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; |
5 | use ide_db::source_change::FileSystemEdit; | ||
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
6 | use syntax::TextRange; | 7 | use syntax::TextRange; |
7 | use test_utils::{assert_eq_text, extract_offset, extract_range}; | 8 | use test_utils::{assert_eq_text, extract_offset, extract_range}; |
@@ -47,25 +48,29 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
47 | let before = db.file_text(file_id).to_string(); | 48 | let before = db.file_text(file_id).to_string(); |
48 | let frange = FileRange { file_id, range: selection.into() }; | 49 | let frange = FileRange { file_id, range: selection.into() }; |
49 | 50 | ||
50 | let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange) | 51 | let assist = Assist::get(&db, &AssistConfig::default(), true, frange) |
51 | .into_iter() | 52 | .into_iter() |
52 | .find(|assist| assist.assist.id.0 == assist_id) | 53 | .find(|assist| assist.id.0 == assist_id) |
53 | .unwrap_or_else(|| { | 54 | .unwrap_or_else(|| { |
54 | panic!( | 55 | panic!( |
55 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 56 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
56 | assist_id, | 57 | assist_id, |
57 | Assist::resolved(&db, &AssistConfig::default(), frange) | 58 | Assist::get(&db, &AssistConfig::default(), false, frange) |
58 | .into_iter() | 59 | .into_iter() |
59 | .map(|assist| assist.assist.id.0) | 60 | .map(|assist| assist.id.0) |
60 | .collect::<Vec<_>>() | 61 | .collect::<Vec<_>>() |
61 | .join(", ") | 62 | .join(", ") |
62 | ) | 63 | ) |
63 | }); | 64 | }); |
64 | 65 | ||
65 | let actual = { | 66 | let actual = { |
66 | let change = assist.source_change.source_file_edits.pop().unwrap(); | 67 | let source_change = assist.source_change.unwrap(); |
67 | let mut actual = before; | 68 | let mut actual = before; |
68 | change.edit.apply(&mut actual); | 69 | for source_file_edit in source_change.source_file_edits { |
70 | if source_file_edit.file_id == file_id { | ||
71 | source_file_edit.edit.apply(&mut actual) | ||
72 | } | ||
73 | } | ||
69 | actual | 74 | actual |
70 | }; | 75 | }; |
71 | assert_eq_text!(&after, &actual); | 76 | assert_eq_text!(&after, &actual); |
@@ -86,20 +91,21 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
86 | let sema = Semantics::new(&db); | 91 | let sema = Semantics::new(&db); |
87 | let config = AssistConfig::default(); | 92 | let config = AssistConfig::default(); |
88 | let ctx = AssistContext::new(sema, &config, frange); | 93 | let ctx = AssistContext::new(sema, &config, frange); |
89 | let mut acc = Assists::new_resolved(&ctx); | 94 | let mut acc = Assists::new(&ctx, true); |
90 | handler(&mut acc, &ctx); | 95 | handler(&mut acc, &ctx); |
91 | let mut res = acc.finish_resolved(); | 96 | let mut res = acc.finish(); |
92 | 97 | ||
93 | let assist = match assist_label { | 98 | let assist = match assist_label { |
94 | Some(label) => res.into_iter().find(|resolved| resolved.assist.label == label), | 99 | Some(label) => res.into_iter().find(|resolved| resolved.label == label), |
95 | None => res.pop(), | 100 | None => res.pop(), |
96 | }; | 101 | }; |
97 | 102 | ||
98 | match (assist, expected) { | 103 | match (assist, expected) { |
99 | (Some(assist), ExpectedResult::After(after)) => { | 104 | (Some(assist), ExpectedResult::After(after)) => { |
100 | let mut source_change = assist.source_change; | 105 | let mut source_change = assist.source_change.unwrap(); |
101 | assert!(!source_change.source_file_edits.is_empty()); | 106 | assert!(!source_change.source_file_edits.is_empty()); |
102 | let skip_header = source_change.source_file_edits.len() == 1; | 107 | let skip_header = source_change.source_file_edits.len() == 1 |
108 | && source_change.file_system_edits.len() == 0; | ||
103 | source_change.source_file_edits.sort_by_key(|it| it.file_id); | 109 | source_change.source_file_edits.sort_by_key(|it| it.file_id); |
104 | 110 | ||
105 | let mut buf = String::new(); | 111 | let mut buf = String::new(); |
@@ -115,10 +121,25 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
115 | buf.push_str(&text); | 121 | buf.push_str(&text); |
116 | } | 122 | } |
117 | 123 | ||
124 | for file_system_edit in source_change.file_system_edits.clone() { | ||
125 | match file_system_edit { | ||
126 | FileSystemEdit::CreateFile { dst, initial_contents } => { | ||
127 | let sr = db.file_source_root(dst.anchor); | ||
128 | let sr = db.source_root(sr); | ||
129 | let mut base = sr.path_for_file(&dst.anchor).unwrap().clone(); | ||
130 | base.pop(); | ||
131 | let created_file_path = format!("{}{}", base.to_string(), &dst.path[1..]); | ||
132 | format_to!(buf, "//- {}\n", created_file_path); | ||
133 | buf.push_str(&initial_contents); | ||
134 | } | ||
135 | _ => (), | ||
136 | } | ||
137 | } | ||
138 | |||
118 | assert_eq_text!(after, &buf); | 139 | assert_eq_text!(after, &buf); |
119 | } | 140 | } |
120 | (Some(assist), ExpectedResult::Target(target)) => { | 141 | (Some(assist), ExpectedResult::Target(target)) => { |
121 | let range = assist.assist.target; | 142 | let range = assist.target; |
122 | assert_eq_text!(&text_without_caret[range], target); | 143 | assert_eq_text!(&text_without_caret[range], target); |
123 | } | 144 | } |
124 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), | 145 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), |
@@ -135,14 +156,11 @@ fn assist_order_field_struct() { | |||
135 | let (before_cursor_pos, before) = extract_offset(before); | 156 | let (before_cursor_pos, before) = extract_offset(before); |
136 | let (db, file_id) = with_single_file(&before); | 157 | let (db, file_id) = with_single_file(&before); |
137 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; | 158 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; |
138 | let assists = Assist::resolved(&db, &AssistConfig::default(), frange); | 159 | let assists = Assist::get(&db, &AssistConfig::default(), false, frange); |
139 | let mut assists = assists.iter(); | 160 | let mut assists = assists.iter(); |
140 | 161 | ||
141 | assert_eq!( | 162 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
142 | assists.next().expect("expected assist").assist.label, | 163 | assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); |
143 | "Change visibility to pub(crate)" | ||
144 | ); | ||
145 | assert_eq!(assists.next().expect("expected assist").assist.label, "Add `#[derive]`"); | ||
146 | } | 164 | } |
147 | 165 | ||
148 | #[test] | 166 | #[test] |
@@ -158,11 +176,11 @@ fn assist_order_if_expr() { | |||
158 | let (range, before) = extract_range(before); | 176 | let (range, before) = extract_range(before); |
159 | let (db, file_id) = with_single_file(&before); | 177 | let (db, file_id) = with_single_file(&before); |
160 | let frange = FileRange { file_id, range }; | 178 | let frange = FileRange { file_id, range }; |
161 | let assists = Assist::resolved(&db, &AssistConfig::default(), frange); | 179 | let assists = Assist::get(&db, &AssistConfig::default(), false, frange); |
162 | let mut assists = assists.iter(); | 180 | let mut assists = assists.iter(); |
163 | 181 | ||
164 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 182 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
165 | assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); | 183 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); |
166 | } | 184 | } |
167 | 185 | ||
168 | #[test] | 186 | #[test] |
@@ -183,27 +201,27 @@ fn assist_filter_works() { | |||
183 | let mut cfg = AssistConfig::default(); | 201 | let mut cfg = AssistConfig::default(); |
184 | cfg.allowed = Some(vec![AssistKind::Refactor]); | 202 | cfg.allowed = Some(vec![AssistKind::Refactor]); |
185 | 203 | ||
186 | let assists = Assist::resolved(&db, &cfg, frange); | 204 | let assists = Assist::get(&db, &cfg, false, frange); |
187 | let mut assists = assists.iter(); | 205 | let mut assists = assists.iter(); |
188 | 206 | ||
189 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 207 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
190 | assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); | 208 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); |
191 | } | 209 | } |
192 | 210 | ||
193 | { | 211 | { |
194 | let mut cfg = AssistConfig::default(); | 212 | let mut cfg = AssistConfig::default(); |
195 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 213 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
196 | let assists = Assist::resolved(&db, &cfg, frange); | 214 | let assists = Assist::get(&db, &cfg, false, frange); |
197 | assert_eq!(assists.len(), 1); | 215 | assert_eq!(assists.len(), 1); |
198 | 216 | ||
199 | let mut assists = assists.iter(); | 217 | let mut assists = assists.iter(); |
200 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 218 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
201 | } | 219 | } |
202 | 220 | ||
203 | { | 221 | { |
204 | let mut cfg = AssistConfig::default(); | 222 | let mut cfg = AssistConfig::default(); |
205 | cfg.allowed = Some(vec![AssistKind::QuickFix]); | 223 | cfg.allowed = Some(vec![AssistKind::QuickFix]); |
206 | let assists = Assist::resolved(&db, &cfg, frange); | 224 | let assists = Assist::get(&db, &cfg, false, frange); |
207 | assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); | 225 | assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); |
208 | } | 226 | } |
209 | } | 227 | } |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index cc7c4a343..d3dfe24e7 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -42,26 +42,26 @@ fn doctest_add_impl_default_members() { | |||
42 | "add_impl_default_members", | 42 | "add_impl_default_members", |
43 | r#####" | 43 | r#####" |
44 | trait Trait { | 44 | trait Trait { |
45 | Type X; | 45 | type X; |
46 | fn foo(&self); | 46 | fn foo(&self); |
47 | fn bar(&self) {} | 47 | fn bar(&self) {} |
48 | } | 48 | } |
49 | 49 | ||
50 | impl Trait for () { | 50 | impl Trait for () { |
51 | Type X = (); | 51 | type X = (); |
52 | fn foo(&self) {}<|> | 52 | fn foo(&self) {}<|> |
53 | 53 | ||
54 | } | 54 | } |
55 | "#####, | 55 | "#####, |
56 | r#####" | 56 | r#####" |
57 | trait Trait { | 57 | trait Trait { |
58 | Type X; | 58 | type X; |
59 | fn foo(&self); | 59 | fn foo(&self); |
60 | fn bar(&self) {} | 60 | fn bar(&self) {} |
61 | } | 61 | } |
62 | 62 | ||
63 | impl Trait for () { | 63 | impl Trait for () { |
64 | Type X = (); | 64 | type X = (); |
65 | fn foo(&self) {} | 65 | fn foo(&self) {} |
66 | 66 | ||
67 | $0fn bar(&self) {} | 67 | $0fn bar(&self) {} |
@@ -76,7 +76,7 @@ fn doctest_add_impl_missing_members() { | |||
76 | "add_impl_missing_members", | 76 | "add_impl_missing_members", |
77 | r#####" | 77 | r#####" |
78 | trait Trait<T> { | 78 | trait Trait<T> { |
79 | Type X; | 79 | type X; |
80 | fn foo(&self) -> T; | 80 | fn foo(&self) -> T; |
81 | fn bar(&self) {} | 81 | fn bar(&self) {} |
82 | } | 82 | } |
@@ -87,14 +87,16 @@ impl Trait<u32> for () {<|> | |||
87 | "#####, | 87 | "#####, |
88 | r#####" | 88 | r#####" |
89 | trait Trait<T> { | 89 | trait Trait<T> { |
90 | Type X; | 90 | type X; |
91 | fn foo(&self) -> T; | 91 | fn foo(&self) -> T; |
92 | fn bar(&self) {} | 92 | fn bar(&self) {} |
93 | } | 93 | } |
94 | 94 | ||
95 | impl Trait<u32> for () { | 95 | impl Trait<u32> for () { |
96 | $0type X; | ||
97 | |||
96 | fn foo(&self) -> u32 { | 98 | fn foo(&self) -> u32 { |
97 | ${0:todo!()} | 99 | todo!() |
98 | } | 100 | } |
99 | } | 101 | } |
100 | "#####, | 102 | "#####, |
@@ -236,6 +238,21 @@ fn qux(bar: Bar, baz: Baz) {} | |||
236 | } | 238 | } |
237 | 239 | ||
238 | #[test] | 240 | #[test] |
241 | fn doctest_extract_module_to_file() { | ||
242 | check_doc_test( | ||
243 | "extract_module_to_file", | ||
244 | r#####" | ||
245 | mod foo {<|> | ||
246 | fn t() {} | ||
247 | } | ||
248 | "#####, | ||
249 | r#####" | ||
250 | mod foo; | ||
251 | "#####, | ||
252 | ) | ||
253 | } | ||
254 | |||
255 | #[test] | ||
239 | fn doctest_extract_struct_from_enum_variant() { | 256 | fn doctest_extract_struct_from_enum_variant() { |
240 | check_doc_test( | 257 | check_doc_test( |
241 | "extract_struct_from_enum_variant", | 258 | "extract_struct_from_enum_variant", |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index f2cacf7c8..d41084b59 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -232,7 +232,13 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
232 | }; | 232 | }; |
233 | Some(make::expr_method_call(receiver, method, arg_list)) | 233 | Some(make::expr_method_call(receiver, method, arg_list)) |
234 | } | 234 | } |
235 | ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(), | 235 | ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => { |
236 | if let ast::Expr::ParenExpr(parexpr) = pe.expr()? { | ||
237 | parexpr.expr() | ||
238 | } else { | ||
239 | pe.expr() | ||
240 | } | ||
241 | } | ||
236 | // FIXME: | 242 | // FIXME: |
237 | // ast::Expr::Literal(true | false ) | 243 | // ast::Expr::Literal(true | false ) |
238 | _ => None, | 244 | _ => None, |
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs index 1ef6b5f48..d9fe13485 100644 --- a/crates/completion/src/completions.rs +++ b/crates/completion/src/completions.rs | |||
@@ -19,9 +19,14 @@ use hir::{ModPath, ScopeDef, Type}; | |||
19 | use crate::{ | 19 | use crate::{ |
20 | item::Builder, | 20 | item::Builder, |
21 | render::{ | 21 | render::{ |
22 | const_::render_const, enum_variant::render_variant, function::render_fn, | 22 | const_::render_const, |
23 | macro_::render_macro, render_field, render_resolution, render_tuple_field, | 23 | enum_variant::render_variant, |
24 | type_alias::render_type_alias, RenderContext, | 24 | function::render_fn, |
25 | macro_::render_macro, | ||
26 | pattern::{render_struct_pat, render_variant_pat}, | ||
27 | render_field, render_resolution, render_tuple_field, | ||
28 | type_alias::render_type_alias, | ||
29 | RenderContext, | ||
25 | }, | 30 | }, |
26 | CompletionContext, CompletionItem, | 31 | CompletionContext, CompletionItem, |
27 | }; | 32 | }; |
@@ -105,6 +110,28 @@ impl Completions { | |||
105 | self.add(item) | 110 | self.add(item) |
106 | } | 111 | } |
107 | 112 | ||
113 | pub(crate) fn add_variant_pat( | ||
114 | &mut self, | ||
115 | ctx: &CompletionContext, | ||
116 | variant: hir::Variant, | ||
117 | local_name: Option<hir::Name>, | ||
118 | ) { | ||
119 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name) { | ||
120 | self.add(item); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | pub(crate) fn add_struct_pat( | ||
125 | &mut self, | ||
126 | ctx: &CompletionContext, | ||
127 | strukt: hir::Struct, | ||
128 | local_name: Option<hir::Name>, | ||
129 | ) { | ||
130 | if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) { | ||
131 | self.add(item); | ||
132 | } | ||
133 | } | ||
134 | |||
108 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { | 135 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { |
109 | if let Some(item) = render_const(RenderContext::new(ctx), constant) { | 136 | if let Some(item) = render_const(RenderContext::new(ctx), constant) { |
110 | self.add(item); | 137 | self.add(item); |
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs index 4d56731ec..eee31098d 100644 --- a/crates/completion/src/completions/pattern.rs +++ b/crates/completion/src/completions/pattern.rs | |||
@@ -2,9 +2,9 @@ | |||
2 | 2 | ||
3 | use crate::{CompletionContext, Completions}; | 3 | use crate::{CompletionContext, Completions}; |
4 | 4 | ||
5 | /// Completes constats and paths in patterns. | 5 | /// Completes constants and paths in patterns. |
6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
7 | if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_let_pat_binding) { | 7 | if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) { |
8 | return; | 8 | return; |
9 | } | 9 | } |
10 | if ctx.record_pat_syntax.is_some() { | 10 | if ctx.record_pat_syntax.is_some() { |
@@ -15,20 +15,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
15 | // suggest variants + auto-imports | 15 | // suggest variants + auto-imports |
16 | ctx.scope.process_all_names(&mut |name, res| { | 16 | ctx.scope.process_all_names(&mut |name, res| { |
17 | let add_resolution = match &res { | 17 | let add_resolution = match &res { |
18 | hir::ScopeDef::ModuleDef(def) => { | 18 | hir::ScopeDef::ModuleDef(def) => match def { |
19 | if ctx.is_irrefutable_let_pat_binding { | 19 | hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { |
20 | matches!(def, hir::ModuleDef::Adt(hir::Adt::Struct(_))) | 20 | acc.add_struct_pat(ctx, strukt.clone(), Some(name.clone())); |
21 | } else { | 21 | true |
22 | matches!( | ||
23 | def, | ||
24 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | ||
25 | | hir::ModuleDef::Adt(hir::Adt::Struct(..)) | ||
26 | | hir::ModuleDef::Variant(..) | ||
27 | | hir::ModuleDef::Const(..) | ||
28 | | hir::ModuleDef::Module(..) | ||
29 | ) | ||
30 | } | 22 | } |
31 | } | 23 | hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => { |
24 | acc.add_variant_pat(ctx, variant.clone(), Some(name.clone())); | ||
25 | true | ||
26 | } | ||
27 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | ||
28 | | hir::ModuleDef::Variant(..) | ||
29 | | hir::ModuleDef::Const(..) | ||
30 | | hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding, | ||
31 | _ => false, | ||
32 | }, | ||
32 | hir::ScopeDef::MacroDef(_) => true, | 33 | hir::ScopeDef::MacroDef(_) => true, |
33 | _ => false, | 34 | _ => false, |
34 | }; | 35 | }; |
@@ -42,13 +43,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
42 | mod tests { | 43 | mod tests { |
43 | use expect_test::{expect, Expect}; | 44 | use expect_test::{expect, Expect}; |
44 | 45 | ||
45 | use crate::{test_utils::completion_list, CompletionKind}; | 46 | use crate::{ |
47 | test_utils::{check_edit, completion_list}, | ||
48 | CompletionKind, | ||
49 | }; | ||
46 | 50 | ||
47 | fn check(ra_fixture: &str, expect: Expect) { | 51 | fn check(ra_fixture: &str, expect: Expect) { |
48 | let actual = completion_list(ra_fixture, CompletionKind::Reference); | 52 | let actual = completion_list(ra_fixture, CompletionKind::Reference); |
49 | expect.assert_eq(&actual) | 53 | expect.assert_eq(&actual) |
50 | } | 54 | } |
51 | 55 | ||
56 | fn check_snippet(ra_fixture: &str, expect: Expect) { | ||
57 | let actual = completion_list(ra_fixture, CompletionKind::Snippet); | ||
58 | expect.assert_eq(&actual) | ||
59 | } | ||
60 | |||
52 | #[test] | 61 | #[test] |
53 | fn completes_enum_variants_and_modules() { | 62 | fn completes_enum_variants_and_modules() { |
54 | check( | 63 | check( |
@@ -69,7 +78,7 @@ fn foo() { | |||
69 | en E | 78 | en E |
70 | ct Z | 79 | ct Z |
71 | st Bar | 80 | st Bar |
72 | ev X () | 81 | ev X |
73 | md m | 82 | md m |
74 | "#]], | 83 | "#]], |
75 | ); | 84 | ); |
@@ -114,4 +123,139 @@ fn foo() { | |||
114 | "#]], | 123 | "#]], |
115 | ); | 124 | ); |
116 | } | 125 | } |
126 | |||
127 | #[test] | ||
128 | fn completes_in_param() { | ||
129 | check( | ||
130 | r#" | ||
131 | enum E { X } | ||
132 | |||
133 | static FOO: E = E::X; | ||
134 | struct Bar { f: u32 } | ||
135 | |||
136 | fn foo(<|>) { | ||
137 | } | ||
138 | "#, | ||
139 | expect![[r#" | ||
140 | st Bar | ||
141 | "#]], | ||
142 | ); | ||
143 | } | ||
144 | |||
145 | #[test] | ||
146 | fn completes_pat_in_let() { | ||
147 | check_snippet( | ||
148 | r#" | ||
149 | struct Bar { f: u32 } | ||
150 | |||
151 | fn foo() { | ||
152 | let <|> | ||
153 | } | ||
154 | "#, | ||
155 | expect![[r#" | ||
156 | bn Bar Bar { f$1 }$0 | ||
157 | "#]], | ||
158 | ); | ||
159 | } | ||
160 | |||
161 | #[test] | ||
162 | fn completes_param_pattern() { | ||
163 | check_snippet( | ||
164 | r#" | ||
165 | struct Foo { bar: String, baz: String } | ||
166 | struct Bar(String, String); | ||
167 | struct Baz; | ||
168 | fn outer(<|>) {} | ||
169 | "#, | ||
170 | expect![[r#" | ||
171 | bn Foo Foo { bar$1, baz$2 }: Foo$0 | ||
172 | bn Bar Bar($1, $2): Bar$0 | ||
173 | "#]], | ||
174 | ) | ||
175 | } | ||
176 | |||
177 | #[test] | ||
178 | fn completes_let_pattern() { | ||
179 | check_snippet( | ||
180 | r#" | ||
181 | struct Foo { bar: String, baz: String } | ||
182 | struct Bar(String, String); | ||
183 | struct Baz; | ||
184 | fn outer() { | ||
185 | let <|> | ||
186 | } | ||
187 | "#, | ||
188 | expect![[r#" | ||
189 | bn Foo Foo { bar$1, baz$2 }$0 | ||
190 | bn Bar Bar($1, $2)$0 | ||
191 | "#]], | ||
192 | ) | ||
193 | } | ||
194 | |||
195 | #[test] | ||
196 | fn completes_refutable_pattern() { | ||
197 | check_snippet( | ||
198 | r#" | ||
199 | struct Foo { bar: i32, baz: i32 } | ||
200 | struct Bar(String, String); | ||
201 | struct Baz; | ||
202 | fn outer() { | ||
203 | match () { | ||
204 | <|> | ||
205 | } | ||
206 | } | ||
207 | "#, | ||
208 | expect![[r#" | ||
209 | bn Foo Foo { bar$1, baz$2 }$0 | ||
210 | bn Bar Bar($1, $2)$0 | ||
211 | "#]], | ||
212 | ) | ||
213 | } | ||
214 | |||
215 | #[test] | ||
216 | fn omits_private_fields_pat() { | ||
217 | check_snippet( | ||
218 | r#" | ||
219 | mod foo { | ||
220 | pub struct Foo { pub bar: i32, baz: i32 } | ||
221 | pub struct Bar(pub String, String); | ||
222 | pub struct Invisible(String, String); | ||
223 | } | ||
224 | use foo::*; | ||
225 | |||
226 | fn outer() { | ||
227 | match () { | ||
228 | <|> | ||
229 | } | ||
230 | } | ||
231 | "#, | ||
232 | expect![[r#" | ||
233 | bn Foo Foo { bar$1, .. }$0 | ||
234 | bn Bar Bar($1, ..)$0 | ||
235 | "#]], | ||
236 | ) | ||
237 | } | ||
238 | |||
239 | #[test] | ||
240 | fn only_shows_ident_completion() { | ||
241 | check_edit( | ||
242 | "Foo", | ||
243 | r#" | ||
244 | struct Foo(i32); | ||
245 | fn main() { | ||
246 | match Foo(92) { | ||
247 | <|>(92) => (), | ||
248 | } | ||
249 | } | ||
250 | "#, | ||
251 | r#" | ||
252 | struct Foo(i32); | ||
253 | fn main() { | ||
254 | match Foo(92) { | ||
255 | Foo(92) => (), | ||
256 | } | ||
257 | } | ||
258 | "#, | ||
259 | ); | ||
260 | } | ||
117 | } | 261 | } |
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs index 1300f00b2..882c4dcbc 100644 --- a/crates/completion/src/completions/qualified_path.rs +++ b/crates/completion/src/completions/qualified_path.rs | |||
@@ -118,6 +118,12 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
118 | _ => return, | 118 | _ => return, |
119 | }; | 119 | }; |
120 | 120 | ||
121 | if let Some(Adt::Enum(e)) = ty.as_adt() { | ||
122 | for variant in e.variants(ctx.db) { | ||
123 | acc.add_enum_variant(ctx, variant, None); | ||
124 | } | ||
125 | } | ||
126 | |||
121 | let traits_in_scope = ctx.scope.traits_in_scope(); | 127 | let traits_in_scope = ctx.scope.traits_in_scope(); |
122 | let mut seen = FxHashSet::default(); | 128 | let mut seen = FxHashSet::default(); |
123 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | 129 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { |
@@ -752,4 +758,27 @@ fn main() { | |||
752 | "#]], | 758 | "#]], |
753 | ); | 759 | ); |
754 | } | 760 | } |
761 | |||
762 | #[test] | ||
763 | fn completes_self_enum() { | ||
764 | check( | ||
765 | r#" | ||
766 | enum Foo { | ||
767 | Bar, | ||
768 | Baz, | ||
769 | } | ||
770 | |||
771 | impl Foo { | ||
772 | fn foo(self) { | ||
773 | Self::<|> | ||
774 | } | ||
775 | } | ||
776 | "#, | ||
777 | expect![[r#" | ||
778 | ev Bar () | ||
779 | ev Baz () | ||
780 | me foo(…) fn foo(self) | ||
781 | "#]], | ||
782 | ); | ||
783 | } | ||
755 | } | 784 | } |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 099ffb4d4..d09849752 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use std::iter; | ||
4 | |||
3 | use either::Either; | 5 | use either::Either; |
4 | use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type}; | 6 | use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type}; |
5 | use ide_db::helpers::insert_use::ImportScope; | 7 | use ide_db::helpers::insert_use::ImportScope; |
@@ -50,7 +52,9 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
50 | } | 52 | } |
51 | 53 | ||
52 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { | 54 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { |
53 | if let Some(Adt::Enum(enum_data)) = ty.as_adt() { | 55 | if let Some(Adt::Enum(enum_data)) = |
56 | iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) | ||
57 | { | ||
54 | let variants = enum_data.variants(ctx.db); | 58 | let variants = enum_data.variants(ctx.db); |
55 | 59 | ||
56 | let module = if let Some(module) = ctx.scope.module() { | 60 | let module = if let Some(module) = ctx.scope.module() { |
@@ -701,6 +705,7 @@ fn main() { <|> } | |||
701 | "#]], | 705 | "#]], |
702 | ); | 706 | ); |
703 | } | 707 | } |
708 | |||
704 | #[test] | 709 | #[test] |
705 | fn completes_enum_variant_matcharm() { | 710 | fn completes_enum_variant_matcharm() { |
706 | check( | 711 | check( |
@@ -722,6 +727,26 @@ fn main() { | |||
722 | } | 727 | } |
723 | 728 | ||
724 | #[test] | 729 | #[test] |
730 | fn completes_enum_variant_matcharm_ref() { | ||
731 | check( | ||
732 | r#" | ||
733 | enum Foo { Bar, Baz, Quux } | ||
734 | |||
735 | fn main() { | ||
736 | let foo = Foo::Quux; | ||
737 | match &foo { Qu<|> } | ||
738 | } | ||
739 | "#, | ||
740 | expect![[r#" | ||
741 | ev Foo::Bar () | ||
742 | ev Foo::Baz () | ||
743 | ev Foo::Quux () | ||
744 | en Foo | ||
745 | "#]], | ||
746 | ) | ||
747 | } | ||
748 | |||
749 | #[test] | ||
725 | fn completes_enum_variant_iflet() { | 750 | fn completes_enum_variant_iflet() { |
726 | check( | 751 | check( |
727 | r#" | 752 | r#" |
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs index 5cd11cf77..41de324d8 100644 --- a/crates/completion/src/context.rs +++ b/crates/completion/src/context.rs | |||
@@ -51,7 +51,7 @@ pub(crate) struct CompletionContext<'a> { | |||
51 | /// If a name-binding or reference to a const in a pattern. | 51 | /// If a name-binding or reference to a const in a pattern. |
52 | /// Irrefutable patterns (like let) are excluded. | 52 | /// Irrefutable patterns (like let) are excluded. |
53 | pub(super) is_pat_binding_or_const: bool, | 53 | pub(super) is_pat_binding_or_const: bool, |
54 | pub(super) is_irrefutable_let_pat_binding: bool, | 54 | pub(super) is_irrefutable_pat_binding: bool, |
55 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 55 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
56 | pub(super) is_trivial_path: bool, | 56 | pub(super) is_trivial_path: bool, |
57 | /// If not a trivial path, the prefix (qualifier). | 57 | /// If not a trivial path, the prefix (qualifier). |
@@ -147,7 +147,7 @@ impl<'a> CompletionContext<'a> { | |||
147 | active_parameter: ActiveParameter::at(db, position), | 147 | active_parameter: ActiveParameter::at(db, position), |
148 | is_param: false, | 148 | is_param: false, |
149 | is_pat_binding_or_const: false, | 149 | is_pat_binding_or_const: false, |
150 | is_irrefutable_let_pat_binding: false, | 150 | is_irrefutable_pat_binding: false, |
151 | is_trivial_path: false, | 151 | is_trivial_path: false, |
152 | path_qual: None, | 152 | path_qual: None, |
153 | after_if: false, | 153 | after_if: false, |
@@ -327,14 +327,19 @@ impl<'a> CompletionContext<'a> { | |||
327 | if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { | 327 | if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { |
328 | self.is_pat_binding_or_const = false; | 328 | self.is_pat_binding_or_const = false; |
329 | } | 329 | } |
330 | if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) { | 330 | if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| { |
331 | if let Some(pat) = let_stmt.pat() { | 331 | match_ast! { |
332 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) | 332 | match node { |
333 | { | 333 | ast::LetStmt(it) => Some(it.pat()), |
334 | self.is_pat_binding_or_const = false; | 334 | ast::Param(it) => Some(it.pat()), |
335 | self.is_irrefutable_let_pat_binding = true; | 335 | _ => None, |
336 | } | 336 | } |
337 | } | 337 | } |
338 | }) { | ||
339 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { | ||
340 | self.is_pat_binding_or_const = false; | ||
341 | self.is_irrefutable_pat_binding = true; | ||
342 | } | ||
338 | } | 343 | } |
339 | } | 344 | } |
340 | if is_node::<ast::Param>(name.syntax()) { | 345 | if is_node::<ast::Param>(name.syntax()) { |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 1092a4825..1ba7201a1 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -5,6 +5,7 @@ pub(crate) mod macro_; | |||
5 | pub(crate) mod function; | 5 | pub(crate) mod function; |
6 | pub(crate) mod enum_variant; | 6 | pub(crate) mod enum_variant; |
7 | pub(crate) mod const_; | 7 | pub(crate) mod const_; |
8 | pub(crate) mod pattern; | ||
8 | pub(crate) mod type_alias; | 9 | pub(crate) mod type_alias; |
9 | 10 | ||
10 | mod builder_ext; | 11 | mod builder_ext; |
@@ -159,6 +160,12 @@ impl<'a> Render<'a> { | |||
159 | let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); | 160 | let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); |
160 | return Some(item); | 161 | return Some(item); |
161 | } | 162 | } |
163 | ScopeDef::ModuleDef(Variant(_)) | ||
164 | if self.ctx.completion.is_pat_binding_or_const | ||
165 | | self.ctx.completion.is_irrefutable_pat_binding => | ||
166 | { | ||
167 | CompletionItemKind::EnumVariant | ||
168 | } | ||
162 | ScopeDef::ModuleDef(Variant(var)) => { | 169 | ScopeDef::ModuleDef(Variant(var)) => { |
163 | let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); | 170 | let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); |
164 | return Some(item); | 171 | return Some(item); |
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs index ce8718bd5..d053a988b 100644 --- a/crates/completion/src/render/builder_ext.rs +++ b/crates/completion/src/render/builder_ext.rs | |||
@@ -34,7 +34,6 @@ impl Builder { | |||
34 | return false; | 34 | return false; |
35 | } | 35 | } |
36 | if ctx.is_pattern_call { | 36 | if ctx.is_pattern_call { |
37 | mark::hit!(dont_duplicate_pattern_parens); | ||
38 | return false; | 37 | return false; |
39 | } | 38 | } |
40 | if ctx.is_call { | 39 | if ctx.is_call { |
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index 7176fd9b3..732e139ec 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs | |||
@@ -126,50 +126,5 @@ fn main() -> Option<i32> { | |||
126 | } | 126 | } |
127 | "#, | 127 | "#, |
128 | ); | 128 | ); |
129 | check_edit( | ||
130 | "Some", | ||
131 | r#" | ||
132 | enum Option<T> { Some(T), None } | ||
133 | use Option::*; | ||
134 | fn main(value: Option<i32>) { | ||
135 | match value { | ||
136 | Som<|> | ||
137 | } | ||
138 | } | ||
139 | "#, | ||
140 | r#" | ||
141 | enum Option<T> { Some(T), None } | ||
142 | use Option::*; | ||
143 | fn main(value: Option<i32>) { | ||
144 | match value { | ||
145 | Some($0) | ||
146 | } | ||
147 | } | ||
148 | "#, | ||
149 | ); | ||
150 | } | ||
151 | |||
152 | #[test] | ||
153 | fn dont_duplicate_pattern_parens() { | ||
154 | mark::check!(dont_duplicate_pattern_parens); | ||
155 | check_edit( | ||
156 | "Var", | ||
157 | r#" | ||
158 | enum E { Var(i32) } | ||
159 | fn main() { | ||
160 | match E::Var(92) { | ||
161 | E::<|>(92) => (), | ||
162 | } | ||
163 | } | ||
164 | "#, | ||
165 | r#" | ||
166 | enum E { Var(i32) } | ||
167 | fn main() { | ||
168 | match E::Var(92) { | ||
169 | E::Var(92) => (), | ||
170 | } | ||
171 | } | ||
172 | "#, | ||
173 | ); | ||
174 | } | 129 | } |
175 | } | 130 | } |
diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs new file mode 100644 index 000000000..a3b6a3cac --- /dev/null +++ b/crates/completion/src/render/pattern.rs | |||
@@ -0,0 +1,148 @@ | |||
1 | //! Renderer for patterns. | ||
2 | |||
3 | use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind}; | ||
4 | use itertools::Itertools; | ||
5 | |||
6 | use crate::{ | ||
7 | config::SnippetCap, item::CompletionKind, render::RenderContext, CompletionItem, | ||
8 | CompletionItemKind, | ||
9 | }; | ||
10 | |||
11 | fn visible_fields( | ||
12 | ctx: &RenderContext<'_>, | ||
13 | fields: &[hir::Field], | ||
14 | item: impl HasAttrs, | ||
15 | ) -> Option<(Vec<hir::Field>, bool)> { | ||
16 | let module = ctx.completion.scope.module()?; | ||
17 | let n_fields = fields.len(); | ||
18 | let fields = fields | ||
19 | .into_iter() | ||
20 | .filter(|field| field.is_visible_from(ctx.db(), module)) | ||
21 | .copied() | ||
22 | .collect::<Vec<_>>(); | ||
23 | |||
24 | let fields_omitted = | ||
25 | n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists(); | ||
26 | Some((fields, fields_omitted)) | ||
27 | } | ||
28 | |||
29 | pub(crate) fn render_struct_pat( | ||
30 | ctx: RenderContext<'_>, | ||
31 | strukt: hir::Struct, | ||
32 | local_name: Option<Name>, | ||
33 | ) -> Option<CompletionItem> { | ||
34 | let _p = profile::span("render_struct_pat"); | ||
35 | |||
36 | let fields = strukt.fields(ctx.db()); | ||
37 | let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?; | ||
38 | |||
39 | if visible_fields.is_empty() { | ||
40 | // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields | ||
41 | return None; | ||
42 | } | ||
43 | |||
44 | let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string(); | ||
45 | let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?; | ||
46 | |||
47 | Some(build_completion(ctx, name, pat, strukt)) | ||
48 | } | ||
49 | |||
50 | pub(crate) fn render_variant_pat( | ||
51 | ctx: RenderContext<'_>, | ||
52 | variant: hir::Variant, | ||
53 | local_name: Option<Name>, | ||
54 | ) -> Option<CompletionItem> { | ||
55 | let _p = profile::span("render_variant_pat"); | ||
56 | |||
57 | let fields = variant.fields(ctx.db()); | ||
58 | let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?; | ||
59 | |||
60 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); | ||
61 | let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?; | ||
62 | |||
63 | Some(build_completion(ctx, name, pat, variant)) | ||
64 | } | ||
65 | |||
66 | fn build_completion( | ||
67 | ctx: RenderContext<'_>, | ||
68 | name: String, | ||
69 | pat: String, | ||
70 | item: impl HasAttrs + Copy, | ||
71 | ) -> CompletionItem { | ||
72 | let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) | ||
73 | .kind(CompletionItemKind::Binding) | ||
74 | .set_documentation(ctx.docs(item)) | ||
75 | .set_deprecated(ctx.is_deprecated(item)) | ||
76 | .detail(&pat); | ||
77 | let completion = if let Some(snippet_cap) = ctx.snippet_cap() { | ||
78 | completion.insert_snippet(snippet_cap, pat) | ||
79 | } else { | ||
80 | completion.insert_text(pat) | ||
81 | }; | ||
82 | completion.build() | ||
83 | } | ||
84 | |||
85 | fn render_pat( | ||
86 | ctx: &RenderContext<'_>, | ||
87 | name: &str, | ||
88 | kind: StructKind, | ||
89 | fields: &[hir::Field], | ||
90 | fields_omitted: bool, | ||
91 | ) -> Option<String> { | ||
92 | let mut pat = match kind { | ||
93 | StructKind::Tuple if ctx.snippet_cap().is_some() => { | ||
94 | render_tuple_as_pat(&fields, &name, fields_omitted) | ||
95 | } | ||
96 | StructKind::Record => { | ||
97 | render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted) | ||
98 | } | ||
99 | _ => return None, | ||
100 | }; | ||
101 | |||
102 | if ctx.completion.is_param { | ||
103 | pat.push(':'); | ||
104 | pat.push(' '); | ||
105 | pat.push_str(&name); | ||
106 | } | ||
107 | if ctx.snippet_cap().is_some() { | ||
108 | pat.push_str("$0"); | ||
109 | } | ||
110 | Some(pat) | ||
111 | } | ||
112 | |||
113 | fn render_record_as_pat( | ||
114 | db: &dyn HirDatabase, | ||
115 | snippet_cap: Option<SnippetCap>, | ||
116 | fields: &[hir::Field], | ||
117 | name: &str, | ||
118 | fields_omitted: bool, | ||
119 | ) -> String { | ||
120 | let fields = fields.iter(); | ||
121 | if snippet_cap.is_some() { | ||
122 | format!( | ||
123 | "{name} {{ {}{} }}", | ||
124 | fields | ||
125 | .enumerate() | ||
126 | .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1)) | ||
127 | .format(", "), | ||
128 | if fields_omitted { ", .." } else { "" }, | ||
129 | name = name | ||
130 | ) | ||
131 | } else { | ||
132 | format!( | ||
133 | "{name} {{ {}{} }}", | ||
134 | fields.map(|field| field.name(db)).format(", "), | ||
135 | if fields_omitted { ", .." } else { "" }, | ||
136 | name = name | ||
137 | ) | ||
138 | } | ||
139 | } | ||
140 | |||
141 | fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String { | ||
142 | format!( | ||
143 | "{name}({}{})", | ||
144 | fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "), | ||
145 | if fields_omitted { ", .." } else { "" }, | ||
146 | name = name | ||
147 | ) | ||
148 | } | ||
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index d6c7e71ea..b7ded3478 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -9,7 +9,7 @@ use hir_def::{ | |||
9 | adt::StructKind, | 9 | adt::StructKind, |
10 | adt::VariantData, | 10 | adt::VariantData, |
11 | builtin_type::BuiltinType, | 11 | builtin_type::BuiltinType, |
12 | expr::{BindingAnnotation, Pat, PatId}, | 12 | expr::{BindingAnnotation, LabelId, Pat, PatId}, |
13 | import_map, | 13 | import_map, |
14 | item_tree::ItemTreeNode, | 14 | item_tree::ItemTreeNode, |
15 | lang_item::LangItemTarget, | 15 | lang_item::LangItemTarget, |
@@ -374,8 +374,6 @@ impl Module { | |||
374 | let crate_def_map = db.crate_def_map(self.id.krate); | 374 | let crate_def_map = db.crate_def_map(self.id.krate); |
375 | crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); | 375 | crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); |
376 | for decl in self.declarations(db) { | 376 | for decl in self.declarations(db) { |
377 | decl.diagnostics(db, sink); | ||
378 | |||
379 | match decl { | 377 | match decl { |
380 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | 378 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), |
381 | crate::ModuleDef::Module(m) => { | 379 | crate::ModuleDef::Module(m) => { |
@@ -384,7 +382,9 @@ impl Module { | |||
384 | m.diagnostics(db, sink) | 382 | m.diagnostics(db, sink) |
385 | } | 383 | } |
386 | } | 384 | } |
387 | _ => (), | 385 | _ => { |
386 | decl.diagnostics(db, sink); | ||
387 | } | ||
388 | } | 388 | } |
389 | } | 389 | } |
390 | 390 | ||
@@ -511,6 +511,10 @@ impl Struct { | |||
511 | db.struct_data(self.id).repr.clone() | 511 | db.struct_data(self.id).repr.clone() |
512 | } | 512 | } |
513 | 513 | ||
514 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
515 | self.variant_data(db).kind() | ||
516 | } | ||
517 | |||
514 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | 518 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { |
515 | db.struct_data(self.id).variant_data.clone() | 519 | db.struct_data(self.id).variant_data.clone() |
516 | } | 520 | } |
@@ -1202,6 +1206,34 @@ impl Local { | |||
1202 | } | 1206 | } |
1203 | 1207 | ||
1204 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 1208 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1209 | pub struct Label { | ||
1210 | pub(crate) parent: DefWithBodyId, | ||
1211 | pub(crate) label_id: LabelId, | ||
1212 | } | ||
1213 | |||
1214 | impl Label { | ||
1215 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1216 | self.parent(db).module(db) | ||
1217 | } | ||
1218 | |||
1219 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1220 | self.parent.into() | ||
1221 | } | ||
1222 | |||
1223 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1224 | let body = db.body(self.parent.into()); | ||
1225 | body[self.label_id].name.clone() | ||
1226 | } | ||
1227 | |||
1228 | pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> { | ||
1229 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1230 | let src = source_map.label_syntax(self.label_id); | ||
1231 | let root = src.file_syntax(db.upcast()); | ||
1232 | src.map(|ast| ast.to_node(&root)) | ||
1233 | } | ||
1234 | } | ||
1235 | |||
1236 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1205 | pub enum GenericParam { | 1237 | pub enum GenericParam { |
1206 | TypeParam(TypeParam), | 1238 | TypeParam(TypeParam), |
1207 | LifetimeParam(LifetimeParam), | 1239 | LifetimeParam(LifetimeParam), |
@@ -1268,7 +1300,6 @@ impl LifetimeParam { | |||
1268 | } | 1300 | } |
1269 | } | 1301 | } |
1270 | 1302 | ||
1271 | // FIXME: rename from `ImplDef` to `Impl` | ||
1272 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 1303 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
1273 | pub struct Impl { | 1304 | pub struct Impl { |
1274 | pub(crate) id: ImplId, | 1305 | pub(crate) id: ImplId, |
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index 8e0c571b8..a0792b9a6 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs | |||
@@ -4,12 +4,15 @@ | |||
4 | //! are splitting the hir. | 4 | //! are splitting the hir. |
5 | 5 | ||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | expr::PatId, item_scope::ItemInNs, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, | 7 | expr::{LabelId, PatId}, |
8 | GenericDefId, ModuleDefId, VariantId, | 8 | item_scope::ItemInNs, |
9 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, ModuleDefId, | ||
10 | VariantId, | ||
9 | }; | 11 | }; |
10 | 12 | ||
11 | use crate::{ | 13 | use crate::{ |
12 | Adt, AssocItem, DefWithBody, Field, GenericDef, Local, MacroDef, ModuleDef, Variant, VariantDef, | 14 | Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, |
15 | VariantDef, | ||
13 | }; | 16 | }; |
14 | 17 | ||
15 | macro_rules! from_id { | 18 | macro_rules! from_id { |
@@ -228,6 +231,12 @@ impl From<(DefWithBodyId, PatId)> for Local { | |||
228 | } | 231 | } |
229 | } | 232 | } |
230 | 233 | ||
234 | impl From<(DefWithBodyId, LabelId)> for Label { | ||
235 | fn from((parent, label_id): (DefWithBodyId, LabelId)) -> Self { | ||
236 | Label { parent, label_id } | ||
237 | } | ||
238 | } | ||
239 | |||
231 | impl From<MacroDef> for ItemInNs { | 240 | impl From<MacroDef> for ItemInNs { |
232 | fn from(macro_def: MacroDef) -> Self { | 241 | fn from(macro_def: MacroDef) -> Self { |
233 | ItemInNs::Macros(macro_def.into()) | 242 | ItemInNs::Macros(macro_def.into()) |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bdd270c58..7ac9fd507 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -35,8 +35,8 @@ 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 | Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef, | 37 | Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef, |
38 | HasVisibility, Impl, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, | 38 | HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef, |
39 | Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, | 39 | Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, |
40 | }, | 40 | }, |
41 | has_source::HasSource, | 41 | has_source::HasSource, |
42 | semantics::{PathResolution, Semantics, SemanticsScope}, | 42 | semantics::{PathResolution, Semantics, SemanticsScope}, |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 25ebf73d8..67cd16e31 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -15,7 +15,7 @@ use itertools::Itertools; | |||
15 | use rustc_hash::{FxHashMap, FxHashSet}; | 15 | use rustc_hash::{FxHashMap, FxHashSet}; |
16 | use syntax::{ | 16 | use syntax::{ |
17 | algo::find_node_at_offset, | 17 | algo::find_node_at_offset, |
18 | ast::{self, GenericParamsOwner}, | 18 | ast::{self, GenericParamsOwner, LoopBodyOwner}, |
19 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextSize, | 19 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextSize, |
20 | }; | 20 | }; |
21 | 21 | ||
@@ -25,8 +25,8 @@ use crate::{ | |||
25 | diagnostics::Diagnostic, | 25 | diagnostics::Diagnostic, |
26 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 26 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
27 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | 27 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, |
28 | AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, LifetimeParam, Local, | 28 | AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, Label, LifetimeParam, |
29 | MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, | 29 | Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, |
30 | VariantDef, | 30 | VariantDef, |
31 | }; | 31 | }; |
32 | 32 | ||
@@ -182,6 +182,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
182 | self.imp.resolve_lifetime_param(lifetime) | 182 | self.imp.resolve_lifetime_param(lifetime) |
183 | } | 183 | } |
184 | 184 | ||
185 | pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> { | ||
186 | self.imp.resolve_label(lifetime) | ||
187 | } | ||
188 | |||
185 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 189 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
186 | self.imp.type_of_expr(expr) | 190 | self.imp.type_of_expr(expr) |
187 | } | 191 | } |
@@ -425,6 +429,28 @@ impl<'db> SemanticsImpl<'db> { | |||
425 | ToDef::to_def(self, src) | 429 | ToDef::to_def(self, src) |
426 | } | 430 | } |
427 | 431 | ||
432 | fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> { | ||
433 | let text = lifetime.text(); | ||
434 | let label = lifetime.syntax().ancestors().find_map(|syn| { | ||
435 | let label = match_ast! { | ||
436 | match syn { | ||
437 | ast::ForExpr(it) => it.label(), | ||
438 | ast::WhileExpr(it) => it.label(), | ||
439 | ast::LoopExpr(it) => it.label(), | ||
440 | ast::EffectExpr(it) => it.label(), | ||
441 | _ => None, | ||
442 | } | ||
443 | }; | ||
444 | label.filter(|l| { | ||
445 | l.lifetime() | ||
446 | .and_then(|lt| lt.lifetime_ident_token()) | ||
447 | .map_or(false, |lt| lt.text() == text) | ||
448 | }) | ||
449 | })?; | ||
450 | let src = self.find_file(label.syntax().clone()).with_value(label); | ||
451 | ToDef::to_def(self, src) | ||
452 | } | ||
453 | |||
428 | fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 454 | fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
429 | self.analyze(expr.syntax()).type_of_expr(self.db, expr) | 455 | self.analyze(expr.syntax()).type_of_expr(self.db, expr) |
430 | } | 456 | } |
@@ -720,6 +746,7 @@ to_def_impls![ | |||
720 | (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), | 746 | (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), |
721 | (crate::MacroDef, ast::MacroRules, macro_rules_to_def), | 747 | (crate::MacroDef, ast::MacroRules, macro_rules_to_def), |
722 | (crate::Local, ast::IdentPat, bind_pat_to_def), | 748 | (crate::Local, ast::IdentPat, bind_pat_to_def), |
749 | (crate::Label, ast::Label, label_to_def), | ||
723 | ]; | 750 | ]; |
724 | 751 | ||
725 | fn find_root(node: &SyntaxNode) -> SyntaxNode { | 752 | fn find_root(node: &SyntaxNode) -> SyntaxNode { |
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 3efca5baa..424e6e8a9 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs | |||
@@ -4,7 +4,7 @@ use base_db::FileId; | |||
4 | use hir_def::{ | 4 | use hir_def::{ |
5 | child_by_source::ChildBySource, | 5 | child_by_source::ChildBySource, |
6 | dyn_map::DynMap, | 6 | dyn_map::DynMap, |
7 | expr::PatId, | 7 | expr::{LabelId, PatId}, |
8 | keys::{self, Key}, | 8 | keys::{self, Key}, |
9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, | 9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, |
10 | LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, | 10 | LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, |
@@ -108,12 +108,21 @@ impl SourceToDefCtx<'_, '_> { | |||
108 | &mut self, | 108 | &mut self, |
109 | src: InFile<ast::IdentPat>, | 109 | src: InFile<ast::IdentPat>, |
110 | ) -> Option<(DefWithBodyId, PatId)> { | 110 | ) -> Option<(DefWithBodyId, PatId)> { |
111 | let container = self.find_pat_container(src.as_ref().map(|it| it.syntax()))?; | 111 | let container = self.find_pat_or_label_container(src.as_ref().map(|it| it.syntax()))?; |
112 | let (_body, source_map) = self.db.body_with_source_map(container); | 112 | let (_body, source_map) = self.db.body_with_source_map(container); |
113 | let src = src.map(ast::Pat::from); | 113 | let src = src.map(ast::Pat::from); |
114 | let pat_id = source_map.node_pat(src.as_ref())?; | 114 | let pat_id = source_map.node_pat(src.as_ref())?; |
115 | Some((container, pat_id)) | 115 | Some((container, pat_id)) |
116 | } | 116 | } |
117 | pub(super) fn label_to_def( | ||
118 | &mut self, | ||
119 | src: InFile<ast::Label>, | ||
120 | ) -> Option<(DefWithBodyId, LabelId)> { | ||
121 | let container = self.find_pat_or_label_container(src.as_ref().map(|it| it.syntax()))?; | ||
122 | let (_body, source_map) = self.db.body_with_source_map(container); | ||
123 | let label_id = source_map.node_label(src.as_ref())?; | ||
124 | Some((container, label_id)) | ||
125 | } | ||
117 | 126 | ||
118 | fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( | 127 | fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( |
119 | &mut self, | 128 | &mut self, |
@@ -237,7 +246,7 @@ impl SourceToDefCtx<'_, '_> { | |||
237 | None | 246 | None |
238 | } | 247 | } |
239 | 248 | ||
240 | fn find_pat_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> { | 249 | fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> { |
241 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | 250 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { |
242 | let res: DefWithBodyId = match_ast! { | 251 | let res: DefWithBodyId = match_ast! { |
243 | match (container.value) { | 252 | match (container.value) { |
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 998b82601..d07004b9d 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -26,7 +26,7 @@ pub(crate) use lower::LowerCtx; | |||
26 | use crate::{ | 26 | use crate::{ |
27 | attr::{Attrs, RawAttrs}, | 27 | attr::{Attrs, RawAttrs}, |
28 | db::DefDatabase, | 28 | db::DefDatabase, |
29 | expr::{Expr, ExprId, Pat, PatId}, | 29 | expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, |
30 | item_scope::BuiltinShadowMode, | 30 | item_scope::BuiltinShadowMode, |
31 | item_scope::ItemScope, | 31 | item_scope::ItemScope, |
32 | nameres::CrateDefMap, | 32 | nameres::CrateDefMap, |
@@ -226,6 +226,7 @@ pub(crate) struct Mark { | |||
226 | pub struct Body { | 226 | pub struct Body { |
227 | pub exprs: Arena<Expr>, | 227 | pub exprs: Arena<Expr>, |
228 | pub pats: Arena<Pat>, | 228 | pub pats: Arena<Pat>, |
229 | pub labels: Arena<Label>, | ||
229 | /// The patterns for the function's parameters. While the parameter types are | 230 | /// The patterns for the function's parameters. While the parameter types are |
230 | /// part of the function signature, the patterns are not (they don't change | 231 | /// part of the function signature, the patterns are not (they don't change |
231 | /// the external type of the function). | 232 | /// the external type of the function). |
@@ -244,6 +245,8 @@ pub type ExprSource = InFile<ExprPtr>; | |||
244 | pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; | 245 | pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; |
245 | pub type PatSource = InFile<PatPtr>; | 246 | pub type PatSource = InFile<PatPtr>; |
246 | 247 | ||
248 | pub type LabelPtr = AstPtr<ast::Label>; | ||
249 | pub type LabelSource = InFile<LabelPtr>; | ||
247 | /// An item body together with the mapping from syntax nodes to HIR expression | 250 | /// An item body together with the mapping from syntax nodes to HIR expression |
248 | /// IDs. This is needed to go from e.g. a position in a file to the HIR | 251 | /// IDs. This is needed to go from e.g. a position in a file to the HIR |
249 | /// expression containing it; but for type inference etc., we want to operate on | 252 | /// expression containing it; but for type inference etc., we want to operate on |
@@ -261,6 +264,8 @@ pub struct BodySourceMap { | |||
261 | expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>, | 264 | expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>, |
262 | pat_map: FxHashMap<PatSource, PatId>, | 265 | pat_map: FxHashMap<PatSource, PatId>, |
263 | pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, | 266 | pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, |
267 | label_map: FxHashMap<LabelSource, LabelId>, | ||
268 | label_map_back: ArenaMap<LabelId, LabelSource>, | ||
264 | field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>, | 269 | field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>, |
265 | expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, | 270 | expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, |
266 | 271 | ||
@@ -334,6 +339,14 @@ impl Index<PatId> for Body { | |||
334 | } | 339 | } |
335 | } | 340 | } |
336 | 341 | ||
342 | impl Index<LabelId> for Body { | ||
343 | type Output = Label; | ||
344 | |||
345 | fn index(&self, label: LabelId) -> &Label { | ||
346 | &self.labels[label] | ||
347 | } | ||
348 | } | ||
349 | |||
337 | impl BodySourceMap { | 350 | impl BodySourceMap { |
338 | pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> { | 351 | pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> { |
339 | self.expr_map_back[expr].clone() | 352 | self.expr_map_back[expr].clone() |
@@ -363,6 +376,15 @@ impl BodySourceMap { | |||
363 | self.pat_map.get(&src).cloned() | 376 | self.pat_map.get(&src).cloned() |
364 | } | 377 | } |
365 | 378 | ||
379 | pub fn label_syntax(&self, label: LabelId) -> LabelSource { | ||
380 | self.label_map_back[label].clone() | ||
381 | } | ||
382 | |||
383 | pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> { | ||
384 | let src = node.map(|it| AstPtr::new(it)); | ||
385 | self.label_map.get(&src).cloned() | ||
386 | } | ||
387 | |||
366 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { | 388 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { |
367 | self.field_map[&(expr, field)].clone() | 389 | self.field_map[&(expr, field)].clone() |
368 | } | 390 | } |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 0f404be1b..17c72779b 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -22,13 +22,14 @@ use test_utils::mark; | |||
22 | 22 | ||
23 | use crate::{ | 23 | use crate::{ |
24 | adt::StructKind, | 24 | adt::StructKind, |
25 | body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, | 25 | body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax}, |
26 | builtin_type::{BuiltinFloat, BuiltinInt}, | 26 | builtin_type::{BuiltinFloat, BuiltinInt}, |
27 | db::DefDatabase, | 27 | db::DefDatabase, |
28 | diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro}, | 28 | diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro}, |
29 | expr::{ | 29 | expr::{ |
30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, | 30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label, |
31 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, | 31 | LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, |
32 | Statement, | ||
32 | }, | 33 | }, |
33 | item_scope::BuiltinShadowMode, | 34 | item_scope::BuiltinShadowMode, |
34 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, | 35 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, |
@@ -72,6 +73,7 @@ pub(super) fn lower( | |||
72 | body: Body { | 73 | body: Body { |
73 | exprs: Arena::default(), | 74 | exprs: Arena::default(), |
74 | pats: Arena::default(), | 75 | pats: Arena::default(), |
76 | labels: Arena::default(), | ||
75 | params: Vec::new(), | 77 | params: Vec::new(), |
76 | body_expr: dummy_expr_id(), | 78 | body_expr: dummy_expr_id(), |
77 | item_scope: Default::default(), | 79 | item_scope: Default::default(), |
@@ -175,6 +177,18 @@ impl ExprCollector<'_> { | |||
175 | id | 177 | id |
176 | } | 178 | } |
177 | 179 | ||
180 | fn alloc_label(&mut self, label: Label, ptr: AstPtr<ast::Label>) -> LabelId { | ||
181 | let src = self.expander.to_source(ptr); | ||
182 | let id = self.make_label(label, src.clone()); | ||
183 | self.source_map.label_map.insert(src, id); | ||
184 | id | ||
185 | } | ||
186 | fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId { | ||
187 | let id = self.body.labels.alloc(label); | ||
188 | self.source_map.label_map_back.insert(id, src); | ||
189 | id | ||
190 | } | ||
191 | |||
178 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { | 192 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { |
179 | let syntax_ptr = AstPtr::new(&expr); | 193 | let syntax_ptr = AstPtr::new(&expr); |
180 | if self.check_cfg(&expr).is_none() { | 194 | if self.check_cfg(&expr).is_none() { |
@@ -228,37 +242,40 @@ impl ExprCollector<'_> { | |||
228 | self.alloc_expr(Expr::Unsafe { body }, syntax_ptr) | 242 | self.alloc_expr(Expr::Unsafe { body }, syntax_ptr) |
229 | } | 243 | } |
230 | // FIXME: we need to record these effects somewhere... | 244 | // FIXME: we need to record these effects somewhere... |
231 | ast::Effect::Label(label) => match e.block_expr() { | 245 | ast::Effect::Label(label) => { |
232 | Some(block) => { | 246 | let label = self.collect_label(label); |
233 | let res = self.collect_block(block); | 247 | match e.block_expr() { |
234 | match &mut self.body.exprs[res] { | 248 | Some(block) => { |
235 | Expr::Block { label: block_label, .. } => { | 249 | let res = self.collect_block(block); |
236 | *block_label = label.lifetime().map(|t| Name::new_lifetime(&t)) | 250 | match &mut self.body.exprs[res] { |
251 | Expr::Block { label: block_label, .. } => { | ||
252 | *block_label = Some(label); | ||
253 | } | ||
254 | _ => unreachable!(), | ||
237 | } | 255 | } |
238 | _ => unreachable!(), | 256 | res |
239 | } | 257 | } |
240 | res | 258 | None => self.missing_expr(), |
241 | } | 259 | } |
242 | None => self.missing_expr(), | 260 | } |
243 | }, | ||
244 | // FIXME: we need to record these effects somewhere... | 261 | // FIXME: we need to record these effects somewhere... |
245 | ast::Effect::Async(_) => { | 262 | ast::Effect::Async(_) => { |
246 | let body = self.collect_block_opt(e.block_expr()); | 263 | let body = self.collect_block_opt(e.block_expr()); |
247 | self.alloc_expr(Expr::Async { body }, syntax_ptr) | 264 | self.alloc_expr(Expr::Async { body }, syntax_ptr) |
248 | } | 265 | } |
266 | ast::Effect::Const(_) => { | ||
267 | let body = self.collect_block_opt(e.block_expr()); | ||
268 | self.alloc_expr(Expr::Const { body }, syntax_ptr) | ||
269 | } | ||
249 | }, | 270 | }, |
250 | ast::Expr::BlockExpr(e) => self.collect_block(e), | 271 | ast::Expr::BlockExpr(e) => self.collect_block(e), |
251 | ast::Expr::LoopExpr(e) => { | 272 | ast::Expr::LoopExpr(e) => { |
273 | let label = e.label().map(|label| self.collect_label(label)); | ||
252 | let body = self.collect_block_opt(e.loop_body()); | 274 | let body = self.collect_block_opt(e.loop_body()); |
253 | self.alloc_expr( | 275 | self.alloc_expr(Expr::Loop { body, label }, syntax_ptr) |
254 | Expr::Loop { | ||
255 | body, | ||
256 | label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)), | ||
257 | }, | ||
258 | syntax_ptr, | ||
259 | ) | ||
260 | } | 276 | } |
261 | ast::Expr::WhileExpr(e) => { | 277 | ast::Expr::WhileExpr(e) => { |
278 | let label = e.label().map(|label| self.collect_label(label)); | ||
262 | let body = self.collect_block_opt(e.loop_body()); | 279 | let body = self.collect_block_opt(e.loop_body()); |
263 | 280 | ||
264 | let condition = match e.condition() { | 281 | let condition = match e.condition() { |
@@ -279,42 +296,20 @@ impl ExprCollector<'_> { | |||
279 | ]; | 296 | ]; |
280 | let match_expr = | 297 | let match_expr = |
281 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); | 298 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); |
282 | return self.alloc_expr( | 299 | return self |
283 | Expr::Loop { | 300 | .alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr); |
284 | body: match_expr, | ||
285 | label: e | ||
286 | .label() | ||
287 | .and_then(|l| l.lifetime()) | ||
288 | .map(|l| Name::new_lifetime(&l)), | ||
289 | }, | ||
290 | syntax_ptr, | ||
291 | ); | ||
292 | } | 301 | } |
293 | }, | 302 | }, |
294 | }; | 303 | }; |
295 | 304 | ||
296 | self.alloc_expr( | 305 | self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr) |
297 | Expr::While { | ||
298 | condition, | ||
299 | body, | ||
300 | label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)), | ||
301 | }, | ||
302 | syntax_ptr, | ||
303 | ) | ||
304 | } | 306 | } |
305 | ast::Expr::ForExpr(e) => { | 307 | ast::Expr::ForExpr(e) => { |
308 | let label = e.label().map(|label| self.collect_label(label)); | ||
306 | let iterable = self.collect_expr_opt(e.iterable()); | 309 | let iterable = self.collect_expr_opt(e.iterable()); |
307 | let pat = self.collect_pat_opt(e.pat()); | 310 | let pat = self.collect_pat_opt(e.pat()); |
308 | let body = self.collect_block_opt(e.loop_body()); | 311 | let body = self.collect_block_opt(e.loop_body()); |
309 | self.alloc_expr( | 312 | self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr) |
310 | Expr::For { | ||
311 | iterable, | ||
312 | pat, | ||
313 | body, | ||
314 | label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)), | ||
315 | }, | ||
316 | syntax_ptr, | ||
317 | ) | ||
318 | } | 313 | } |
319 | ast::Expr::CallExpr(e) => { | 314 | ast::Expr::CallExpr(e) => { |
320 | let callee = self.collect_expr_opt(e.expr()); | 315 | let callee = self.collect_expr_opt(e.expr()); |
@@ -814,6 +809,13 @@ impl ExprCollector<'_> { | |||
814 | } | 809 | } |
815 | } | 810 | } |
816 | 811 | ||
812 | fn collect_label(&mut self, ast_label: ast::Label) -> LabelId { | ||
813 | let label = Label { | ||
814 | name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime), | ||
815 | }; | ||
816 | self.alloc_label(label, AstPtr::new(&ast_label)) | ||
817 | } | ||
818 | |||
817 | fn collect_pat(&mut self, pat: ast::Pat) -> PatId { | 819 | fn collect_pat(&mut self, pat: ast::Pat) -> PatId { |
818 | let pattern = match &pat { | 820 | let pattern = match &pat { |
819 | ast::Pat::IdentPat(bp) => { | 821 | ast::Pat::IdentPat(bp) => { |
@@ -932,6 +934,14 @@ impl ExprCollector<'_> { | |||
932 | let inner = self.collect_pat_opt(boxpat.pat()); | 934 | let inner = self.collect_pat_opt(boxpat.pat()); |
933 | Pat::Box { inner } | 935 | Pat::Box { inner } |
934 | } | 936 | } |
937 | ast::Pat::ConstBlockPat(const_block_pat) => { | ||
938 | if let Some(expr) = const_block_pat.block_expr() { | ||
939 | let expr_id = self.collect_block(expr); | ||
940 | Pat::ConstBlock(expr_id) | ||
941 | } else { | ||
942 | Pat::Missing | ||
943 | } | ||
944 | } | ||
935 | // FIXME: implement | 945 | // FIXME: implement |
936 | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, | 946 | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, |
937 | }; | 947 | }; |
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index e5d740a36..6a481769d 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -30,6 +30,12 @@ pub(crate) fn dummy_expr_id() -> ExprId { | |||
30 | pub type PatId = Idx<Pat>; | 30 | pub type PatId = Idx<Pat>; |
31 | 31 | ||
32 | #[derive(Debug, Clone, Eq, PartialEq)] | 32 | #[derive(Debug, Clone, Eq, PartialEq)] |
33 | pub struct Label { | ||
34 | pub name: Name, | ||
35 | } | ||
36 | pub type LabelId = Idx<Label>; | ||
37 | |||
38 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
33 | pub enum Literal { | 39 | pub enum Literal { |
34 | String(String), | 40 | String(String), |
35 | ByteString(Vec<u8>), | 41 | ByteString(Vec<u8>), |
@@ -52,22 +58,22 @@ pub enum Expr { | |||
52 | Block { | 58 | Block { |
53 | statements: Vec<Statement>, | 59 | statements: Vec<Statement>, |
54 | tail: Option<ExprId>, | 60 | tail: Option<ExprId>, |
55 | label: Option<Name>, | 61 | label: Option<LabelId>, |
56 | }, | 62 | }, |
57 | Loop { | 63 | Loop { |
58 | body: ExprId, | 64 | body: ExprId, |
59 | label: Option<Name>, | 65 | label: Option<LabelId>, |
60 | }, | 66 | }, |
61 | While { | 67 | While { |
62 | condition: ExprId, | 68 | condition: ExprId, |
63 | body: ExprId, | 69 | body: ExprId, |
64 | label: Option<Name>, | 70 | label: Option<LabelId>, |
65 | }, | 71 | }, |
66 | For { | 72 | For { |
67 | iterable: ExprId, | 73 | iterable: ExprId, |
68 | pat: PatId, | 74 | pat: PatId, |
69 | body: ExprId, | 75 | body: ExprId, |
70 | label: Option<Name>, | 76 | label: Option<LabelId>, |
71 | }, | 77 | }, |
72 | Call { | 78 | Call { |
73 | callee: ExprId, | 79 | callee: ExprId, |
@@ -114,6 +120,9 @@ pub enum Expr { | |||
114 | Async { | 120 | Async { |
115 | body: ExprId, | 121 | body: ExprId, |
116 | }, | 122 | }, |
123 | Const { | ||
124 | body: ExprId, | ||
125 | }, | ||
117 | Cast { | 126 | Cast { |
118 | expr: ExprId, | 127 | expr: ExprId, |
119 | type_ref: TypeRef, | 128 | type_ref: TypeRef, |
@@ -253,7 +262,10 @@ impl Expr { | |||
253 | f(*expr); | 262 | f(*expr); |
254 | } | 263 | } |
255 | } | 264 | } |
256 | Expr::TryBlock { body } | Expr::Unsafe { body } | Expr::Async { body } => f(*body), | 265 | Expr::TryBlock { body } |
266 | | Expr::Unsafe { body } | ||
267 | | Expr::Async { body } | ||
268 | | Expr::Const { body } => f(*body), | ||
257 | Expr::Loop { body, .. } => f(*body), | 269 | Expr::Loop { body, .. } => f(*body), |
258 | Expr::While { condition, body, .. } => { | 270 | Expr::While { condition, body, .. } => { |
259 | f(*condition); | 271 | f(*condition); |
@@ -399,12 +411,18 @@ pub enum Pat { | |||
399 | TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> }, | 411 | TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> }, |
400 | Ref { pat: PatId, mutability: Mutability }, | 412 | Ref { pat: PatId, mutability: Mutability }, |
401 | Box { inner: PatId }, | 413 | Box { inner: PatId }, |
414 | ConstBlock(ExprId), | ||
402 | } | 415 | } |
403 | 416 | ||
404 | impl Pat { | 417 | impl Pat { |
405 | pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) { | 418 | pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) { |
406 | match self { | 419 | match self { |
407 | Pat::Range { .. } | Pat::Lit(..) | Pat::Path(..) | Pat::Wild | Pat::Missing => {} | 420 | Pat::Range { .. } |
421 | | Pat::Lit(..) | ||
422 | | Pat::Path(..) | ||
423 | | Pat::ConstBlock(..) | ||
424 | | Pat::Wild | ||
425 | | Pat::Missing => {} | ||
408 | Pat::Bind { subpat, .. } => { | 426 | Pat::Bind { subpat, .. } => { |
409 | subpat.iter().copied().for_each(f); | 427 | subpat.iter().copied().for_each(f); |
410 | } | 428 | } |
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs index 41134d23b..bb8fca009 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs | |||
@@ -62,6 +62,7 @@ pub struct GenericParams { | |||
62 | pub enum WherePredicate { | 62 | pub enum WherePredicate { |
63 | TypeBound { target: WherePredicateTypeTarget, bound: TypeBound }, | 63 | TypeBound { target: WherePredicateTypeTarget, bound: TypeBound }, |
64 | Lifetime { target: LifetimeRef, bound: LifetimeRef }, | 64 | Lifetime { target: LifetimeRef, bound: LifetimeRef }, |
65 | ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound }, | ||
65 | } | 66 | } |
66 | 67 | ||
67 | #[derive(Clone, PartialEq, Eq, Debug)] | 68 | #[derive(Clone, PartialEq, Eq, Debug)] |
@@ -69,7 +70,6 @@ pub enum WherePredicateTypeTarget { | |||
69 | TypeRef(TypeRef), | 70 | TypeRef(TypeRef), |
70 | /// For desugared where predicates that can directly refer to a type param. | 71 | /// For desugared where predicates that can directly refer to a type param. |
71 | TypeParam(LocalTypeParamId), | 72 | TypeParam(LocalTypeParamId), |
72 | // FIXME: ForLifetime(Vec<LifetimeParamId>, TypeRef) | ||
73 | } | 73 | } |
74 | 74 | ||
75 | #[derive(Default)] | 75 | #[derive(Default)] |
@@ -234,7 +234,7 @@ impl GenericParams { | |||
234 | for bound in | 234 | for bound in |
235 | node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) | 235 | node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) |
236 | { | 236 | { |
237 | self.add_where_predicate_from_bound(lower_ctx, bound, target.clone()); | 237 | self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone()); |
238 | } | 238 | } |
239 | } | 239 | } |
240 | 240 | ||
@@ -279,8 +279,25 @@ impl GenericParams { | |||
279 | } else { | 279 | } else { |
280 | continue; | 280 | continue; |
281 | }; | 281 | }; |
282 | |||
283 | let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| { | ||
284 | // Higher-Ranked Trait Bounds | ||
285 | param_list | ||
286 | .lifetime_params() | ||
287 | .map(|lifetime_param| { | ||
288 | lifetime_param | ||
289 | .lifetime() | ||
290 | .map_or_else(Name::missing, |lt| Name::new_lifetime(<)) | ||
291 | }) | ||
292 | .collect() | ||
293 | }); | ||
282 | for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { | 294 | for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { |
283 | self.add_where_predicate_from_bound(lower_ctx, bound, target.clone()); | 295 | self.add_where_predicate_from_bound( |
296 | lower_ctx, | ||
297 | bound, | ||
298 | lifetimes.as_ref(), | ||
299 | target.clone(), | ||
300 | ); | ||
284 | } | 301 | } |
285 | } | 302 | } |
286 | } | 303 | } |
@@ -289,6 +306,7 @@ impl GenericParams { | |||
289 | &mut self, | 306 | &mut self, |
290 | lower_ctx: &LowerCtx, | 307 | lower_ctx: &LowerCtx, |
291 | bound: ast::TypeBound, | 308 | bound: ast::TypeBound, |
309 | hrtb_lifetimes: Option<&Box<[Name]>>, | ||
292 | target: Either<TypeRef, LifetimeRef>, | 310 | target: Either<TypeRef, LifetimeRef>, |
293 | ) { | 311 | ) { |
294 | if bound.question_mark_token().is_some() { | 312 | if bound.question_mark_token().is_some() { |
@@ -297,9 +315,16 @@ impl GenericParams { | |||
297 | } | 315 | } |
298 | let bound = TypeBound::from_ast(lower_ctx, bound); | 316 | let bound = TypeBound::from_ast(lower_ctx, bound); |
299 | let predicate = match (target, bound) { | 317 | let predicate = match (target, bound) { |
300 | (Either::Left(type_ref), bound) => WherePredicate::TypeBound { | 318 | (Either::Left(type_ref), bound) => match hrtb_lifetimes { |
301 | target: WherePredicateTypeTarget::TypeRef(type_ref), | 319 | Some(hrtb_lifetimes) => WherePredicate::ForLifetime { |
302 | bound, | 320 | lifetimes: hrtb_lifetimes.clone(), |
321 | target: WherePredicateTypeTarget::TypeRef(type_ref), | ||
322 | bound, | ||
323 | }, | ||
324 | None => WherePredicate::TypeBound { | ||
325 | target: WherePredicateTypeTarget::TypeRef(type_ref), | ||
326 | bound, | ||
327 | }, | ||
303 | }, | 328 | }, |
304 | (Either::Right(lifetime), TypeBound::Lifetime(bound)) => { | 329 | (Either::Right(lifetime), TypeBound::Lifetime(bound)) => { |
305 | WherePredicate::Lifetime { target: lifetime, bound } | 330 | WherePredicate::Lifetime { target: lifetime, bound } |
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs index b4ccd4488..af3262439 100644 --- a/crates/hir_def/src/nameres/mod_resolution.rs +++ b/crates/hir_def/src/nameres/mod_resolution.rs | |||
@@ -79,7 +79,7 @@ impl ModDir { | |||
79 | for candidate in candidate_files.iter() { | 79 | for candidate in candidate_files.iter() { |
80 | let path = AnchoredPath { anchor: file_id, path: candidate.as_str() }; | 80 | let path = AnchoredPath { anchor: file_id, path: candidate.as_str() }; |
81 | if let Some(file_id) = db.resolve_path(path) { | 81 | if let Some(file_id) = db.resolve_path(path) { |
82 | let is_mod_rs = candidate.ends_with("mod.rs"); | 82 | let is_mod_rs = candidate.ends_with("/mod.rs"); |
83 | 83 | ||
84 | let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() { | 84 | let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() { |
85 | (DirPath::empty(), false) | 85 | (DirPath::empty(), false) |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index dddbbcdac..6382521fb 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -563,6 +563,7 @@ mod tests { | |||
563 | 563 | ||
564 | let args = macro_call.token_tree().unwrap(); | 564 | let args = macro_call.token_tree().unwrap(); |
565 | let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0; | 565 | let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0; |
566 | let call_id = AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)); | ||
566 | 567 | ||
567 | let arg_id = db.intern_eager_expansion({ | 568 | let arg_id = db.intern_eager_expansion({ |
568 | EagerCallLoc { | 569 | EagerCallLoc { |
@@ -570,7 +571,7 @@ mod tests { | |||
570 | fragment: FragmentKind::Expr, | 571 | fragment: FragmentKind::Expr, |
571 | subtree: Arc::new(parsed_args.clone()), | 572 | subtree: Arc::new(parsed_args.clone()), |
572 | krate, | 573 | krate, |
573 | file_id: file_id.into(), | 574 | call: call_id, |
574 | } | 575 | } |
575 | }); | 576 | }); |
576 | 577 | ||
@@ -580,7 +581,7 @@ mod tests { | |||
580 | fragment, | 581 | fragment, |
581 | subtree: Arc::new(subtree), | 582 | subtree: Arc::new(subtree), |
582 | krate, | 583 | krate, |
583 | file_id: file_id.into(), | 584 | call: call_id, |
584 | }; | 585 | }; |
585 | 586 | ||
586 | let id: MacroCallId = db.intern_eager_expansion(eager).into(); | 587 | let id: MacroCallId = db.intern_eager_expansion(eager).into(); |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 4477d867f..077de3727 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -5,7 +5,7 @@ use std::sync::Arc; | |||
5 | use base_db::{salsa, SourceDatabase}; | 5 | use base_db::{salsa, SourceDatabase}; |
6 | use mbe::{ExpandError, ExpandResult, MacroRules}; | 6 | use mbe::{ExpandError, ExpandResult, MacroRules}; |
7 | use parser::FragmentKind; | 7 | use parser::FragmentKind; |
8 | use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; | 8 | use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, | 11 | ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, |
@@ -129,11 +129,11 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | |||
129 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | 129 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { |
130 | match id.kind { | 130 | match id.kind { |
131 | MacroDefKind::Declarative => { | 131 | MacroDefKind::Declarative => { |
132 | let macro_call = match id.ast_id?.to_node(db) { | 132 | let macro_rules = match id.ast_id?.to_node(db) { |
133 | syntax::ast::Macro::MacroRules(mac) => mac, | 133 | syntax::ast::Macro::MacroRules(mac) => mac, |
134 | syntax::ast::Macro::MacroDef(_) => return None, | 134 | syntax::ast::Macro::MacroDef(_) => return None, |
135 | }; | 135 | }; |
136 | let arg = macro_call.token_tree()?; | 136 | let arg = macro_rules.token_tree()?; |
137 | let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { | 137 | let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { |
138 | log::warn!("fail on macro_def to token tree: {:#?}", arg); | 138 | log::warn!("fail on macro_def to token tree: {:#?}", arg); |
139 | None | 139 | None |
@@ -141,7 +141,8 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, | |||
141 | let rules = match MacroRules::parse(&tt) { | 141 | let rules = match MacroRules::parse(&tt) { |
142 | Ok(it) => it, | 142 | Ok(it) => it, |
143 | Err(err) => { | 143 | Err(err) => { |
144 | log::warn!("fail on macro_def parse: error: {:#?} {:#?}", err, tt); | 144 | let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); |
145 | log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt); | ||
145 | return None; | 146 | return None; |
146 | } | 147 | } |
147 | }; | 148 | }; |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index 0229a836e..6354b090d 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -110,6 +110,9 @@ pub fn expand_eager_macro( | |||
110 | || err("malformed macro invocation"), | 110 | || err("malformed macro invocation"), |
111 | )?; | 111 | )?; |
112 | 112 | ||
113 | let ast_map = db.ast_id_map(macro_call.file_id); | ||
114 | let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value)); | ||
115 | |||
113 | // Note: | 116 | // Note: |
114 | // When `lazy_expand` is called, its *parent* file must be already exists. | 117 | // When `lazy_expand` is called, its *parent* file must be already exists. |
115 | // Here we store an eager macro id for the argument expanded subtree here | 118 | // Here we store an eager macro id for the argument expanded subtree here |
@@ -120,7 +123,7 @@ pub fn expand_eager_macro( | |||
120 | fragment: FragmentKind::Expr, | 123 | fragment: FragmentKind::Expr, |
121 | subtree: Arc::new(parsed_args.clone()), | 124 | subtree: Arc::new(parsed_args.clone()), |
122 | krate, | 125 | krate, |
123 | file_id: macro_call.file_id, | 126 | call: call_id, |
124 | } | 127 | } |
125 | }); | 128 | }); |
126 | let arg_file_id: MacroCallId = arg_id.into(); | 129 | let arg_file_id: MacroCallId = arg_id.into(); |
@@ -141,13 +144,8 @@ pub fn expand_eager_macro( | |||
141 | let res = eager.expand(db, arg_id, &subtree); | 144 | let res = eager.expand(db, arg_id, &subtree); |
142 | 145 | ||
143 | let (subtree, fragment) = diagnostic_sink.expand_result_option(res)?; | 146 | let (subtree, fragment) = diagnostic_sink.expand_result_option(res)?; |
144 | let eager = EagerCallLoc { | 147 | let eager = |
145 | def, | 148 | EagerCallLoc { def, fragment, subtree: Arc::new(subtree), krate, call: call_id }; |
146 | fragment, | ||
147 | subtree: Arc::new(subtree), | ||
148 | krate, | ||
149 | file_id: macro_call.file_id, | ||
150 | }; | ||
151 | 149 | ||
152 | Ok(db.intern_eager_expansion(eager)) | 150 | Ok(db.intern_eager_expansion(eager)) |
153 | } else { | 151 | } else { |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index d486186e5..3fa1b1d77 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -83,7 +83,7 @@ impl HirFileId { | |||
83 | } | 83 | } |
84 | MacroCallId::EagerMacro(id) => { | 84 | MacroCallId::EagerMacro(id) => { |
85 | let loc = db.lookup_intern_eager_expansion(id); | 85 | let loc = db.lookup_intern_eager_expansion(id); |
86 | loc.file_id | 86 | loc.call.file_id |
87 | } | 87 | } |
88 | }; | 88 | }; |
89 | file_id.original_file(db) | 89 | file_id.original_file(db) |
@@ -103,7 +103,7 @@ impl HirFileId { | |||
103 | } | 103 | } |
104 | MacroCallId::EagerMacro(id) => { | 104 | MacroCallId::EagerMacro(id) => { |
105 | let loc = db.lookup_intern_eager_expansion(id); | 105 | let loc = db.lookup_intern_eager_expansion(id); |
106 | loc.file_id | 106 | loc.call.file_id |
107 | } | 107 | } |
108 | }; | 108 | }; |
109 | } | 109 | } |
@@ -114,17 +114,16 @@ impl HirFileId { | |||
114 | pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> { | 114 | pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> { |
115 | match self.0 { | 115 | match self.0 { |
116 | HirFileIdRepr::FileId(_) => None, | 116 | HirFileIdRepr::FileId(_) => None, |
117 | HirFileIdRepr::MacroFile(macro_file) => { | 117 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { |
118 | let lazy_id = match macro_file.macro_call_id { | 118 | MacroCallId::LazyMacro(lazy_id) => { |
119 | MacroCallId::LazyMacro(id) => id, | 119 | let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id); |
120 | MacroCallId::EagerMacro(_id) => { | 120 | Some(loc.kind.node(db)) |
121 | // FIXME: handle call node for eager macro | 121 | } |
122 | return None; | 122 | MacroCallId::EagerMacro(id) => { |
123 | } | 123 | let loc: EagerCallLoc = db.lookup_intern_eager_expansion(id); |
124 | }; | 124 | Some(loc.call.with_value(loc.call.to_node(db).syntax().clone())) |
125 | let loc = db.lookup_intern_macro(lazy_id); | 125 | } |
126 | Some(loc.kind.node(db)) | 126 | }, |
127 | } | ||
128 | } | 127 | } |
129 | } | 128 | } |
130 | 129 | ||
@@ -304,7 +303,7 @@ pub struct EagerCallLoc { | |||
304 | pub(crate) fragment: FragmentKind, | 303 | pub(crate) fragment: FragmentKind, |
305 | pub(crate) subtree: Arc<tt::Subtree>, | 304 | pub(crate) subtree: Arc<tt::Subtree>, |
306 | pub(crate) krate: CrateId, | 305 | pub(crate) krate: CrateId, |
307 | pub(crate) file_id: HirFileId, | 306 | pub(crate) call: AstId<ast::MacroCall>, |
308 | } | 307 | } |
309 | 308 | ||
310 | /// ExpansionInfo mainly describes how to map text range between src and expanded macro | 309 | /// ExpansionInfo mainly describes how to map text range between src and expanded macro |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index 289e812fe..965c1780a 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -17,9 +17,9 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 17 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 19 | scoped-tls = "1" |
20 | chalk-solve = { version = "0.43", default-features = false } | 20 | chalk-solve = { version = "0.45", default-features = false } |
21 | chalk-ir = "0.43" | 21 | chalk-ir = "0.45" |
22 | chalk-recursive = "0.43" | 22 | chalk-recursive = "0.45" |
23 | 23 | ||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | hir_def = { path = "../hir_def", version = "0.0.0" } | 25 | hir_def = { path = "../hir_def", version = "0.0.0" } |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 2cdce2cef..70a3f3075 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -143,7 +143,7 @@ impl<'a> InferenceContext<'a> { | |||
143 | self.breakables.push(BreakableContext { | 143 | self.breakables.push(BreakableContext { |
144 | may_break: false, | 144 | may_break: false, |
145 | break_ty: break_ty.clone(), | 145 | break_ty: break_ty.clone(), |
146 | label: label.clone(), | 146 | label: label.map(|label| self.body[label].name.clone()), |
147 | }); | 147 | }); |
148 | let ty = self.infer_block(statements, *tail, &Expectation::has_type(break_ty)); | 148 | let ty = self.infer_block(statements, *tail, &Expectation::has_type(break_ty)); |
149 | let ctxt = self.breakables.pop().expect("breakable stack broken"); | 149 | let ctxt = self.breakables.pop().expect("breakable stack broken"); |
@@ -155,7 +155,7 @@ impl<'a> InferenceContext<'a> { | |||
155 | } | 155 | } |
156 | None => self.infer_block(statements, *tail, expected), | 156 | None => self.infer_block(statements, *tail, expected), |
157 | }, | 157 | }, |
158 | Expr::Unsafe { body } => self.infer_expr(*body, expected), | 158 | Expr::Unsafe { body } | Expr::Const { body } => self.infer_expr(*body, expected), |
159 | Expr::TryBlock { body } => { | 159 | Expr::TryBlock { body } => { |
160 | let _inner = self.infer_expr(*body, expected); | 160 | let _inner = self.infer_expr(*body, expected); |
161 | // FIXME should be std::result::Result<{inner}, _> | 161 | // FIXME should be std::result::Result<{inner}, _> |
@@ -172,7 +172,7 @@ impl<'a> InferenceContext<'a> { | |||
172 | self.breakables.push(BreakableContext { | 172 | self.breakables.push(BreakableContext { |
173 | may_break: false, | 173 | may_break: false, |
174 | break_ty: self.table.new_type_var(), | 174 | break_ty: self.table.new_type_var(), |
175 | label: label.clone(), | 175 | label: label.map(|label| self.body[label].name.clone()), |
176 | }); | 176 | }); |
177 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 177 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
178 | 178 | ||
@@ -191,7 +191,7 @@ impl<'a> InferenceContext<'a> { | |||
191 | self.breakables.push(BreakableContext { | 191 | self.breakables.push(BreakableContext { |
192 | may_break: false, | 192 | may_break: false, |
193 | break_ty: Ty::Unknown, | 193 | break_ty: Ty::Unknown, |
194 | label: label.clone(), | 194 | label: label.map(|label| self.body[label].name.clone()), |
195 | }); | 195 | }); |
196 | // while let is desugared to a match loop, so this is always simple while | 196 | // while let is desugared to a match loop, so this is always simple while |
197 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 197 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
@@ -207,7 +207,7 @@ impl<'a> InferenceContext<'a> { | |||
207 | self.breakables.push(BreakableContext { | 207 | self.breakables.push(BreakableContext { |
208 | may_break: false, | 208 | may_break: false, |
209 | break_ty: Ty::Unknown, | 209 | break_ty: Ty::Unknown, |
210 | label: label.clone(), | 210 | label: label.map(|label| self.body[label].name.clone()), |
211 | }); | 211 | }); |
212 | let pat_ty = | 212 | let pat_ty = |
213 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); | 213 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index b70ec55eb..d974f805b 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -243,6 +243,9 @@ impl<'a> InferenceContext<'a> { | |||
243 | } | 243 | } |
244 | None => Ty::Unknown, | 244 | None => Ty::Unknown, |
245 | }, | 245 | }, |
246 | Pat::ConstBlock(expr) => { | ||
247 | self.infer_expr(*expr, &Expectation::has_type(expected.clone())) | ||
248 | } | ||
246 | Pat::Missing => Ty::Unknown, | 249 | Pat::Missing => Ty::Unknown, |
247 | }; | 250 | }; |
248 | // use a new type variable if we got Ty::Unknown here | 251 | // use a new type variable if we got Ty::Unknown here |
@@ -264,8 +267,9 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { | |||
264 | | Pat::Range { .. } | 267 | | Pat::Range { .. } |
265 | | Pat::Slice { .. } => true, | 268 | | Pat::Slice { .. } => true, |
266 | Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)), | 269 | Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)), |
267 | // FIXME: Path/Lit might actually evaluate to ref, but inference is unimplemented. | 270 | // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented. |
268 | Pat::Path(..) => true, | 271 | Pat::Path(..) => true, |
272 | Pat::ConstBlock(..) => true, | ||
269 | Pat::Lit(expr) => match body[*expr] { | 273 | Pat::Lit(expr) => match body[*expr] { |
270 | Expr::Literal(Literal::String(..)) => false, | 274 | Expr::Literal(Literal::String(..)) => false, |
271 | _ => true, | 275 | _ => true, |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 8392cb770..8da56cd11 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -675,7 +675,8 @@ impl GenericPredicate { | |||
675 | where_predicate: &'a WherePredicate, | 675 | where_predicate: &'a WherePredicate, |
676 | ) -> impl Iterator<Item = GenericPredicate> + 'a { | 676 | ) -> impl Iterator<Item = GenericPredicate> + 'a { |
677 | match where_predicate { | 677 | match where_predicate { |
678 | WherePredicate::TypeBound { target, bound } => { | 678 | WherePredicate::ForLifetime { target, bound, .. } |
679 | | WherePredicate::TypeBound { target, bound } => { | ||
679 | let self_ty = match target { | 680 | let self_ty = match target { |
680 | WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref), | 681 | WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref), |
681 | WherePredicateTypeTarget::TypeParam(param_id) => { | 682 | WherePredicateTypeTarget::TypeParam(param_id) => { |
@@ -888,14 +889,13 @@ pub(crate) fn generic_predicates_for_param_query( | |||
888 | .where_predicates_in_scope() | 889 | .where_predicates_in_scope() |
889 | // we have to filter out all other predicates *first*, before attempting to lower them | 890 | // we have to filter out all other predicates *first*, before attempting to lower them |
890 | .filter(|pred| match pred { | 891 | .filter(|pred| match pred { |
891 | WherePredicate::TypeBound { | 892 | WherePredicate::ForLifetime { target, .. } |
892 | target: WherePredicateTypeTarget::TypeRef(type_ref), | 893 | | WherePredicate::TypeBound { target, .. } => match target { |
893 | .. | 894 | WherePredicateTypeTarget::TypeRef(type_ref) => { |
894 | } => Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id), | 895 | Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id) |
895 | WherePredicate::TypeBound { | 896 | } |
896 | target: WherePredicateTypeTarget::TypeParam(local_id), | 897 | WherePredicateTypeTarget::TypeParam(local_id) => *local_id == param_id.local_id, |
897 | .. | 898 | }, |
898 | } => *local_id == param_id.local_id, | ||
899 | WherePredicate::Lifetime { .. } => false, | 899 | WherePredicate::Lifetime { .. } => false, |
900 | }) | 900 | }) |
901 | .flat_map(|pred| { | 901 | .flat_map(|pred| { |
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 5a5f48fd0..2053d8f56 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -774,3 +774,33 @@ fn foo(tuple: Tuple) { | |||
774 | "#]], | 774 | "#]], |
775 | ); | 775 | ); |
776 | } | 776 | } |
777 | |||
778 | #[test] | ||
779 | fn const_block_pattern() { | ||
780 | check_infer( | ||
781 | r#" | ||
782 | struct Foo(usize); | ||
783 | fn foo(foo: Foo) { | ||
784 | match foo { | ||
785 | const { Foo(15 + 32) } => {}, | ||
786 | _ => {} | ||
787 | } | ||
788 | }"#, | ||
789 | expect![[r#" | ||
790 | 26..29 'foo': Foo | ||
791 | 36..115 '{ ... } }': () | ||
792 | 42..113 'match ... }': () | ||
793 | 48..51 'foo': Foo | ||
794 | 62..84 'const ... 32) }': Foo | ||
795 | 68..84 '{ Foo(... 32) }': Foo | ||
796 | 70..73 'Foo': Foo(usize) -> Foo | ||
797 | 70..82 'Foo(15 + 32)': Foo | ||
798 | 74..76 '15': usize | ||
799 | 74..81 '15 + 32': usize | ||
800 | 79..81 '32': usize | ||
801 | 88..90 '{}': () | ||
802 | 100..101 '_': Foo | ||
803 | 105..107 '{}': () | ||
804 | "#]], | ||
805 | ); | ||
806 | } | ||
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index a569223b4..a61282d5a 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -1894,6 +1894,7 @@ fn effects_smoke_test() { | |||
1894 | let x = unsafe { 92 }; | 1894 | let x = unsafe { 92 }; |
1895 | let y = async { async { () }.await }; | 1895 | let y = async { async { () }.await }; |
1896 | let z = try { () }; | 1896 | let z = try { () }; |
1897 | let w = const { 92 }; | ||
1897 | let t = 'a: { 92 }; | 1898 | let t = 'a: { 92 }; |
1898 | } | 1899 | } |
1899 | 1900 | ||
@@ -1905,7 +1906,7 @@ fn effects_smoke_test() { | |||
1905 | } | 1906 | } |
1906 | "#, | 1907 | "#, |
1907 | expect![[r#" | 1908 | expect![[r#" |
1908 | 16..136 '{ ...2 }; }': () | 1909 | 16..162 '{ ...2 }; }': () |
1909 | 26..27 'x': i32 | 1910 | 26..27 'x': i32 |
1910 | 30..43 'unsafe { 92 }': i32 | 1911 | 30..43 'unsafe { 92 }': i32 |
1911 | 37..43 '{ 92 }': i32 | 1912 | 37..43 '{ 92 }': i32 |
@@ -1921,9 +1922,13 @@ fn effects_smoke_test() { | |||
1921 | 99..109 'try { () }': {unknown} | 1922 | 99..109 'try { () }': {unknown} |
1922 | 103..109 '{ () }': () | 1923 | 103..109 '{ () }': () |
1923 | 105..107 '()': () | 1924 | 105..107 '()': () |
1924 | 119..120 't': i32 | 1925 | 119..120 'w': i32 |
1925 | 127..133 '{ 92 }': i32 | 1926 | 123..135 'const { 92 }': i32 |
1926 | 129..131 '92': i32 | 1927 | 129..135 '{ 92 }': i32 |
1928 | 131..133 '92': i32 | ||
1929 | 145..146 't': i32 | ||
1930 | 153..159 '{ 92 }': i32 | ||
1931 | 155..157 '92': i32 | ||
1927 | "#]], | 1932 | "#]], |
1928 | ) | 1933 | ) |
1929 | } | 1934 | } |
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index 69eae6f79..2196af677 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs | |||
@@ -56,8 +56,13 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
56 | fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> { | 56 | fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> { |
57 | self.db.struct_datum(self.krate, struct_id) | 57 | self.db.struct_datum(self.krate, struct_id) |
58 | } | 58 | } |
59 | fn adt_repr(&self, _struct_id: AdtId) -> rust_ir::AdtRepr { | 59 | fn adt_repr(&self, _struct_id: AdtId) -> Arc<rust_ir::AdtRepr<Interner>> { |
60 | rust_ir::AdtRepr { repr_c: false, repr_packed: false } | 60 | // FIXME: keep track of these |
61 | Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None }) | ||
62 | } | ||
63 | fn discriminant_type(&self, _ty: chalk_ir::Ty<Interner>) -> chalk_ir::Ty<Interner> { | ||
64 | // FIXME: keep track of this | ||
65 | chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(&Interner) | ||
61 | } | 66 | } |
62 | fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> { | 67 | fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> { |
63 | self.db.impl_datum(self.krate, impl_id) | 68 | self.db.impl_datum(self.krate, impl_id) |
@@ -457,6 +462,7 @@ fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> { | |||
457 | "fn" => WellKnownTrait::Fn, | 462 | "fn" => WellKnownTrait::Fn, |
458 | "unsize" => WellKnownTrait::Unsize, | 463 | "unsize" => WellKnownTrait::Unsize, |
459 | "coerce_unsized" => WellKnownTrait::CoerceUnsized, | 464 | "coerce_unsized" => WellKnownTrait::CoerceUnsized, |
465 | "discriminant_kind" => WellKnownTrait::DiscriminantKind, | ||
460 | _ => return None, | 466 | _ => return None, |
461 | }) | 467 | }) |
462 | } | 468 | } |
@@ -473,6 +479,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str { | |||
473 | WellKnownTrait::Unsize => "unsize", | 479 | WellKnownTrait::Unsize => "unsize", |
474 | WellKnownTrait::Unpin => "unpin", | 480 | WellKnownTrait::Unpin => "unpin", |
475 | WellKnownTrait::CoerceUnsized => "coerce_unsized", | 481 | WellKnownTrait::CoerceUnsized => "coerce_unsized", |
482 | WellKnownTrait::DiscriminantKind => "discriminant_kind", | ||
476 | } | 483 | } |
477 | } | 484 | } |
478 | 485 | ||
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs index af880c065..65b79df0d 100644 --- a/crates/hir_ty/src/utils.rs +++ b/crates/hir_ty/src/utils.rs | |||
@@ -5,7 +5,9 @@ use std::sync::Arc; | |||
5 | use hir_def::{ | 5 | use hir_def::{ |
6 | adt::VariantData, | 6 | adt::VariantData, |
7 | db::DefDatabase, | 7 | db::DefDatabase, |
8 | generics::{GenericParams, TypeParamData, TypeParamProvenance, WherePredicateTypeTarget}, | 8 | generics::{ |
9 | GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, | ||
10 | }, | ||
9 | path::Path, | 11 | path::Path, |
10 | resolver::{HasResolver, TypeNs}, | 12 | resolver::{HasResolver, TypeNs}, |
11 | type_ref::TypeRef, | 13 | type_ref::TypeRef, |
@@ -27,7 +29,8 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | |||
27 | .where_predicates | 29 | .where_predicates |
28 | .iter() | 30 | .iter() |
29 | .filter_map(|pred| match pred { | 31 | .filter_map(|pred| match pred { |
30 | hir_def::generics::WherePredicate::TypeBound { target, bound } => match target { | 32 | WherePredicate::ForLifetime { target, bound, .. } |
33 | | WherePredicate::TypeBound { target, bound } => match target { | ||
31 | WherePredicateTypeTarget::TypeRef(TypeRef::Path(p)) | 34 | WherePredicateTypeTarget::TypeRef(TypeRef::Path(p)) |
32 | if p == &Path::from(name![Self]) => | 35 | if p == &Path::from(name![Self]) => |
33 | { | 36 | { |
@@ -38,7 +41,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | |||
38 | } | 41 | } |
39 | _ => None, | 42 | _ => None, |
40 | }, | 43 | }, |
41 | hir_def::generics::WherePredicate::Lifetime { .. } => None, | 44 | WherePredicate::Lifetime { .. } => None, |
42 | }) | 45 | }) |
43 | .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { | 46 | .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { |
44 | Some(TypeNs::TraitId(t)) => Some(t), | 47 | Some(TypeNs::TraitId(t)) => Some(t), |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 049f808dc..038273750 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -248,35 +248,7 @@ mod tests { | |||
248 | /// * a diagnostic is produced | 248 | /// * a diagnostic is produced |
249 | /// * this diagnostic fix trigger range touches the input cursor position | 249 | /// * this diagnostic fix trigger range touches the input cursor position |
250 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied | 250 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied |
251 | pub(super) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { | 251 | pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { |
252 | let after = trim_indent(ra_fixture_after); | ||
253 | |||
254 | let (analysis, file_position) = fixture::position(ra_fixture_before); | ||
255 | let diagnostic = analysis | ||
256 | .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) | ||
257 | .unwrap() | ||
258 | .pop() | ||
259 | .unwrap(); | ||
260 | let mut fix = diagnostic.fix.unwrap(); | ||
261 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; | ||
262 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | ||
263 | let actual = { | ||
264 | let mut actual = target_file_contents.to_string(); | ||
265 | edit.apply(&mut actual); | ||
266 | actual | ||
267 | }; | ||
268 | |||
269 | assert_eq_text!(&after, &actual); | ||
270 | assert!( | ||
271 | fix.fix_trigger_range.contains_inclusive(file_position.offset), | ||
272 | "diagnostic fix range {:?} does not touch cursor position {:?}", | ||
273 | fix.fix_trigger_range, | ||
274 | file_position.offset | ||
275 | ); | ||
276 | } | ||
277 | |||
278 | /// Similar to `check_fix`, but applies all the available fixes. | ||
279 | fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) { | ||
280 | let after = trim_indent(ra_fixture_after); | 252 | let after = trim_indent(ra_fixture_after); |
281 | 253 | ||
282 | let (analysis, file_position) = fixture::position(ra_fixture_before); | 254 | let (analysis, file_position) = fixture::position(ra_fixture_before); |
@@ -286,10 +258,12 @@ mod tests { | |||
286 | .pop() | 258 | .pop() |
287 | .unwrap(); | 259 | .unwrap(); |
288 | let fix = diagnostic.fix.unwrap(); | 260 | let fix = diagnostic.fix.unwrap(); |
289 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | ||
290 | let actual = { | 261 | let actual = { |
291 | let mut actual = target_file_contents.to_string(); | 262 | let file_id = fix.source_change.source_file_edits.first().unwrap().file_id; |
263 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); | ||
264 | |||
292 | // Go from the last one to the first one, so that ranges won't be affected by previous edits. | 265 | // Go from the last one to the first one, so that ranges won't be affected by previous edits. |
266 | // FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/4901#issuecomment-644675309 | ||
293 | for edit in fix.source_change.source_file_edits.iter().rev() { | 267 | for edit in fix.source_change.source_file_edits.iter().rev() { |
294 | edit.edit.apply(&mut actual); | 268 | edit.edit.apply(&mut actual); |
295 | } | 269 | } |
@@ -305,29 +279,6 @@ mod tests { | |||
305 | ); | 279 | ); |
306 | } | 280 | } |
307 | 281 | ||
308 | /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker | ||
309 | /// which has a fix that can apply to other files. | ||
310 | fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { | ||
311 | let ra_fixture_after = &trim_indent(ra_fixture_after); | ||
312 | let (analysis, file_pos) = fixture::position(ra_fixture_before); | ||
313 | let current_file_id = file_pos.file_id; | ||
314 | let diagnostic = analysis | ||
315 | .diagnostics(&DiagnosticsConfig::default(), current_file_id) | ||
316 | .unwrap() | ||
317 | .pop() | ||
318 | .unwrap(); | ||
319 | let mut fix = diagnostic.fix.unwrap(); | ||
320 | let edit = fix.source_change.source_file_edits.pop().unwrap(); | ||
321 | let changed_file_id = edit.file_id; | ||
322 | let before = analysis.file_text(changed_file_id).unwrap(); | ||
323 | let actual = { | ||
324 | let mut actual = before.to_string(); | ||
325 | edit.edit.apply(&mut actual); | ||
326 | actual | ||
327 | }; | ||
328 | assert_eq_text!(ra_fixture_after, &actual); | ||
329 | } | ||
330 | |||
331 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics | 282 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics |
332 | /// apply to the file containing the cursor. | 283 | /// apply to the file containing the cursor. |
333 | pub(crate) fn check_no_diagnostics(ra_fixture: &str) { | 284 | pub(crate) fn check_no_diagnostics(ra_fixture: &str) { |
@@ -619,6 +570,7 @@ fn test_fn() { | |||
619 | ), | 570 | ), |
620 | path: "foo.rs", | 571 | path: "foo.rs", |
621 | }, | 572 | }, |
573 | initial_contents: "", | ||
622 | }, | 574 | }, |
623 | ], | 575 | ], |
624 | is_snippet: false, | 576 | is_snippet: false, |
@@ -763,25 +715,25 @@ struct Foo { | |||
763 | 715 | ||
764 | #[test] | 716 | #[test] |
765 | fn test_add_field_in_other_file_from_usage() { | 717 | fn test_add_field_in_other_file_from_usage() { |
766 | check_apply_diagnostic_fix_in_other_file( | 718 | check_fix( |
767 | r" | 719 | r#" |
768 | //- /main.rs | 720 | //- /main.rs |
769 | mod foo; | 721 | mod foo; |
770 | 722 | ||
771 | fn main() { | 723 | fn main() { |
772 | <|>foo::Foo { bar: 3, baz: false}; | 724 | foo::Foo { bar: 3, <|>baz: false}; |
773 | } | 725 | } |
774 | //- /foo.rs | 726 | //- /foo.rs |
775 | struct Foo { | 727 | struct Foo { |
776 | bar: i32 | 728 | bar: i32 |
777 | } | 729 | } |
778 | ", | 730 | "#, |
779 | r" | 731 | r#" |
780 | struct Foo { | 732 | struct Foo { |
781 | bar: i32, | 733 | bar: i32, |
782 | pub(crate) baz: bool | 734 | pub(crate) baz: bool |
783 | } | 735 | } |
784 | ", | 736 | "#, |
785 | ) | 737 | ) |
786 | } | 738 | } |
787 | 739 | ||
@@ -801,7 +753,7 @@ struct Foo { | |||
801 | 753 | ||
802 | #[test] | 754 | #[test] |
803 | fn test_rename_incorrect_case() { | 755 | fn test_rename_incorrect_case() { |
804 | check_fixes( | 756 | check_fix( |
805 | r#" | 757 | r#" |
806 | pub struct test_struct<|> { one: i32 } | 758 | pub struct test_struct<|> { one: i32 } |
807 | 759 | ||
@@ -818,7 +770,7 @@ pub fn some_fn(val: TestStruct) -> TestStruct { | |||
818 | "#, | 770 | "#, |
819 | ); | 771 | ); |
820 | 772 | ||
821 | check_fixes( | 773 | check_fix( |
822 | r#" | 774 | r#" |
823 | pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { | 775 | pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { |
824 | NonSnakeCase | 776 | NonSnakeCase |
@@ -831,7 +783,7 @@ pub fn some_fn(non_snake_case: u8) -> u8 { | |||
831 | "#, | 783 | "#, |
832 | ); | 784 | ); |
833 | 785 | ||
834 | check_fixes( | 786 | check_fix( |
835 | r#" | 787 | r#" |
836 | pub fn SomeFn<|>(val: u8) -> u8 { | 788 | pub fn SomeFn<|>(val: u8) -> u8 { |
837 | if val != 0 { SomeFn(val - 1) } else { val } | 789 | if val != 0 { SomeFn(val - 1) } else { val } |
@@ -844,7 +796,7 @@ pub fn some_fn(val: u8) -> u8 { | |||
844 | "#, | 796 | "#, |
845 | ); | 797 | ); |
846 | 798 | ||
847 | check_fixes( | 799 | check_fix( |
848 | r#" | 800 | r#" |
849 | fn some_fn() { | 801 | fn some_fn() { |
850 | let whatAWeird_Formatting<|> = 10; | 802 | let whatAWeird_Formatting<|> = 10; |
@@ -873,7 +825,7 @@ fn foo() { | |||
873 | 825 | ||
874 | #[test] | 826 | #[test] |
875 | fn test_rename_incorrect_case_struct_method() { | 827 | fn test_rename_incorrect_case_struct_method() { |
876 | check_fixes( | 828 | check_fix( |
877 | r#" | 829 | r#" |
878 | pub struct TestStruct; | 830 | pub struct TestStruct; |
879 | 831 | ||
@@ -894,4 +846,17 @@ impl TestStruct { | |||
894 | "#, | 846 | "#, |
895 | ); | 847 | ); |
896 | } | 848 | } |
849 | |||
850 | #[test] | ||
851 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
852 | let input = r#"fn FOO<|>() {}"#; | ||
853 | let expected = r#"fn foo() {}"#; | ||
854 | |||
855 | let (analysis, file_position) = fixture::position(input); | ||
856 | let diagnostics = | ||
857 | analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap(); | ||
858 | assert_eq!(diagnostics.len(), 1); | ||
859 | |||
860 | check_fix(input, expected); | ||
861 | } | ||
897 | } | 862 | } |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index e8b896623..d79f5c170 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -40,6 +40,7 @@ impl DiagnosticWithFix for UnresolvedModule { | |||
40 | anchor: self.file.original_file(sema.db), | 40 | anchor: self.file.original_file(sema.db), |
41 | path: self.candidate.clone(), | 41 | path: self.candidate.clone(), |
42 | }, | 42 | }, |
43 | initial_contents: "".to_string(), | ||
43 | } | 44 | } |
44 | .into(), | 45 | .into(), |
45 | unresolved_module.syntax().text_range(), | 46 | unresolved_module.syntax().text_range(), |
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index cd8ec54fa..6431e7d6d 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -5,7 +5,7 @@ use std::fmt; | |||
5 | use either::Either; | 5 | use either::Either; |
6 | use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; | 6 | use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; |
7 | use ide_db::{ | 7 | use ide_db::{ |
8 | base_db::{FileId, SourceDatabase}, | 8 | base_db::{FileId, FileRange, SourceDatabase}, |
9 | symbol_index::FileSymbolKind, | 9 | symbol_index::FileSymbolKind, |
10 | }; | 10 | }; |
11 | use ide_db::{defs::Definition, RootDatabase}; | 11 | use ide_db::{defs::Definition, RootDatabase}; |
@@ -28,6 +28,7 @@ pub enum SymbolKind { | |||
28 | ValueParam, | 28 | ValueParam, |
29 | SelfParam, | 29 | SelfParam, |
30 | Local, | 30 | Local, |
31 | Label, | ||
31 | Function, | 32 | Function, |
32 | Const, | 33 | Const, |
33 | Static, | 34 | Static, |
@@ -223,6 +224,7 @@ impl TryToNav for Definition { | |||
223 | Definition::Local(it) => Some(it.to_nav(db)), | 224 | Definition::Local(it) => Some(it.to_nav(db)), |
224 | Definition::TypeParam(it) => Some(it.to_nav(db)), | 225 | Definition::TypeParam(it) => Some(it.to_nav(db)), |
225 | Definition::LifetimeParam(it) => Some(it.to_nav(db)), | 226 | Definition::LifetimeParam(it) => Some(it.to_nav(db)), |
227 | Definition::Label(it) => Some(it.to_nav(db)), | ||
226 | } | 228 | } |
227 | } | 229 | } |
228 | } | 230 | } |
@@ -421,6 +423,27 @@ impl ToNav for hir::Local { | |||
421 | } | 423 | } |
422 | } | 424 | } |
423 | 425 | ||
426 | impl ToNav for hir::Label { | ||
427 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
428 | let src = self.source(db); | ||
429 | let node = src.value.syntax(); | ||
430 | let FileRange { file_id, range } = src.with_value(node).original_file_range(db); | ||
431 | let focus_range = | ||
432 | src.value.lifetime().and_then(|lt| lt.lifetime_ident_token()).map(|lt| lt.text_range()); | ||
433 | let name = self.name(db).to_string().into(); | ||
434 | NavigationTarget { | ||
435 | file_id, | ||
436 | name, | ||
437 | kind: Some(SymbolKind::Label), | ||
438 | full_range: range, | ||
439 | focus_range, | ||
440 | container_name: None, | ||
441 | description: None, | ||
442 | docs: None, | ||
443 | } | ||
444 | } | ||
445 | } | ||
446 | |||
424 | impl ToNav for hir::TypeParam { | 447 | impl ToNav for hir::TypeParam { |
425 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 448 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
426 | let src = self.source(db); | 449 | let src = self.source(db); |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index b61ea0b3e..e10516f43 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -193,7 +193,8 @@ fn rewrite_intra_doc_link( | |||
193 | Definition::SelfType(_) | 193 | Definition::SelfType(_) |
194 | | Definition::Local(_) | 194 | | Definition::Local(_) |
195 | | Definition::TypeParam(_) | 195 | | Definition::TypeParam(_) |
196 | | Definition::LifetimeParam(_) => return None, | 196 | | Definition::LifetimeParam(_) |
197 | | Definition::Label(_) => return None, | ||
197 | }?; | 198 | }?; |
198 | let krate = resolved.module(db)?.krate(); | 199 | let krate = resolved.module(db)?.krate(); |
199 | let canonical_path = resolved.canonical_path(db)?; | 200 | let canonical_path = resolved.canonical_path(db)?; |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 7a12e9965..912144f8b 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -750,6 +750,31 @@ fn test() { | |||
750 | } | 750 | } |
751 | 751 | ||
752 | #[test] | 752 | #[test] |
753 | fn goto_through_included_file() { | ||
754 | check( | ||
755 | r#" | ||
756 | //- /main.rs | ||
757 | #[rustc_builtin_macro] | ||
758 | macro_rules! include {} | ||
759 | |||
760 | include!("foo.rs"); | ||
761 | //^^^^^^^^^^^^^^^^^^^ | ||
762 | |||
763 | fn f() { | ||
764 | foo<|>(); | ||
765 | } | ||
766 | |||
767 | mod confuse_index { | ||
768 | pub fn foo() {} | ||
769 | } | ||
770 | |||
771 | //- /foo.rs | ||
772 | fn foo() {} | ||
773 | "#, | ||
774 | ); | ||
775 | } | ||
776 | |||
777 | #[test] | ||
753 | fn goto_for_type_param() { | 778 | fn goto_for_type_param() { |
754 | check( | 779 | check( |
755 | r#" | 780 | r#" |
@@ -1077,4 +1102,47 @@ fn foo<'foobar>(_: &'foobar ()) { | |||
1077 | }"#, | 1102 | }"#, |
1078 | ) | 1103 | ) |
1079 | } | 1104 | } |
1105 | |||
1106 | #[test] | ||
1107 | #[ignore] // requires the HIR to somehow track these hrtb lifetimes | ||
1108 | fn goto_lifetime_hrtb() { | ||
1109 | check( | ||
1110 | r#"trait Foo<T> {} | ||
1111 | fn foo<T>() where for<'a> T: Foo<&'a<|> (u8, u16)>, {} | ||
1112 | //^^ | ||
1113 | "#, | ||
1114 | ); | ||
1115 | check( | ||
1116 | r#"trait Foo<T> {} | ||
1117 | fn foo<T>() where for<'a<|>> T: Foo<&'a (u8, u16)>, {} | ||
1118 | //^^ | ||
1119 | "#, | ||
1120 | ); | ||
1121 | } | ||
1122 | |||
1123 | #[test] | ||
1124 | #[ignore] // requires ForTypes to be implemented | ||
1125 | fn goto_lifetime_hrtb_for_type() { | ||
1126 | check( | ||
1127 | r#"trait Foo<T> {} | ||
1128 | fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {} | ||
1129 | //^^ | ||
1130 | "#, | ||
1131 | ); | ||
1132 | } | ||
1133 | |||
1134 | #[test] | ||
1135 | fn goto_label() { | ||
1136 | check( | ||
1137 | r#" | ||
1138 | fn foo<'foo>(_: &'foo ()) { | ||
1139 | 'foo: { | ||
1140 | //^^^^ | ||
1141 | 'bar: loop { | ||
1142 | break 'foo<|>; | ||
1143 | } | ||
1144 | } | ||
1145 | }"#, | ||
1146 | ) | ||
1147 | } | ||
1080 | } | 1148 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 52f993cc9..73245fbe7 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -370,7 +370,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
370 | Adt::Enum(it) => from_def_source(db, it, mod_path), | 370 | Adt::Enum(it) => from_def_source(db, it, mod_path), |
371 | }) | 371 | }) |
372 | } | 372 | } |
373 | Definition::TypeParam(_) | Definition::LifetimeParam(_) => { | 373 | Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => { |
374 | // FIXME: Hover for generic param | 374 | // FIXME: Hover for generic param |
375 | None | 375 | None |
376 | } | 376 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index dbad9a84f..41eb139d1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -79,7 +79,7 @@ pub use crate::{ | |||
79 | HighlightedRange, | 79 | HighlightedRange, |
80 | }, | 80 | }, |
81 | }; | 81 | }; |
82 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; | 82 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind}; |
83 | pub use completion::{ | 83 | pub use completion::{ |
84 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, | 84 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, |
85 | CompletionScore, ImportEdit, InsertTextFormat, | 85 | CompletionScore, ImportEdit, InsertTextFormat, |
@@ -490,23 +490,17 @@ impl Analysis { | |||
490 | .unwrap_or_default()) | 490 | .unwrap_or_default()) |
491 | } | 491 | } |
492 | 492 | ||
493 | /// Computes resolved assists with source changes for the given position. | 493 | /// Computes assists (aka code actions aka intentions) for the given |
494 | pub fn resolved_assists( | 494 | /// position. If `resolve == false`, computes enough info to show the |
495 | &self, | 495 | /// lightbulb list in the editor, but doesn't compute actual edits, to |
496 | config: &AssistConfig, | 496 | /// improve performance. |
497 | frange: FileRange, | 497 | pub fn assists( |
498 | ) -> Cancelable<Vec<ResolvedAssist>> { | ||
499 | self.with_db(|db| assists::Assist::resolved(db, config, frange)) | ||
500 | } | ||
501 | |||
502 | /// Computes unresolved assists (aka code actions aka intentions) for the given | ||
503 | /// position. | ||
504 | pub fn unresolved_assists( | ||
505 | &self, | 498 | &self, |
506 | config: &AssistConfig, | 499 | config: &AssistConfig, |
500 | resolve: bool, | ||
507 | frange: FileRange, | 501 | frange: FileRange, |
508 | ) -> Cancelable<Vec<Assist>> { | 502 | ) -> Cancelable<Vec<Assist>> { |
509 | self.with_db(|db| Assist::unresolved(db, config, frange)) | 503 | self.with_db(|db| Assist::get(db, config, resolve, frange)) |
510 | } | 504 | } |
511 | 505 | ||
512 | /// Computes the set of diagnostics for the given file. | 506 | /// Computes the set of diagnostics for the given file. |
@@ -535,6 +529,14 @@ impl Analysis { | |||
535 | self.with_db(|db| references::rename::prepare_rename(db, position)) | 529 | self.with_db(|db| references::rename::prepare_rename(db, position)) |
536 | } | 530 | } |
537 | 531 | ||
532 | pub fn will_rename_file( | ||
533 | &self, | ||
534 | file_id: FileId, | ||
535 | new_name_stem: &str, | ||
536 | ) -> Cancelable<Option<SourceChange>> { | ||
537 | self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) | ||
538 | } | ||
539 | |||
538 | pub fn structural_search_replace( | 540 | pub fn structural_search_replace( |
539 | &self, | 541 | &self, |
540 | query: &str, | 542 | query: &str, |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 18ea19305..21b2d7ca1 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -130,7 +130,7 @@ pub(crate) fn find_all_refs( | |||
130 | kind = ReferenceKind::FieldShorthandForLocal; | 130 | kind = ReferenceKind::FieldShorthandForLocal; |
131 | } | 131 | } |
132 | } | 132 | } |
133 | } else if let Definition::LifetimeParam(_) = def { | 133 | } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) { |
134 | kind = ReferenceKind::Lifetime; | 134 | kind = ReferenceKind::Lifetime; |
135 | }; | 135 | }; |
136 | 136 | ||
@@ -147,20 +147,20 @@ fn find_name( | |||
147 | ) -> Option<RangeInfo<Definition>> { | 147 | ) -> Option<RangeInfo<Definition>> { |
148 | if let Some(name) = opt_name { | 148 | if let Some(name) = opt_name { |
149 | let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db); | 149 | let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db); |
150 | let range = name.syntax().text_range(); | 150 | let FileRange { range, .. } = sema.original_range(name.syntax()); |
151 | return Some(RangeInfo::new(range, def)); | 151 | return Some(RangeInfo::new(range, def)); |
152 | } | 152 | } |
153 | 153 | ||
154 | let (text_range, def) = if let Some(lifetime) = | 154 | let (FileRange { range, .. }, def) = if let Some(lifetime) = |
155 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) | 155 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) |
156 | { | 156 | { |
157 | if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime) | 157 | if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime) |
158 | .map(|class| NameRefClass::referenced(class, sema.db)) | 158 | .map(|class| NameRefClass::referenced(class, sema.db)) |
159 | { | 159 | { |
160 | (lifetime.syntax().text_range(), def) | 160 | (sema.original_range(lifetime.syntax()), def) |
161 | } else { | 161 | } else { |
162 | ( | 162 | ( |
163 | lifetime.syntax().text_range(), | 163 | sema.original_range(lifetime.syntax()), |
164 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db), | 164 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db), |
165 | ) | 165 | ) |
166 | } | 166 | } |
@@ -168,11 +168,11 @@ fn find_name( | |||
168 | let name_ref = | 168 | let name_ref = |
169 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; | 169 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; |
170 | ( | 170 | ( |
171 | name_ref.syntax().text_range(), | 171 | sema.original_range(name_ref.syntax()), |
172 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db), | 172 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db), |
173 | ) | 173 | ) |
174 | }; | 174 | }; |
175 | Some(RangeInfo::new(text_range, def)) | 175 | Some(RangeInfo::new(range, def)) |
176 | } | 176 | } |
177 | 177 | ||
178 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { | 178 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { |
@@ -1086,4 +1086,62 @@ impl<'a> Foo<'a> for &'a () { | |||
1086 | "#]], | 1086 | "#]], |
1087 | ); | 1087 | ); |
1088 | } | 1088 | } |
1089 | |||
1090 | #[test] | ||
1091 | fn test_map_range_to_original() { | ||
1092 | check( | ||
1093 | r#" | ||
1094 | macro_rules! foo {($i:ident) => {$i} } | ||
1095 | fn main() { | ||
1096 | let a<|> = "test"; | ||
1097 | foo!(a); | ||
1098 | } | ||
1099 | "#, | ||
1100 | expect![[r#" | ||
1101 | a Local FileId(0) 59..60 Other | ||
1102 | |||
1103 | FileId(0) 80..81 Other Read | ||
1104 | "#]], | ||
1105 | ); | ||
1106 | } | ||
1107 | |||
1108 | #[test] | ||
1109 | fn test_map_range_to_original_ref() { | ||
1110 | check( | ||
1111 | r#" | ||
1112 | macro_rules! foo {($i:ident) => {$i} } | ||
1113 | fn main() { | ||
1114 | let a = "test"; | ||
1115 | foo!(a<|>); | ||
1116 | } | ||
1117 | "#, | ||
1118 | expect![[r#" | ||
1119 | a Local FileId(0) 59..60 Other | ||
1120 | |||
1121 | FileId(0) 80..81 Other Read | ||
1122 | "#]], | ||
1123 | ); | ||
1124 | } | ||
1125 | |||
1126 | #[test] | ||
1127 | fn test_find_labels() { | ||
1128 | check( | ||
1129 | r#" | ||
1130 | fn foo<'a>() -> &'a () { | ||
1131 | 'a: loop { | ||
1132 | 'b: loop { | ||
1133 | continue 'a<|>; | ||
1134 | } | ||
1135 | break 'a; | ||
1136 | } | ||
1137 | } | ||
1138 | "#, | ||
1139 | expect![[r#" | ||
1140 | 'a Label FileId(0) 29..32 29..31 Lifetime | ||
1141 | |||
1142 | FileId(0) 80..82 Lifetime | ||
1143 | FileId(0) 108..110 Lifetime | ||
1144 | "#]], | ||
1145 | ); | ||
1146 | } | ||
1089 | } | 1147 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index cd721b7eb..854bf194e 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -6,7 +6,7 @@ use std::{ | |||
6 | }; | 6 | }; |
7 | 7 | ||
8 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; | 8 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; |
9 | use ide_db::base_db::{AnchoredPathBuf, FileRange, SourceDatabaseExt}; | 9 | use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt}; |
10 | use ide_db::{ | 10 | use ide_db::{ |
11 | defs::{Definition, NameClass, NameRefClass}, | 11 | defs::{Definition, NameClass, NameRefClass}, |
12 | RootDatabase, | 12 | RootDatabase, |
@@ -110,6 +110,23 @@ pub(crate) fn rename_with_semantics( | |||
110 | } | 110 | } |
111 | } | 111 | } |
112 | 112 | ||
113 | pub(crate) fn will_rename_file( | ||
114 | db: &RootDatabase, | ||
115 | file_id: FileId, | ||
116 | new_name_stem: &str, | ||
117 | ) -> Option<SourceChange> { | ||
118 | let sema = Semantics::new(db); | ||
119 | let module = sema.to_module_def(file_id)?; | ||
120 | |||
121 | let decl = module.declaration_source(db)?; | ||
122 | let range = decl.value.name()?.syntax().text_range(); | ||
123 | |||
124 | let position = FilePosition { file_id: decl.file_id.original_file(db), offset: range.start() }; | ||
125 | let mut change = rename_mod(&sema, position, module, new_name_stem).ok()?.info; | ||
126 | change.file_system_edits.clear(); | ||
127 | Some(change) | ||
128 | } | ||
129 | |||
113 | fn find_module_at_offset( | 130 | fn find_module_at_offset( |
114 | sema: &Semantics<RootDatabase>, | 131 | sema: &Semantics<RootDatabase>, |
115 | position: FilePosition, | 132 | position: FilePosition, |
@@ -1523,4 +1540,29 @@ fn main() { | |||
1523 | }"#, | 1540 | }"#, |
1524 | ); | 1541 | ); |
1525 | } | 1542 | } |
1543 | |||
1544 | #[test] | ||
1545 | fn test_rename_label() { | ||
1546 | check( | ||
1547 | "'foo", | ||
1548 | r#" | ||
1549 | fn foo<'a>() -> &'a () { | ||
1550 | 'a: { | ||
1551 | 'b: loop { | ||
1552 | break 'a<|>; | ||
1553 | } | ||
1554 | } | ||
1555 | } | ||
1556 | "#, | ||
1557 | r#" | ||
1558 | fn foo<'a>() -> &'a () { | ||
1559 | 'foo: { | ||
1560 | 'b: loop { | ||
1561 | break 'foo; | ||
1562 | } | ||
1563 | } | ||
1564 | } | ||
1565 | "#, | ||
1566 | ) | ||
1567 | } | ||
1526 | } | 1568 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 2f2b99130..891183266 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -193,7 +193,7 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op | |||
193 | if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { | 193 | if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { |
194 | if let Some(adt) = imp.target_ty(sema.db).as_adt() { | 194 | if let Some(adt) = imp.target_ty(sema.db).as_adt() { |
195 | let name = adt.name(sema.db).to_string(); | 195 | let name = adt.name(sema.db).to_string(); |
196 | let idx = path.rfind(':').unwrap_or(0); | 196 | let idx = path.rfind(':').map_or(0, |idx| idx + 1); |
197 | let (prefix, suffix) = path.split_at(idx); | 197 | let (prefix, suffix) = path.split_at(idx); |
198 | return format!("{}{}::{}", prefix, name, suffix); | 198 | return format!("{}{}::{}", prefix, name, suffix); |
199 | } | 199 | } |
@@ -931,4 +931,42 @@ mod test_mod { | |||
931 | "#]], | 931 | "#]], |
932 | ); | 932 | ); |
933 | } | 933 | } |
934 | |||
935 | #[test] | ||
936 | fn test_doc_runnables_impl_mod() { | ||
937 | check( | ||
938 | r#" | ||
939 | //- /lib.rs | ||
940 | mod foo; | ||
941 | //- /foo.rs | ||
942 | struct Foo;<|> | ||
943 | impl Foo { | ||
944 | /// ``` | ||
945 | /// let x = 5; | ||
946 | /// ``` | ||
947 | fn foo() {} | ||
948 | } | ||
949 | "#, | ||
950 | &[&DOCTEST], | ||
951 | expect![[r#" | ||
952 | [ | ||
953 | Runnable { | ||
954 | nav: NavigationTarget { | ||
955 | file_id: FileId( | ||
956 | 1, | ||
957 | ), | ||
958 | full_range: 27..81, | ||
959 | name: "foo", | ||
960 | }, | ||
961 | kind: DocTest { | ||
962 | test_id: Path( | ||
963 | "foo::Foo::foo", | ||
964 | ), | ||
965 | }, | ||
966 | cfg: None, | ||
967 | }, | ||
968 | ] | ||
969 | "#]], | ||
970 | ); | ||
971 | } | ||
934 | } | 972 | } |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 00c717c7c..5ad96581b 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -560,10 +560,20 @@ fn highlight_element( | |||
560 | CHAR => HighlightTag::CharLiteral.into(), | 560 | CHAR => HighlightTag::CharLiteral.into(), |
561 | QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow, | 561 | QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow, |
562 | LIFETIME => { | 562 | LIFETIME => { |
563 | let h = Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)); | 563 | let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); |
564 | match element.parent().map(|it| it.kind()) { | 564 | |
565 | Some(LIFETIME_PARAM) | Some(LABEL) => h | HighlightModifier::Definition, | 565 | match NameClass::classify_lifetime(sema, &lifetime) { |
566 | _ => h, | 566 | Some(NameClass::Definition(def)) => { |
567 | highlight_def(db, def) | HighlightModifier::Definition | ||
568 | } | ||
569 | None => match NameRefClass::classify_lifetime(sema, &lifetime) { | ||
570 | Some(NameRefClass::Definition(def)) => highlight_def(db, def), | ||
571 | _ => Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)), | ||
572 | }, | ||
573 | _ => { | ||
574 | Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)) | ||
575 | | HighlightModifier::Definition | ||
576 | } | ||
567 | } | 577 | } |
568 | } | 578 | } |
569 | p if p.is_punct() => match p { | 579 | p if p.is_punct() => match p { |
@@ -825,6 +835,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
825 | return h; | 835 | return h; |
826 | } | 836 | } |
827 | Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam), | 837 | Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam), |
838 | Definition::Label(_) => HighlightTag::Symbol(SymbolKind::Label), | ||
828 | } | 839 | } |
829 | .into() | 840 | .into() |
830 | } | 841 | } |
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index abcc5cccc..99ba3a59d 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs | |||
@@ -64,6 +64,7 @@ body { margin: 0; } | |||
64 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 64 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
65 | 65 | ||
66 | .lifetime { color: #DFAF8F; font-style: italic; } | 66 | .lifetime { color: #DFAF8F; font-style: italic; } |
67 | .label { color: #DFAF8F; font-style: italic; } | ||
67 | .comment { color: #7F9F7F; } | 68 | .comment { color: #7F9F7F; } |
68 | .documentation { color: #629755; } | 69 | .documentation { color: #629755; } |
69 | .injected { opacity: 0.65 ; } | 70 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 974f54fa0..2a6cc0cab 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs | |||
@@ -80,6 +80,7 @@ impl HighlightTag { | |||
80 | SymbolKind::LifetimeParam => "lifetime", | 80 | SymbolKind::LifetimeParam => "lifetime", |
81 | SymbolKind::Macro => "macro", | 81 | SymbolKind::Macro => "macro", |
82 | SymbolKind::Local => "variable", | 82 | SymbolKind::Local => "variable", |
83 | SymbolKind::Label => "label", | ||
83 | SymbolKind::ValueParam => "value_param", | 84 | SymbolKind::ValueParam => "value_param", |
84 | SymbolKind::SelfParam => "self_keyword", | 85 | SymbolKind::SelfParam => "self_keyword", |
85 | SymbolKind::Impl => "self_type", | 86 | SymbolKind::Impl => "self_type", |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index db6f32d33..506ebe60e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 4e511baa9..4dd7413ba 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 800d894c7..ed452586a 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 7f18ad297..92e7dc3e4 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index c843b5085..31dad5d42 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index d26f48516..e3a0aa317 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 588e86a34..72ff9dd40 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
@@ -194,6 +195,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
194 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> | 195 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> |
195 | 196 | ||
196 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span> | 197 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span> |
198 | |||
199 | <span class="label declaration">'foo</span><span class="punctuation">:</span> <span class="keyword control">loop</span> <span class="punctuation">{</span> | ||
200 | <span class="keyword control">break</span> <span class="label">'foo</span><span class="punctuation">;</span> | ||
201 | <span class="keyword control">continue</span> <span class="label">'foo</span><span class="punctuation">;</span> | ||
202 | <span class="punctuation">}</span> | ||
197 | <span class="punctuation">}</span> | 203 | <span class="punctuation">}</span> |
198 | 204 | ||
199 | <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="punctuation">{</span> | 205 | <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="punctuation">{</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html index c7589605f..8b3dfa69f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index f53d2c3ba..e0df0d2b5 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -168,6 +168,11 @@ fn main() { | |||
168 | let baz = -baz; | 168 | let baz = -baz; |
169 | 169 | ||
170 | let _ = !true; | 170 | let _ = !true; |
171 | |||
172 | 'foo: loop { | ||
173 | break 'foo; | ||
174 | continue 'foo; | ||
175 | } | ||
171 | } | 176 | } |
172 | 177 | ||
173 | enum Option<T> { | 178 | enum Option<T> { |
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index 9d7dce1d4..d33a6cb86 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs | |||
@@ -6,8 +6,8 @@ | |||
6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). | 6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). |
7 | 7 | ||
8 | use hir::{ | 8 | use hir::{ |
9 | db::HirDatabase, Crate, Field, HasVisibility, Impl, LifetimeParam, Local, MacroDef, Module, | 9 | db::HirDatabase, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, |
10 | ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, | 10 | Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, |
11 | }; | 11 | }; |
12 | use syntax::{ | 12 | use syntax::{ |
13 | ast::{self, AstNode}, | 13 | ast::{self, AstNode}, |
@@ -26,7 +26,7 @@ pub enum Definition { | |||
26 | Local(Local), | 26 | Local(Local), |
27 | TypeParam(TypeParam), | 27 | TypeParam(TypeParam), |
28 | LifetimeParam(LifetimeParam), | 28 | LifetimeParam(LifetimeParam), |
29 | // FIXME: Label | 29 | Label(Label), |
30 | } | 30 | } |
31 | 31 | ||
32 | impl Definition { | 32 | impl Definition { |
@@ -39,6 +39,7 @@ impl Definition { | |||
39 | Definition::Local(it) => Some(it.module(db)), | 39 | Definition::Local(it) => Some(it.module(db)), |
40 | Definition::TypeParam(it) => Some(it.module(db)), | 40 | Definition::TypeParam(it) => Some(it.module(db)), |
41 | Definition::LifetimeParam(it) => Some(it.module(db)), | 41 | Definition::LifetimeParam(it) => Some(it.module(db)), |
42 | Definition::Label(it) => Some(it.module(db)), | ||
42 | } | 43 | } |
43 | } | 44 | } |
44 | 45 | ||
@@ -51,6 +52,7 @@ impl Definition { | |||
51 | Definition::Local(_) => None, | 52 | Definition::Local(_) => None, |
52 | Definition::TypeParam(_) => None, | 53 | Definition::TypeParam(_) => None, |
53 | Definition::LifetimeParam(_) => None, | 54 | Definition::LifetimeParam(_) => None, |
55 | Definition::Label(_) => None, | ||
54 | } | 56 | } |
55 | } | 57 | } |
56 | 58 | ||
@@ -77,6 +79,7 @@ impl Definition { | |||
77 | Definition::Local(it) => it.name(db)?, | 79 | Definition::Local(it) => it.name(db)?, |
78 | Definition::TypeParam(it) => it.name(db), | 80 | Definition::TypeParam(it) => it.name(db), |
79 | Definition::LifetimeParam(it) => it.name(db), | 81 | Definition::LifetimeParam(it) => it.name(db), |
82 | Definition::Label(it) => it.name(db), | ||
80 | }; | 83 | }; |
81 | Some(name) | 84 | Some(name) |
82 | } | 85 | } |
@@ -248,7 +251,10 @@ impl NameClass { | |||
248 | let def = sema.to_def(&it)?; | 251 | let def = sema.to_def(&it)?; |
249 | Some(NameClass::Definition(Definition::LifetimeParam(def))) | 252 | Some(NameClass::Definition(Definition::LifetimeParam(def))) |
250 | }, | 253 | }, |
251 | ast::Label(_it) => None, | 254 | ast::Label(it) => { |
255 | let def = sema.to_def(&it)?; | ||
256 | Some(NameClass::Definition(Definition::Label(def))) | ||
257 | }, | ||
252 | _ => None, | 258 | _ => None, |
253 | } | 259 | } |
254 | } | 260 | } |
@@ -370,6 +376,9 @@ impl NameRefClass { | |||
370 | let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string()); | 376 | let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string()); |
371 | let parent = lifetime.syntax().parent()?; | 377 | let parent = lifetime.syntax().parent()?; |
372 | match parent.kind() { | 378 | match parent.kind() { |
379 | SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { | ||
380 | sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition) | ||
381 | } | ||
373 | SyntaxKind::LIFETIME_ARG | 382 | SyntaxKind::LIFETIME_ARG |
374 | | SyntaxKind::SELF_PARAM | 383 | | SyntaxKind::SELF_PARAM |
375 | | SyntaxKind::TYPE_BOUND | 384 | | SyntaxKind::TYPE_BOUND |
@@ -387,7 +396,6 @@ impl NameRefClass { | |||
387 | .map(Definition::LifetimeParam) | 396 | .map(Definition::LifetimeParam) |
388 | .map(NameRefClass::Definition) | 397 | .map(NameRefClass::Definition) |
389 | } | 398 | } |
390 | SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => None, | ||
391 | _ => None, | 399 | _ => None, |
392 | } | 400 | } |
393 | } | 401 | } |
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs index e87d98dad..10c0abdac 100644 --- a/crates/ide_db/src/source_change.rs +++ b/crates/ide_db/src/source_change.rs | |||
@@ -44,7 +44,7 @@ impl From<Vec<SourceFileEdit>> for SourceChange { | |||
44 | 44 | ||
45 | #[derive(Debug, Clone)] | 45 | #[derive(Debug, Clone)] |
46 | pub enum FileSystemEdit { | 46 | pub enum FileSystemEdit { |
47 | CreateFile { dst: AnchoredPathBuf }, | 47 | CreateFile { dst: AnchoredPathBuf, initial_contents: String }, |
48 | MoveFile { src: FileId, dst: AnchoredPathBuf }, | 48 | MoveFile { src: FileId, dst: AnchoredPathBuf }, |
49 | } | 49 | } |
50 | 50 | ||
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index f10e7a9b6..451fa1456 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -1004,6 +1004,18 @@ fn test_underscore() { | |||
1004 | } | 1004 | } |
1005 | 1005 | ||
1006 | #[test] | 1006 | #[test] |
1007 | fn test_vertical_bar_with_pat() { | ||
1008 | parse_macro( | ||
1009 | r#" | ||
1010 | macro_rules! foo { | ||
1011 | (| $pat:pat | ) => { 0 } | ||
1012 | } | ||
1013 | "#, | ||
1014 | ) | ||
1015 | .assert_expand_items(r#"foo! { | x | }"#, r#"0"#); | ||
1016 | } | ||
1017 | |||
1018 | #[test] | ||
1007 | fn test_lifetime() { | 1019 | fn test_lifetime() { |
1008 | parse_macro( | 1020 | parse_macro( |
1009 | r#" | 1021 | r#" |
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 1a078f6b4..f08c8bab7 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -55,7 +55,7 @@ pub(crate) mod fragments { | |||
55 | use super::*; | 55 | use super::*; |
56 | 56 | ||
57 | pub(crate) use super::{ | 57 | pub(crate) use super::{ |
58 | expressions::block_expr, paths::type_path as path, patterns::pattern, types::type_, | 58 | expressions::block_expr, paths::type_path as path, patterns::pattern_single, types::type_, |
59 | }; | 59 | }; |
60 | 60 | ||
61 | pub(crate) fn expr(p: &mut Parser) { | 61 | pub(crate) fn expr(p: &mut Parser) { |
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 18b63feb7..c7a3556a7 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs | |||
@@ -46,6 +46,7 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet = | |||
46 | T![continue], | 46 | T![continue], |
47 | T![async], | 47 | T![async], |
48 | T![try], | 48 | T![try], |
49 | T![const], | ||
49 | T![loop], | 50 | T![loop], |
50 | T![for], | 51 | T![for], |
51 | LIFETIME_IDENT, | 52 | LIFETIME_IDENT, |
@@ -115,6 +116,14 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
115 | block_expr(p); | 116 | block_expr(p); |
116 | m.complete(p, EFFECT_EXPR) | 117 | m.complete(p, EFFECT_EXPR) |
117 | } | 118 | } |
119 | // test const_block | ||
120 | // fn f() { const { } } | ||
121 | T![const] if la == T!['{'] => { | ||
122 | let m = p.start(); | ||
123 | p.bump(T![const]); | ||
124 | block_expr(p); | ||
125 | m.complete(p, EFFECT_EXPR) | ||
126 | } | ||
118 | T!['{'] => { | 127 | T!['{'] => { |
119 | // test for_range_from | 128 | // test for_range_from |
120 | // fn foo() { | 129 | // fn foo() { |
@@ -156,11 +165,13 @@ fn tuple_expr(p: &mut Parser) -> CompletedMarker { | |||
156 | let mut saw_expr = false; | 165 | let mut saw_expr = false; |
157 | while !p.at(EOF) && !p.at(T![')']) { | 166 | while !p.at(EOF) && !p.at(T![')']) { |
158 | saw_expr = true; | 167 | saw_expr = true; |
159 | if !p.at_ts(EXPR_FIRST) { | 168 | |
160 | p.error("expected expression"); | 169 | // test tuple_attrs |
170 | // const A: (i64, i64) = (1, #[cfg(test)] 2); | ||
171 | if !expr_with_attrs(p) { | ||
161 | break; | 172 | break; |
162 | } | 173 | } |
163 | expr(p); | 174 | |
164 | if !p.at(T![')']) { | 175 | if !p.at(T![')']) { |
165 | saw_comma = true; | 176 | saw_comma = true; |
166 | p.expect(T![,]); | 177 | p.expect(T![,]); |
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs index 8999829b4..cf4168d32 100644 --- a/crates/parser/src/grammar/items.rs +++ b/crates/parser/src/grammar/items.rs | |||
@@ -96,7 +96,10 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { | |||
96 | let mut has_mods = false; | 96 | let mut has_mods = false; |
97 | 97 | ||
98 | // modifiers | 98 | // modifiers |
99 | has_mods |= p.eat(T![const]); | 99 | if p.at(T![const]) && p.nth(1) != T!['{'] { |
100 | p.eat(T![const]); | ||
101 | has_mods = true; | ||
102 | } | ||
100 | 103 | ||
101 | // test_err async_without_semicolon | 104 | // test_err async_without_semicolon |
102 | // fn foo() { let _ = async {} } | 105 | // fn foo() { let _ = async {} } |
@@ -167,7 +170,7 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { | |||
167 | m.complete(p, TRAIT); | 170 | m.complete(p, TRAIT); |
168 | } | 171 | } |
169 | 172 | ||
170 | T![const] => { | 173 | T![const] if p.nth(1) != T!['{'] => { |
171 | consts::konst(p, m); | 174 | consts::konst(p, m); |
172 | } | 175 | } |
173 | 176 | ||
@@ -386,10 +389,15 @@ fn macro_rules(p: &mut Parser, m: Marker) { | |||
386 | } | 389 | } |
387 | 390 | ||
388 | match p.current() { | 391 | match p.current() { |
389 | T!['{'] => { | 392 | // test macro_rules_non_brace |
393 | // macro_rules! m ( ($i:ident) => {} ); | ||
394 | // macro_rules! m [ ($i:ident) => {} ]; | ||
395 | T!['['] | T!['('] => { | ||
390 | token_tree(p); | 396 | token_tree(p); |
397 | p.expect(T![;]); | ||
391 | } | 398 | } |
392 | _ => p.error("expected `{`"), | 399 | T!['{'] => token_tree(p), |
400 | _ => p.error("expected `{`, `[`, `(`"), | ||
393 | } | 401 | } |
394 | m.complete(p, MACRO_RULES); | 402 | m.complete(p, MACRO_RULES); |
395 | } | 403 | } |
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs index 3ee4e4fca..2d006a1d5 100644 --- a/crates/parser/src/grammar/params.rs +++ b/crates/parser/src/grammar/params.rs | |||
@@ -47,20 +47,23 @@ fn list_(p: &mut Parser, flavor: Flavor) { | |||
47 | if let FnDef = flavor { | 47 | if let FnDef = flavor { |
48 | // test self_param_outer_attr | 48 | // test self_param_outer_attr |
49 | // fn f(#[must_use] self) {} | 49 | // fn f(#[must_use] self) {} |
50 | let m = p.start(); | ||
50 | attributes::outer_attrs(p); | 51 | attributes::outer_attrs(p); |
51 | opt_self_param(p); | 52 | opt_self_param(p, m); |
52 | } | 53 | } |
53 | 54 | ||
54 | while !p.at(EOF) && !p.at(ket) { | 55 | while !p.at(EOF) && !p.at(ket) { |
55 | // test param_outer_arg | 56 | // test param_outer_arg |
56 | // fn f(#[attr1] pat: Type) {} | 57 | // fn f(#[attr1] pat: Type) {} |
58 | let m = p.start(); | ||
57 | attributes::outer_attrs(p); | 59 | attributes::outer_attrs(p); |
58 | 60 | ||
59 | if !p.at_ts(PARAM_FIRST) { | 61 | if !p.at_ts(PARAM_FIRST) { |
60 | p.error("expected value parameter"); | 62 | p.error("expected value parameter"); |
63 | m.abandon(p); | ||
61 | break; | 64 | break; |
62 | } | 65 | } |
63 | let param = param(p, flavor); | 66 | let param = param(p, m, flavor); |
64 | if !p.at(ket) { | 67 | if !p.at(ket) { |
65 | p.expect(T![,]); | 68 | p.expect(T![,]); |
66 | } | 69 | } |
@@ -77,9 +80,8 @@ const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); | |||
77 | 80 | ||
78 | struct Variadic(bool); | 81 | struct Variadic(bool); |
79 | 82 | ||
80 | fn param(p: &mut Parser, flavor: Flavor) -> Variadic { | 83 | fn param(p: &mut Parser, m: Marker, flavor: Flavor) -> Variadic { |
81 | let mut res = Variadic(false); | 84 | let mut res = Variadic(false); |
82 | let m = p.start(); | ||
83 | match flavor { | 85 | match flavor { |
84 | // test param_list_vararg | 86 | // test param_list_vararg |
85 | // extern "C" { fn printf(format: *const i8, ...) -> i32; } | 87 | // extern "C" { fn printf(format: *const i8, ...) -> i32; } |
@@ -151,10 +153,8 @@ fn variadic_param(p: &mut Parser) -> bool { | |||
151 | // fn d(&'a mut self, x: i32) {} | 153 | // fn d(&'a mut self, x: i32) {} |
152 | // fn e(mut self) {} | 154 | // fn e(mut self) {} |
153 | // } | 155 | // } |
154 | fn opt_self_param(p: &mut Parser) { | 156 | fn opt_self_param(p: &mut Parser, m: Marker) { |
155 | let m; | ||
156 | if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { | 157 | if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { |
157 | m = p.start(); | ||
158 | p.eat(T![mut]); | 158 | p.eat(T![mut]); |
159 | p.eat(T![self]); | 159 | p.eat(T![self]); |
160 | // test arb_self_types | 160 | // test arb_self_types |
@@ -174,9 +174,8 @@ fn opt_self_param(p: &mut Parser) { | |||
174 | (T![&], T![mut], T![self], _) => 3, | 174 | (T![&], T![mut], T![self], _) => 3, |
175 | (T![&], LIFETIME_IDENT, T![self], _) => 3, | 175 | (T![&], LIFETIME_IDENT, T![self], _) => 3, |
176 | (T![&], LIFETIME_IDENT, T![mut], T![self]) => 4, | 176 | (T![&], LIFETIME_IDENT, T![mut], T![self]) => 4, |
177 | _ => return, | 177 | _ => return m.abandon(p), |
178 | }; | 178 | }; |
179 | m = p.start(); | ||
180 | p.bump_any(); | 179 | p.bump_any(); |
181 | if p.at(LIFETIME_IDENT) { | 180 | if p.at(LIFETIME_IDENT) { |
182 | lifetime(p); | 181 | lifetime(p); |
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index 7e7f73dee..b53d5749f 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs | |||
@@ -89,6 +89,7 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { | |||
89 | let m = match p.nth(0) { | 89 | let m = match p.nth(0) { |
90 | T![box] => box_pat(p), | 90 | T![box] => box_pat(p), |
91 | T![ref] | T![mut] => ident_pat(p, true), | 91 | T![ref] | T![mut] => ident_pat(p, true), |
92 | T![const] => const_block_pat(p), | ||
92 | IDENT => match p.nth(1) { | 93 | IDENT => match p.nth(1) { |
93 | // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro | 94 | // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro |
94 | // (T![x]). | 95 | // (T![x]). |
@@ -386,3 +387,16 @@ fn box_pat(p: &mut Parser) -> CompletedMarker { | |||
386 | pattern_single(p); | 387 | pattern_single(p); |
387 | m.complete(p, BOX_PAT) | 388 | m.complete(p, BOX_PAT) |
388 | } | 389 | } |
390 | |||
391 | // test const_block_pat | ||
392 | // fn main() { | ||
393 | // let const { 15 } = (); | ||
394 | // let const { foo(); bar() } = (); | ||
395 | // } | ||
396 | fn const_block_pat(p: &mut Parser) -> CompletedMarker { | ||
397 | assert!(p.at(T![const])); | ||
398 | let m = p.start(); | ||
399 | p.bump(T![const]); | ||
400 | expressions::block_expr(p); | ||
401 | m.complete(p, CONST_BLOCK_PAT) | ||
402 | } | ||
diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs index 9c3f7c28a..4aeccd193 100644 --- a/crates/parser/src/grammar/type_params.rs +++ b/crates/parser/src/grammar/type_params.rs | |||
@@ -113,7 +113,7 @@ fn type_bound(p: &mut Parser) -> bool { | |||
113 | p.eat(T![?]); | 113 | p.eat(T![?]); |
114 | match p.current() { | 114 | match p.current() { |
115 | LIFETIME_IDENT => lifetime(p), | 115 | LIFETIME_IDENT => lifetime(p), |
116 | T![for] => types::for_type(p), | 116 | T![for] => types::for_type(p, false), |
117 | _ if paths::is_use_path_start(p) => types::path_type_(p, false), | 117 | _ if paths::is_use_path_start(p) => types::path_type_(p, false), |
118 | _ => { | 118 | _ => { |
119 | m.abandon(p); | 119 | m.abandon(p); |
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs index 36a15eace..94cbf7d85 100644 --- a/crates/parser/src/grammar/types.rs +++ b/crates/parser/src/grammar/types.rs | |||
@@ -44,7 +44,7 @@ fn type_with_bounds_cond(p: &mut Parser, allow_bounds: bool) { | |||
44 | T![&] => ref_type(p), | 44 | T![&] => ref_type(p), |
45 | T![_] => infer_type(p), | 45 | T![_] => infer_type(p), |
46 | T![fn] | T![unsafe] | T![extern] => fn_ptr_type(p), | 46 | T![fn] | T![unsafe] | T![extern] => fn_ptr_type(p), |
47 | T![for] => for_type(p), | 47 | T![for] => for_type(p, allow_bounds), |
48 | T![impl] => impl_trait_type(p), | 48 | T![impl] => impl_trait_type(p), |
49 | T![dyn] => dyn_trait_type(p), | 49 | T![dyn] => dyn_trait_type(p), |
50 | // Some path types are not allowed to have bounds (no plus) | 50 | // Some path types are not allowed to have bounds (no plus) |
@@ -227,7 +227,7 @@ pub(super) fn for_binder(p: &mut Parser) { | |||
227 | // type A = for<'a> fn() -> (); | 227 | // type A = for<'a> fn() -> (); |
228 | // type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); | 228 | // type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); |
229 | // type Obj = for<'a> PartialEq<&'a i32>; | 229 | // type Obj = for<'a> PartialEq<&'a i32>; |
230 | pub(super) fn for_type(p: &mut Parser) { | 230 | pub(super) fn for_type(p: &mut Parser, allow_bounds: bool) { |
231 | assert!(p.at(T![for])); | 231 | assert!(p.at(T![for])); |
232 | let m = p.start(); | 232 | let m = p.start(); |
233 | for_binder(p); | 233 | for_binder(p); |
@@ -240,7 +240,13 @@ pub(super) fn for_type(p: &mut Parser) { | |||
240 | } | 240 | } |
241 | } | 241 | } |
242 | type_no_bounds(p); | 242 | type_no_bounds(p); |
243 | m.complete(p, FOR_TYPE); | 243 | let completed = m.complete(p, FOR_TYPE); |
244 | |||
245 | // test no_dyn_trait_leading_for | ||
246 | // type A = for<'a> Test<'a> + Send; | ||
247 | if allow_bounds { | ||
248 | opt_type_bounds_as_dyn_trait_type(p, completed); | ||
249 | } | ||
244 | } | 250 | } |
245 | 251 | ||
246 | // test impl_trait_type | 252 | // test impl_trait_type |
@@ -290,7 +296,7 @@ fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) { | |||
290 | let path = m.complete(p, kind); | 296 | let path = m.complete(p, kind); |
291 | 297 | ||
292 | if allow_bounds { | 298 | if allow_bounds { |
293 | opt_path_type_bounds_as_dyn_trait_type(p, path); | 299 | opt_type_bounds_as_dyn_trait_type(p, path); |
294 | } | 300 | } |
295 | } | 301 | } |
296 | 302 | ||
@@ -304,19 +310,23 @@ pub(super) fn path_type_(p: &mut Parser, allow_bounds: bool) { | |||
304 | // fn foo() -> Box<dyn T + 'f> {} | 310 | // fn foo() -> Box<dyn T + 'f> {} |
305 | let path = m.complete(p, PATH_TYPE); | 311 | let path = m.complete(p, PATH_TYPE); |
306 | if allow_bounds { | 312 | if allow_bounds { |
307 | opt_path_type_bounds_as_dyn_trait_type(p, path); | 313 | opt_type_bounds_as_dyn_trait_type(p, path); |
308 | } | 314 | } |
309 | } | 315 | } |
310 | 316 | ||
311 | /// This turns a parsed PATH_TYPE optionally into a DYN_TRAIT_TYPE | 317 | /// This turns a parsed PATH_TYPE or FOR_TYPE optionally into a DYN_TRAIT_TYPE |
312 | /// with a TYPE_BOUND_LIST | 318 | /// with a TYPE_BOUND_LIST |
313 | fn opt_path_type_bounds_as_dyn_trait_type(p: &mut Parser, path_type_marker: CompletedMarker) { | 319 | fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) { |
320 | assert!(matches!( | ||
321 | type_marker.kind(), | ||
322 | SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_CALL | ||
323 | )); | ||
314 | if !p.at(T![+]) { | 324 | if !p.at(T![+]) { |
315 | return; | 325 | return; |
316 | } | 326 | } |
317 | 327 | ||
318 | // First create a TYPE_BOUND from the completed PATH_TYPE | 328 | // First create a TYPE_BOUND from the completed PATH_TYPE |
319 | let m = path_type_marker.precede(p).complete(p, TYPE_BOUND); | 329 | let m = type_marker.precede(p).complete(p, TYPE_BOUND); |
320 | 330 | ||
321 | // Next setup a marker for the TYPE_BOUND_LIST | 331 | // Next setup a marker for the TYPE_BOUND_LIST |
322 | let m = m.precede(p); | 332 | let m = m.precede(p); |
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index ab8e4c70e..811e740f9 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs | |||
@@ -112,7 +112,7 @@ pub fn parse_fragment( | |||
112 | FragmentKind::Path => grammar::fragments::path, | 112 | FragmentKind::Path => grammar::fragments::path, |
113 | FragmentKind::Expr => grammar::fragments::expr, | 113 | FragmentKind::Expr => grammar::fragments::expr, |
114 | FragmentKind::Type => grammar::fragments::type_, | 114 | FragmentKind::Type => grammar::fragments::type_, |
115 | FragmentKind::Pattern => grammar::fragments::pattern, | 115 | FragmentKind::Pattern => grammar::fragments::pattern_single, |
116 | FragmentKind::Item => grammar::fragments::item, | 116 | FragmentKind::Item => grammar::fragments::item, |
117 | FragmentKind::Block => grammar::fragments::block_expr, | 117 | FragmentKind::Block => grammar::fragments::block_expr, |
118 | FragmentKind::Visibility => grammar::fragments::opt_visibility, | 118 | FragmentKind::Visibility => grammar::fragments::opt_visibility, |
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index 980aa5979..f69e71bdb 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs | |||
@@ -170,6 +170,7 @@ pub enum SyntaxKind { | |||
170 | RANGE_PAT, | 170 | RANGE_PAT, |
171 | LITERAL_PAT, | 171 | LITERAL_PAT, |
172 | MACRO_PAT, | 172 | MACRO_PAT, |
173 | CONST_BLOCK_PAT, | ||
173 | TUPLE_EXPR, | 174 | TUPLE_EXPR, |
174 | ARRAY_EXPR, | 175 | ARRAY_EXPR, |
175 | PAREN_EXPR, | 176 | PAREN_EXPR, |
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml index 1bfa6c3fc..df9a55c10 100644 --- a/crates/proc_macro_srv/Cargo.toml +++ b/crates/proc_macro_srv/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | object = { version = "0.23", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } | 13 | object = { version = "0.23", default-features = false, features = ["std", "read_core", "elf", "macho", "pe"] } |
14 | libloading = "0.6.0" | 14 | libloading = "0.6.0" |
15 | memmap = "0.7" | 15 | memmap = "0.7" |
16 | 16 | ||
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 0b4d3f4eb..53e70eaf7 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -28,6 +28,7 @@ oorandom = "11.1.2" | |||
28 | rustc-hash = "1.1.0" | 28 | rustc-hash = "1.1.0" |
29 | serde = { version = "1.0.106", features = ["derive"] } | 29 | serde = { version = "1.0.106", features = ["derive"] } |
30 | serde_json = { version = "1.0.48", features = ["preserve_order"] } | 30 | serde_json = { version = "1.0.48", features = ["preserve_order"] } |
31 | serde_path_to_error = "0.1" | ||
31 | threadpool = "1.7.1" | 32 | threadpool = "1.7.1" |
32 | rayon = "1.5" | 33 | rayon = "1.5" |
33 | mimalloc = { version = "0.1.19", default-features = false, optional = true } | 34 | mimalloc = { version = "0.1.19", default-features = false, optional = true } |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index de5eb93b5..80e46bf7f 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -5,12 +5,14 @@ use ide::CompletionResolveCapability; | |||
5 | use lsp_types::{ | 5 | use lsp_types::{ |
6 | CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, | 6 | CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, |
7 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, | 7 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, |
8 | DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability, | 8 | DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern, |
9 | ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions, | 9 | FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability, |
10 | HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions, | ||
10 | SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend, | 11 | SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend, |
11 | SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, | 12 | SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, |
12 | TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, | 13 | TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, |
13 | WorkDoneProgressOptions, | 14 | WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities, |
15 | WorkspaceServerCapabilities, | ||
14 | }; | 16 | }; |
15 | use rustc_hash::FxHashSet; | 17 | use rustc_hash::FxHashSet; |
16 | use serde_json::json; | 18 | use serde_json::json; |
@@ -68,7 +70,26 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
68 | document_link_provider: None, | 70 | document_link_provider: None, |
69 | color_provider: None, | 71 | color_provider: None, |
70 | execute_command_provider: None, | 72 | execute_command_provider: None, |
71 | workspace: None, | 73 | workspace: Some(WorkspaceServerCapabilities { |
74 | workspace_folders: None, | ||
75 | file_operations: Some(WorkspaceFileOperationsServerCapabilities { | ||
76 | did_create: None, | ||
77 | will_create: None, | ||
78 | did_rename: None, | ||
79 | will_rename: Some(FileOperationRegistrationOptions { | ||
80 | filters: vec![FileOperationFilter { | ||
81 | scheme: Some(String::from("file")), | ||
82 | pattern: FileOperationPattern { | ||
83 | glob: String::from("**/*.rs"), | ||
84 | matches: Some(FileOperationPatternKind::File), | ||
85 | options: None, | ||
86 | }, | ||
87 | }], | ||
88 | }), | ||
89 | did_delete: None, | ||
90 | will_delete: None, | ||
91 | }), | ||
92 | }), | ||
72 | call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), | 93 | call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), |
73 | semantic_tokens_provider: Some( | 94 | semantic_tokens_provider: Some( |
74 | SemanticTokensOptions { | 95 | SemanticTokensOptions { |
diff --git a/crates/rust-analyzer/src/cli/progress_report.rs b/crates/rust-analyzer/src/cli/progress_report.rs index bdbe565e6..5a2dc39d5 100644 --- a/crates/rust-analyzer/src/cli/progress_report.rs +++ b/crates/rust-analyzer/src/cli/progress_report.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! A simple progress bar | 1 | //! A simple progress bar |
2 | //! | 2 | //! |
3 | //! A single thread non-optimized progress bar | 3 | //! A single thread non-optimized progress bar |
4 | use std::io::Write; | 4 | use std::io::{self, Write}; |
5 | 5 | ||
6 | /// A Simple ASCII Progress Bar | 6 | /// A Simple ASCII Progress Bar |
7 | pub(crate) struct ProgressReport { | 7 | pub(crate) struct ProgressReport { |
@@ -97,8 +97,8 @@ impl ProgressReport { | |||
97 | } | 97 | } |
98 | } | 98 | } |
99 | 99 | ||
100 | let _ = std::io::stdout().write(output.as_bytes()); | 100 | let _ = io::stdout().write(output.as_bytes()); |
101 | let _ = std::io::stdout().flush(); | 101 | let _ = io::stdout().flush(); |
102 | self.text = text.to_string(); | 102 | self.text = text.to_string(); |
103 | } | 103 | } |
104 | 104 | ||
@@ -115,6 +115,8 @@ impl ProgressReport { | |||
115 | let spaces = " ".repeat(self.text.len()); | 115 | let spaces = " ".repeat(self.text.len()); |
116 | let backspaces = "\x08".repeat(self.text.len()); | 116 | let backspaces = "\x08".repeat(self.text.len()); |
117 | print!("{}{}{}", backspaces, spaces, backspaces); | 117 | print!("{}{}{}", backspaces, spaces, backspaces); |
118 | let _ = io::stdout().flush(); | ||
119 | |||
118 | self.text = String::new(); | 120 | self.text = String::new(); |
119 | } | 121 | } |
120 | } | 122 | } |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1f4b5c24c..1db5b4e7d 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -33,7 +33,7 @@ config_data! { | |||
33 | callInfo_full: bool = "true", | 33 | callInfo_full: bool = "true", |
34 | 34 | ||
35 | /// Automatically refresh project info via `cargo metadata` on | 35 | /// Automatically refresh project info via `cargo metadata` on |
36 | /// Cargo.toml changes. | 36 | /// `Cargo.toml` changes. |
37 | cargo_autoreload: bool = "true", | 37 | cargo_autoreload: bool = "true", |
38 | /// Activate all available features. | 38 | /// Activate all available features. |
39 | cargo_allFeatures: bool = "false", | 39 | cargo_allFeatures: bool = "false", |
@@ -52,7 +52,7 @@ config_data! { | |||
52 | /// Run specified `cargo check` command for diagnostics on save. | 52 | /// Run specified `cargo check` command for diagnostics on save. |
53 | checkOnSave_enable: bool = "true", | 53 | checkOnSave_enable: bool = "true", |
54 | /// Check with all features (will be passed as `--all-features`). | 54 | /// Check with all features (will be passed as `--all-features`). |
55 | /// Defaults to `rust-analyzer.cargo.allFeatures`. | 55 | /// Defaults to `#rust-analyzer.cargo.allFeatures#`. |
56 | checkOnSave_allFeatures: Option<bool> = "null", | 56 | checkOnSave_allFeatures: Option<bool> = "null", |
57 | /// Check all targets and tests (will be passed as `--all-targets`). | 57 | /// Check all targets and tests (will be passed as `--all-targets`). |
58 | checkOnSave_allTargets: bool = "true", | 58 | checkOnSave_allTargets: bool = "true", |
@@ -61,12 +61,12 @@ config_data! { | |||
61 | /// Do not activate the `default` feature. | 61 | /// Do not activate the `default` feature. |
62 | checkOnSave_noDefaultFeatures: Option<bool> = "null", | 62 | checkOnSave_noDefaultFeatures: Option<bool> = "null", |
63 | /// Check for a specific target. Defaults to | 63 | /// Check for a specific target. Defaults to |
64 | /// `rust-analyzer.cargo.target`. | 64 | /// `#rust-analyzer.cargo.target#`. |
65 | checkOnSave_target: Option<String> = "null", | 65 | checkOnSave_target: Option<String> = "null", |
66 | /// Extra arguments for `cargo check`. | 66 | /// Extra arguments for `cargo check`. |
67 | checkOnSave_extraArgs: Vec<String> = "[]", | 67 | checkOnSave_extraArgs: Vec<String> = "[]", |
68 | /// List of features to activate. Defaults to | 68 | /// List of features to activate. Defaults to |
69 | /// `rust-analyzer.cargo.features`. | 69 | /// `#rust-analyzer.cargo.features#`. |
70 | checkOnSave_features: Option<Vec<String>> = "null", | 70 | checkOnSave_features: Option<Vec<String>> = "null", |
71 | /// Advanced option, fully override the command rust-analyzer uses for | 71 | /// Advanced option, fully override the command rust-analyzer uses for |
72 | /// checking. The command should include `--message-format=json` or | 72 | /// checking. The command should include `--message-format=json` or |
@@ -80,7 +80,7 @@ config_data! { | |||
80 | /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. | 80 | /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. |
81 | completion_postfix_enable: bool = "true", | 81 | completion_postfix_enable: bool = "true", |
82 | /// Toggles the additional completions that automatically add imports when completed. | 82 | /// Toggles the additional completions that automatically add imports when completed. |
83 | /// Note that your client have to specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | 83 | /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. |
84 | completion_autoimport_enable: bool = "true", | 84 | completion_autoimport_enable: bool = "true", |
85 | 85 | ||
86 | /// Whether to show native rust-analyzer diagnostics. | 86 | /// Whether to show native rust-analyzer diagnostics. |
@@ -90,13 +90,13 @@ config_data! { | |||
90 | diagnostics_enableExperimental: bool = "true", | 90 | diagnostics_enableExperimental: bool = "true", |
91 | /// List of rust-analyzer diagnostics to disable. | 91 | /// List of rust-analyzer diagnostics to disable. |
92 | diagnostics_disabled: FxHashSet<String> = "[]", | 92 | diagnostics_disabled: FxHashSet<String> = "[]", |
93 | /// List of warnings that should be displayed with info severity.\nThe | 93 | /// List of warnings that should be displayed with info severity.\n\nThe |
94 | /// warnings will be indicated by a blue squiggly underline in code and | 94 | /// warnings will be indicated by a blue squiggly underline in code and |
95 | /// a blue icon in the problems panel. | 95 | /// a blue icon in the `Problems Panel`. |
96 | diagnostics_warningsAsHint: Vec<String> = "[]", | 96 | diagnostics_warningsAsHint: Vec<String> = "[]", |
97 | /// List of warnings that should be displayed with hint severity.\nThe | 97 | /// List of warnings that should be displayed with hint severity.\n\nThe |
98 | /// warnings will be indicated by faded text or three dots in code and | 98 | /// warnings will be indicated by faded text or three dots in code and |
99 | /// will not show up in the problems panel. | 99 | /// will not show up in the `Problems Panel`. |
100 | diagnostics_warningsAsInfo: Vec<String> = "[]", | 100 | diagnostics_warningsAsInfo: Vec<String> = "[]", |
101 | 101 | ||
102 | /// Controls file watching implementation. | 102 | /// Controls file watching implementation. |
@@ -121,7 +121,7 @@ config_data! { | |||
121 | 121 | ||
122 | /// Whether to show inlay type hints for method chains. | 122 | /// Whether to show inlay type hints for method chains. |
123 | inlayHints_chainingHints: bool = "true", | 123 | inlayHints_chainingHints: bool = "true", |
124 | /// Maximum length for inlay hints. | 124 | /// Maximum length for inlay hints. Default is unlimited. |
125 | inlayHints_maxLength: Option<usize> = "null", | 125 | inlayHints_maxLength: Option<usize> = "null", |
126 | /// Whether to show function parameter name inlay hints at the call | 126 | /// Whether to show function parameter name inlay hints at the call |
127 | /// site. | 127 | /// site. |
@@ -145,27 +145,27 @@ config_data! { | |||
145 | lens_methodReferences: bool = "false", | 145 | lens_methodReferences: bool = "false", |
146 | 146 | ||
147 | /// Disable project auto-discovery in favor of explicitly specified set | 147 | /// Disable project auto-discovery in favor of explicitly specified set |
148 | /// of projects. \nElements must be paths pointing to Cargo.toml, | 148 | /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, |
149 | /// rust-project.json, or JSON objects in rust-project.json format. | 149 | /// `rust-project.json`, or JSON objects in `rust-project.json` format. |
150 | linkedProjects: Vec<ManifestOrProjectJson> = "[]", | 150 | linkedProjects: Vec<ManifestOrProjectJson> = "[]", |
151 | /// Number of syntax trees rust-analyzer keeps in memory. | 151 | /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. |
152 | lruCapacity: Option<usize> = "null", | 152 | lruCapacity: Option<usize> = "null", |
153 | /// Whether to show `can't find Cargo.toml` error message. | 153 | /// Whether to show `can't find Cargo.toml` error message. |
154 | notifications_cargoTomlNotFound: bool = "true", | 154 | notifications_cargoTomlNotFound: bool = "true", |
155 | /// Enable Proc macro support, cargo.loadOutDirsFromCheck must be | 155 | /// Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be |
156 | /// enabled. | 156 | /// enabled. |
157 | procMacro_enable: bool = "false", | 157 | procMacro_enable: bool = "false", |
158 | 158 | ||
159 | /// Command to be executed instead of 'cargo' for runnables. | 159 | /// Command to be executed instead of 'cargo' for runnables. |
160 | runnables_overrideCargo: Option<String> = "null", | 160 | runnables_overrideCargo: Option<String> = "null", |
161 | /// Additional arguments to be passed to cargo for runnables such as | 161 | /// Additional arguments to be passed to cargo for runnables such as |
162 | /// tests or binaries.\nFor example, it may be '--release'. | 162 | /// tests or binaries.\nFor example, it may be `--release`. |
163 | runnables_cargoExtraArgs: Vec<String> = "[]", | 163 | runnables_cargoExtraArgs: Vec<String> = "[]", |
164 | 164 | ||
165 | /// Path to the rust compiler sources, for usage in rustc_private projects. | 165 | /// Path to the rust compiler sources, for usage in rustc_private projects. |
166 | rustcSource : Option<String> = "null", | 166 | rustcSource : Option<String> = "null", |
167 | 167 | ||
168 | /// Additional arguments to rustfmt. | 168 | /// Additional arguments to `rustfmt`. |
169 | rustfmt_extraArgs: Vec<String> = "[]", | 169 | rustfmt_extraArgs: Vec<String> = "[]", |
170 | /// Advanced option, fully override the command rust-analyzer uses for | 170 | /// Advanced option, fully override the command rust-analyzer uses for |
171 | /// formatting. | 171 | /// formatting. |
@@ -349,12 +349,12 @@ impl Config { | |||
349 | res | 349 | res |
350 | } | 350 | } |
351 | pub fn update(&mut self, json: serde_json::Value) { | 351 | pub fn update(&mut self, json: serde_json::Value) { |
352 | log::info!("Config::update({:#})", json); | 352 | log::info!("updating config from JSON: {:#}", json); |
353 | if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { | 353 | if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { |
354 | return; | 354 | return; |
355 | } | 355 | } |
356 | self.do_update(json); | 356 | self.do_update(json); |
357 | log::info!("Config::update() = {:#?}", self); | 357 | log::info!("updated config: {:#?}", self); |
358 | } | 358 | } |
359 | fn do_update(&mut self, json: serde_json::Value) { | 359 | fn do_update(&mut self, json: serde_json::Value) { |
360 | let data = ConfigData::from_json(json); | 360 | let data = ConfigData::from_json(json); |
@@ -758,7 +758,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json | |||
758 | ], | 758 | ], |
759 | "enumDescriptions": [ | 759 | "enumDescriptions": [ |
760 | "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.", | 760 | "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.", |
761 | "Prefix all import paths with `self` if they don't begin with `self`, `super`, `crate` or a crate name", | 761 | "Prefix all import paths with `self` if they don't begin with `self`, `super`, `crate` or a crate name.", |
762 | "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to." | 762 | "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to." |
763 | ], | 763 | ], |
764 | }, | 764 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index f16f97131..540759198 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -319,6 +319,10 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
319 | message: "original diagnostic".to_string(), | 319 | message: "original diagnostic".to_string(), |
320 | }; | 320 | }; |
321 | for info in &related_information { | 321 | for info in &related_information { |
322 | // Filter out empty/non-existent messages, as they greatly confuse VS Code. | ||
323 | if info.message.is_empty() { | ||
324 | continue; | ||
325 | } | ||
322 | diagnostics.push(MappedRustDiagnostic { | 326 | diagnostics.push(MappedRustDiagnostic { |
323 | url: info.location.uri.clone(), | 327 | url: info.location.uri.clone(), |
324 | fixes: fixes.clone(), // share fixes to make them easier to apply | 328 | fixes: fixes.clone(), // share fixes to make them easier to apply |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 66f8bee99..374fb5302 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -5,11 +5,13 @@ | |||
5 | use std::{ | 5 | use std::{ |
6 | io::Write as _, | 6 | io::Write as _, |
7 | process::{self, Stdio}, | 7 | process::{self, Stdio}, |
8 | sync::Arc, | ||
8 | }; | 9 | }; |
9 | 10 | ||
10 | use ide::{ | 11 | use ide::{ |
11 | CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, | 12 | AssistConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, |
12 | NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, SymbolKind, TextEdit, | 13 | HoverGotoTypeData, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, |
14 | SearchScope, SourceChange, SymbolKind, TextEdit, | ||
13 | }; | 15 | }; |
14 | use itertools::Itertools; | 16 | use itertools::Itertools; |
15 | use lsp_server::ErrorCode; | 17 | use lsp_server::ErrorCode; |
@@ -400,6 +402,45 @@ pub(crate) fn handle_workspace_symbol( | |||
400 | } | 402 | } |
401 | } | 403 | } |
402 | 404 | ||
405 | pub(crate) fn handle_will_rename_files( | ||
406 | snap: GlobalStateSnapshot, | ||
407 | params: lsp_types::RenameFilesParams, | ||
408 | ) -> Result<Option<lsp_types::WorkspaceEdit>> { | ||
409 | let _p = profile::span("handle_will_rename_files"); | ||
410 | |||
411 | let source_changes: Vec<SourceChange> = params | ||
412 | .files | ||
413 | .into_iter() | ||
414 | .filter_map(|file_rename| { | ||
415 | let from = Url::parse(&file_rename.old_uri).ok()?; | ||
416 | let to = Url::parse(&file_rename.new_uri).ok()?; | ||
417 | |||
418 | let from_path = from.to_file_path().ok()?; | ||
419 | let to_path = to.to_file_path().ok()?; | ||
420 | |||
421 | // Limit to single-level moves for now. | ||
422 | match (from_path.parent(), to_path.parent()) { | ||
423 | (Some(p1), Some(p2)) if p1 == p2 => { | ||
424 | let new_name = to_path.file_stem()?; | ||
425 | let new_name = new_name.to_str()?; | ||
426 | Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())) | ||
427 | } | ||
428 | _ => None, | ||
429 | } | ||
430 | }) | ||
431 | .filter_map(|(file_id, new_name)| { | ||
432 | snap.analysis.will_rename_file(file_id, &new_name).ok()? | ||
433 | }) | ||
434 | .collect(); | ||
435 | |||
436 | // Drop file system edits since we're just renaming things on the same level | ||
437 | let edits = source_changes.into_iter().map(|it| it.source_file_edits).flatten().collect(); | ||
438 | let source_change = SourceChange::from_edits(edits, Vec::new()); | ||
439 | |||
440 | let workspace_edit = to_proto::workspace_edit(&snap, source_change)?; | ||
441 | Ok(Some(workspace_edit)) | ||
442 | } | ||
443 | |||
403 | pub(crate) fn handle_goto_definition( | 444 | pub(crate) fn handle_goto_definition( |
404 | snap: GlobalStateSnapshot, | 445 | snap: GlobalStateSnapshot, |
405 | params: lsp_types::GotoDefinitionParams, | 446 | params: lsp_types::GotoDefinitionParams, |
@@ -865,58 +906,8 @@ pub(crate) fn handle_formatting( | |||
865 | } | 906 | } |
866 | } | 907 | } |
867 | 908 | ||
868 | fn handle_fixes( | ||
869 | snap: &GlobalStateSnapshot, | ||
870 | params: &lsp_types::CodeActionParams, | ||
871 | res: &mut Vec<lsp_ext::CodeAction>, | ||
872 | ) -> Result<()> { | ||
873 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | ||
874 | let line_index = snap.analysis.file_line_index(file_id)?; | ||
875 | let range = from_proto::text_range(&line_index, params.range); | ||
876 | |||
877 | match ¶ms.context.only { | ||
878 | Some(v) => { | ||
879 | if !v.iter().any(|it| { | ||
880 | it == &lsp_types::CodeActionKind::EMPTY | ||
881 | || it == &lsp_types::CodeActionKind::QUICKFIX | ||
882 | }) { | ||
883 | return Ok(()); | ||
884 | } | ||
885 | } | ||
886 | None => {} | ||
887 | }; | ||
888 | |||
889 | let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, file_id)?; | ||
890 | |||
891 | for fix in diagnostics | ||
892 | .into_iter() | ||
893 | .filter_map(|d| d.fix) | ||
894 | .filter(|fix| fix.fix_trigger_range.intersect(range).is_some()) | ||
895 | { | ||
896 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; | ||
897 | let action = lsp_ext::CodeAction { | ||
898 | title: fix.label.to_string(), | ||
899 | group: None, | ||
900 | kind: Some(CodeActionKind::QUICKFIX), | ||
901 | edit: Some(edit), | ||
902 | is_preferred: Some(false), | ||
903 | data: None, | ||
904 | }; | ||
905 | res.push(action); | ||
906 | } | ||
907 | |||
908 | for fix in snap.check_fixes.get(&file_id).into_iter().flatten() { | ||
909 | let fix_range = from_proto::text_range(&line_index, fix.range); | ||
910 | if fix_range.intersect(range).is_none() { | ||
911 | continue; | ||
912 | } | ||
913 | res.push(fix.action.clone()); | ||
914 | } | ||
915 | Ok(()) | ||
916 | } | ||
917 | |||
918 | pub(crate) fn handle_code_action( | 909 | pub(crate) fn handle_code_action( |
919 | mut snap: GlobalStateSnapshot, | 910 | snap: GlobalStateSnapshot, |
920 | params: lsp_types::CodeActionParams, | 911 | params: lsp_types::CodeActionParams, |
921 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { | 912 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { |
922 | let _p = profile::span("handle_code_action"); | 913 | let _p = profile::span("handle_code_action"); |
@@ -932,24 +923,35 @@ pub(crate) fn handle_code_action( | |||
932 | let range = from_proto::text_range(&line_index, params.range); | 923 | let range = from_proto::text_range(&line_index, params.range); |
933 | let frange = FileRange { file_id, range }; | 924 | let frange = FileRange { file_id, range }; |
934 | 925 | ||
935 | snap.config.assist.allowed = params | 926 | let assists_config = AssistConfig { |
936 | .clone() | 927 | allowed: params |
937 | .context | 928 | .clone() |
938 | .only | 929 | .context |
939 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | 930 | .only |
931 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()), | ||
932 | ..snap.config.assist | ||
933 | }; | ||
940 | 934 | ||
941 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | 935 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
942 | 936 | ||
943 | handle_fixes(&snap, ¶ms, &mut res)?; | 937 | let include_quick_fixes = match ¶ms.context.only { |
938 | Some(v) => v.iter().any(|it| { | ||
939 | it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX | ||
940 | }), | ||
941 | None => true, | ||
942 | }; | ||
943 | if include_quick_fixes { | ||
944 | add_quick_fixes(&snap, frange, &line_index, &mut res)?; | ||
945 | } | ||
944 | 946 | ||
945 | if snap.config.client_caps.code_action_resolve { | 947 | if snap.config.client_caps.code_action_resolve { |
946 | for (index, assist) in | 948 | for (index, assist) in |
947 | snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate() | 949 | snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() |
948 | { | 950 | { |
949 | res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); | 951 | res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); |
950 | } | 952 | } |
951 | } else { | 953 | } else { |
952 | for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() { | 954 | for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() { |
953 | res.push(to_proto::resolved_code_action(&snap, assist)?); | 955 | res.push(to_proto::resolved_code_action(&snap, assist)?); |
954 | } | 956 | } |
955 | } | 957 | } |
@@ -957,6 +959,40 @@ pub(crate) fn handle_code_action( | |||
957 | Ok(Some(res)) | 959 | Ok(Some(res)) |
958 | } | 960 | } |
959 | 961 | ||
962 | fn add_quick_fixes( | ||
963 | snap: &GlobalStateSnapshot, | ||
964 | frange: FileRange, | ||
965 | line_index: &Arc<LineIndex>, | ||
966 | acc: &mut Vec<lsp_ext::CodeAction>, | ||
967 | ) -> Result<()> { | ||
968 | let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, frange.file_id)?; | ||
969 | |||
970 | for fix in diagnostics | ||
971 | .into_iter() | ||
972 | .filter_map(|d| d.fix) | ||
973 | .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some()) | ||
974 | { | ||
975 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; | ||
976 | let action = lsp_ext::CodeAction { | ||
977 | title: fix.label.to_string(), | ||
978 | group: None, | ||
979 | kind: Some(CodeActionKind::QUICKFIX), | ||
980 | edit: Some(edit), | ||
981 | is_preferred: Some(false), | ||
982 | data: None, | ||
983 | }; | ||
984 | acc.push(action); | ||
985 | } | ||
986 | |||
987 | for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() { | ||
988 | let fix_range = from_proto::text_range(&line_index, fix.range); | ||
989 | if fix_range.intersect(frange.range).is_some() { | ||
990 | acc.push(fix.action.clone()); | ||
991 | } | ||
992 | } | ||
993 | Ok(()) | ||
994 | } | ||
995 | |||
960 | pub(crate) fn handle_code_action_resolve( | 996 | pub(crate) fn handle_code_action_resolve( |
961 | mut snap: GlobalStateSnapshot, | 997 | mut snap: GlobalStateSnapshot, |
962 | mut code_action: lsp_ext::CodeAction, | 998 | mut code_action: lsp_ext::CodeAction, |
@@ -978,11 +1014,11 @@ pub(crate) fn handle_code_action_resolve( | |||
978 | .only | 1014 | .only |
979 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | 1015 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); |
980 | 1016 | ||
981 | let assists = snap.analysis.resolved_assists(&snap.config.assist, frange)?; | 1017 | let assists = snap.analysis.assists(&snap.config.assist, true, frange)?; |
982 | let (id, index) = split_once(¶ms.id, ':').unwrap(); | 1018 | let (id, index) = split_once(¶ms.id, ':').unwrap(); |
983 | let index = index.parse::<usize>().unwrap(); | 1019 | let index = index.parse::<usize>().unwrap(); |
984 | let assist = &assists[index]; | 1020 | let assist = &assists[index]; |
985 | assert!(assist.assist.id.0 == id); | 1021 | assert!(assist.id.0 == id); |
986 | let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; | 1022 | let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; |
987 | code_action.edit = edit; | 1023 | code_action.edit = edit; |
988 | Ok(code_action) | 1024 | Ok(code_action) |
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 79fe30e53..d538ad69a 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -46,7 +46,7 @@ pub type Error = Box<dyn std::error::Error + Send + Sync>; | |||
46 | pub type Result<T, E = Error> = std::result::Result<T, E>; | 46 | pub type Result<T, E = Error> = std::result::Result<T, E>; |
47 | 47 | ||
48 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { | 48 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { |
49 | let res = T::deserialize(&json) | 49 | let res = serde_path_to_error::deserialize(&json) |
50 | .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?; | 50 | .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?; |
51 | Ok(res) | 51 | Ok(res) |
52 | } | 52 | } |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ec3d5e060..5d55dc96e 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -485,6 +485,7 @@ impl GlobalState { | |||
485 | .on::<lsp_types::request::SemanticTokensRangeRequest>( | 485 | .on::<lsp_types::request::SemanticTokensRangeRequest>( |
486 | handlers::handle_semantic_tokens_range, | 486 | handlers::handle_semantic_tokens_range, |
487 | ) | 487 | ) |
488 | .on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files) | ||
488 | .on::<lsp_ext::Ssr>(handlers::handle_ssr) | 489 | .on::<lsp_ext::Ssr>(handlers::handle_ssr) |
489 | .finish(); | 490 | .finish(); |
490 | Ok(()) | 491 | Ok(()) |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 1daad1c98..c2f6a655d 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -45,6 +45,7 @@ define_semantic_token_types![ | |||
45 | (FORMAT_SPECIFIER, "formatSpecifier"), | 45 | (FORMAT_SPECIFIER, "formatSpecifier"), |
46 | (GENERIC, "generic"), | 46 | (GENERIC, "generic"), |
47 | (LIFETIME, "lifetime"), | 47 | (LIFETIME, "lifetime"), |
48 | (LABEL, "label"), | ||
48 | (PUNCTUATION, "punctuation"), | 49 | (PUNCTUATION, "punctuation"), |
49 | (SELF_KEYWORD, "selfKeyword"), | 50 | (SELF_KEYWORD, "selfKeyword"), |
50 | (TYPE_ALIAS, "typeAlias"), | 51 | (TYPE_ALIAS, "typeAlias"), |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index e0561b5a7..1a38e79f0 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -8,8 +8,8 @@ use ide::{ | |||
8 | Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, | 8 | Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, |
9 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, | 9 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, |
10 | HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, | 10 | HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, |
11 | NavigationTarget, ReferenceAccess, ResolvedAssist, Runnable, Severity, SourceChange, | 11 | NavigationTarget, ReferenceAccess, Runnable, Severity, SourceChange, SourceFileEdit, |
12 | SourceFileEdit, SymbolKind, TextEdit, TextRange, TextSize, | 12 | SymbolKind, TextEdit, TextRange, TextSize, |
13 | }; | 13 | }; |
14 | use itertools::Itertools; | 14 | use itertools::Itertools; |
15 | 15 | ||
@@ -46,7 +46,8 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { | |||
46 | SymbolKind::Local | 46 | SymbolKind::Local |
47 | | SymbolKind::SelfParam | 47 | | SymbolKind::SelfParam |
48 | | SymbolKind::LifetimeParam | 48 | | SymbolKind::LifetimeParam |
49 | | SymbolKind::ValueParam => lsp_types::SymbolKind::Variable, | 49 | | SymbolKind::ValueParam |
50 | | SymbolKind::Label => lsp_types::SymbolKind::Variable, | ||
50 | SymbolKind::Union => lsp_types::SymbolKind::Struct, | 51 | SymbolKind::Union => lsp_types::SymbolKind::Struct, |
51 | } | 52 | } |
52 | } | 53 | } |
@@ -378,6 +379,7 @@ fn semantic_token_type_and_modifiers( | |||
378 | SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, | 379 | SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, |
379 | SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, | 380 | SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, |
380 | SymbolKind::LifetimeParam => semantic_tokens::LIFETIME, | 381 | SymbolKind::LifetimeParam => semantic_tokens::LIFETIME, |
382 | SymbolKind::Label => semantic_tokens::LABEL, | ||
381 | SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER, | 383 | SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER, |
382 | SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, | 384 | SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, |
383 | SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE, | 385 | SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE, |
@@ -634,30 +636,47 @@ pub(crate) fn snippet_text_document_edit( | |||
634 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) | 636 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) |
635 | } | 637 | } |
636 | 638 | ||
637 | pub(crate) fn resource_op( | 639 | pub(crate) fn snippet_text_document_ops( |
638 | snap: &GlobalStateSnapshot, | 640 | snap: &GlobalStateSnapshot, |
639 | file_system_edit: FileSystemEdit, | 641 | file_system_edit: FileSystemEdit, |
640 | ) -> lsp_types::ResourceOp { | 642 | ) -> Vec<lsp_ext::SnippetDocumentChangeOperation> { |
643 | let mut ops = Vec::new(); | ||
641 | match file_system_edit { | 644 | match file_system_edit { |
642 | FileSystemEdit::CreateFile { dst } => { | 645 | FileSystemEdit::CreateFile { dst, initial_contents } => { |
643 | let uri = snap.anchored_path(&dst); | 646 | let uri = snap.anchored_path(&dst); |
644 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { | 647 | let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile { |
645 | uri, | 648 | uri: uri.clone(), |
646 | options: None, | 649 | options: None, |
647 | annotation_id: None, | 650 | annotation_id: None, |
648 | }) | 651 | }); |
652 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(create_file)); | ||
653 | if !initial_contents.is_empty() { | ||
654 | let text_document = | ||
655 | lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None }; | ||
656 | let range = range(&LineIndex::new(""), TextRange::empty(TextSize::from(0))); | ||
657 | let text_edit = lsp_ext::SnippetTextEdit { | ||
658 | range, | ||
659 | new_text: initial_contents, | ||
660 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), | ||
661 | }; | ||
662 | let edit_file = | ||
663 | lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; | ||
664 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit_file)); | ||
665 | } | ||
649 | } | 666 | } |
650 | FileSystemEdit::MoveFile { src, dst } => { | 667 | FileSystemEdit::MoveFile { src, dst } => { |
651 | let old_uri = snap.file_id_to_url(src); | 668 | let old_uri = snap.file_id_to_url(src); |
652 | let new_uri = snap.anchored_path(&dst); | 669 | let new_uri = snap.anchored_path(&dst); |
653 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { | 670 | let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile { |
654 | old_uri, | 671 | old_uri, |
655 | new_uri, | 672 | new_uri, |
656 | options: None, | 673 | options: None, |
657 | annotation_id: None, | 674 | annotation_id: None, |
658 | }) | 675 | }); |
676 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file)) | ||
659 | } | 677 | } |
660 | } | 678 | } |
679 | ops | ||
661 | } | 680 | } |
662 | 681 | ||
663 | pub(crate) fn snippet_workspace_edit( | 682 | pub(crate) fn snippet_workspace_edit( |
@@ -666,8 +685,8 @@ pub(crate) fn snippet_workspace_edit( | |||
666 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { | 685 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { |
667 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); | 686 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); |
668 | for op in source_change.file_system_edits { | 687 | for op in source_change.file_system_edits { |
669 | let op = resource_op(&snap, op); | 688 | let ops = snippet_text_document_ops(snap, op); |
670 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); | 689 | document_changes.extend_from_slice(&ops); |
671 | } | 690 | } |
672 | for edit in source_change.source_file_edits { | 691 | for edit in source_change.source_file_edits { |
673 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?; | 692 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?; |
@@ -761,6 +780,7 @@ pub(crate) fn unresolved_code_action( | |||
761 | assist: Assist, | 780 | assist: Assist, |
762 | index: usize, | 781 | index: usize, |
763 | ) -> Result<lsp_ext::CodeAction> { | 782 | ) -> Result<lsp_ext::CodeAction> { |
783 | assert!(assist.source_change.is_none()); | ||
764 | let res = lsp_ext::CodeAction { | 784 | let res = lsp_ext::CodeAction { |
765 | title: assist.label.to_string(), | 785 | title: assist.label.to_string(), |
766 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), | 786 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), |
@@ -777,18 +797,14 @@ pub(crate) fn unresolved_code_action( | |||
777 | 797 | ||
778 | pub(crate) fn resolved_code_action( | 798 | pub(crate) fn resolved_code_action( |
779 | snap: &GlobalStateSnapshot, | 799 | snap: &GlobalStateSnapshot, |
780 | assist: ResolvedAssist, | 800 | assist: Assist, |
781 | ) -> Result<lsp_ext::CodeAction> { | 801 | ) -> Result<lsp_ext::CodeAction> { |
782 | let change = assist.source_change; | 802 | let change = assist.source_change.unwrap(); |
783 | let res = lsp_ext::CodeAction { | 803 | let res = lsp_ext::CodeAction { |
784 | edit: Some(snippet_workspace_edit(snap, change)?), | 804 | edit: Some(snippet_workspace_edit(snap, change)?), |
785 | title: assist.assist.label.to_string(), | 805 | title: assist.label.to_string(), |
786 | group: assist | 806 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), |
787 | .assist | 807 | kind: Some(code_action_kind(assist.id.1)), |
788 | .group | ||
789 | .filter(|_| snap.config.client_caps.code_action_group) | ||
790 | .map(|gr| gr.0), | ||
791 | kind: Some(code_action_kind(assist.assist.id.1)), | ||
792 | is_preferred: None, | 808 | is_preferred: None, |
793 | data: None, | 809 | data: None, |
794 | }; | 810 | }; |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index c6a6f11e1..21015591c 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "691.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "695.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index e4a9b945c..636ce166d 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs | |||
@@ -358,6 +358,7 @@ pub enum Effect { | |||
358 | Async(SyntaxToken), | 358 | Async(SyntaxToken), |
359 | Unsafe(SyntaxToken), | 359 | Unsafe(SyntaxToken), |
360 | Try(SyntaxToken), | 360 | Try(SyntaxToken), |
361 | Const(SyntaxToken), | ||
361 | // Very much not an effect, but we stuff it into this node anyway | 362 | // Very much not an effect, but we stuff it into this node anyway |
362 | Label(ast::Label), | 363 | Label(ast::Label), |
363 | } | 364 | } |
@@ -373,6 +374,9 @@ impl ast::EffectExpr { | |||
373 | if let Some(token) = self.try_token() { | 374 | if let Some(token) = self.try_token() { |
374 | return Effect::Try(token); | 375 | return Effect::Try(token); |
375 | } | 376 | } |
377 | if let Some(token) = self.const_token() { | ||
378 | return Effect::Const(token); | ||
379 | } | ||
376 | if let Some(label) = self.label() { | 380 | if let Some(label) = self.label() { |
377 | return Effect::Label(label); | 381 | return Effect::Label(label); |
378 | } | 382 | } |
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 1588ba93e..c5b80bffe 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs | |||
@@ -763,6 +763,7 @@ impl EffectExpr { | |||
763 | pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) } | 763 | pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) } |
764 | pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } | 764 | pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } |
765 | pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) } | 765 | pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) } |
766 | pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) } | ||
766 | pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) } | 767 | pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) } |
767 | } | 768 | } |
768 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 769 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
@@ -1251,6 +1252,14 @@ impl TupleStructPat { | |||
1251 | pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } | 1252 | pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } |
1252 | } | 1253 | } |
1253 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 1254 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
1255 | pub struct ConstBlockPat { | ||
1256 | pub(crate) syntax: SyntaxNode, | ||
1257 | } | ||
1258 | impl ConstBlockPat { | ||
1259 | pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) } | ||
1260 | pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) } | ||
1261 | } | ||
1262 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
1254 | pub struct RecordPatFieldList { | 1263 | pub struct RecordPatFieldList { |
1255 | pub(crate) syntax: SyntaxNode, | 1264 | pub(crate) syntax: SyntaxNode, |
1256 | } | 1265 | } |
@@ -1369,6 +1378,7 @@ pub enum Pat { | |||
1369 | SlicePat(SlicePat), | 1378 | SlicePat(SlicePat), |
1370 | TuplePat(TuplePat), | 1379 | TuplePat(TuplePat), |
1371 | TupleStructPat(TupleStructPat), | 1380 | TupleStructPat(TupleStructPat), |
1381 | ConstBlockPat(ConstBlockPat), | ||
1372 | } | 1382 | } |
1373 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 1383 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
1374 | pub enum FieldList { | 1384 | pub enum FieldList { |
@@ -2772,6 +2782,17 @@ impl AstNode for TupleStructPat { | |||
2772 | } | 2782 | } |
2773 | fn syntax(&self) -> &SyntaxNode { &self.syntax } | 2783 | fn syntax(&self) -> &SyntaxNode { &self.syntax } |
2774 | } | 2784 | } |
2785 | impl AstNode for ConstBlockPat { | ||
2786 | fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_BLOCK_PAT } | ||
2787 | fn cast(syntax: SyntaxNode) -> Option<Self> { | ||
2788 | if Self::can_cast(syntax.kind()) { | ||
2789 | Some(Self { syntax }) | ||
2790 | } else { | ||
2791 | None | ||
2792 | } | ||
2793 | } | ||
2794 | fn syntax(&self) -> &SyntaxNode { &self.syntax } | ||
2795 | } | ||
2775 | impl AstNode for RecordPatFieldList { | 2796 | impl AstNode for RecordPatFieldList { |
2776 | fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD_LIST } | 2797 | fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD_LIST } |
2777 | fn cast(syntax: SyntaxNode) -> Option<Self> { | 2798 | fn cast(syntax: SyntaxNode) -> Option<Self> { |
@@ -3242,12 +3263,15 @@ impl From<TuplePat> for Pat { | |||
3242 | impl From<TupleStructPat> for Pat { | 3263 | impl From<TupleStructPat> for Pat { |
3243 | fn from(node: TupleStructPat) -> Pat { Pat::TupleStructPat(node) } | 3264 | fn from(node: TupleStructPat) -> Pat { Pat::TupleStructPat(node) } |
3244 | } | 3265 | } |
3266 | impl From<ConstBlockPat> for Pat { | ||
3267 | fn from(node: ConstBlockPat) -> Pat { Pat::ConstBlockPat(node) } | ||
3268 | } | ||
3245 | impl AstNode for Pat { | 3269 | impl AstNode for Pat { |
3246 | fn can_cast(kind: SyntaxKind) -> bool { | 3270 | fn can_cast(kind: SyntaxKind) -> bool { |
3247 | match kind { | 3271 | match kind { |
3248 | IDENT_PAT | BOX_PAT | REST_PAT | LITERAL_PAT | MACRO_PAT | OR_PAT | PAREN_PAT | 3272 | IDENT_PAT | BOX_PAT | REST_PAT | LITERAL_PAT | MACRO_PAT | OR_PAT | PAREN_PAT |
3249 | | PATH_PAT | WILDCARD_PAT | RANGE_PAT | RECORD_PAT | REF_PAT | SLICE_PAT | 3273 | | PATH_PAT | WILDCARD_PAT | RANGE_PAT | RECORD_PAT | REF_PAT | SLICE_PAT |
3250 | | TUPLE_PAT | TUPLE_STRUCT_PAT => true, | 3274 | | TUPLE_PAT | TUPLE_STRUCT_PAT | CONST_BLOCK_PAT => true, |
3251 | _ => false, | 3275 | _ => false, |
3252 | } | 3276 | } |
3253 | } | 3277 | } |
@@ -3268,6 +3292,7 @@ impl AstNode for Pat { | |||
3268 | SLICE_PAT => Pat::SlicePat(SlicePat { syntax }), | 3292 | SLICE_PAT => Pat::SlicePat(SlicePat { syntax }), |
3269 | TUPLE_PAT => Pat::TuplePat(TuplePat { syntax }), | 3293 | TUPLE_PAT => Pat::TuplePat(TuplePat { syntax }), |
3270 | TUPLE_STRUCT_PAT => Pat::TupleStructPat(TupleStructPat { syntax }), | 3294 | TUPLE_STRUCT_PAT => Pat::TupleStructPat(TupleStructPat { syntax }), |
3295 | CONST_BLOCK_PAT => Pat::ConstBlockPat(ConstBlockPat { syntax }), | ||
3271 | _ => return None, | 3296 | _ => return None, |
3272 | }; | 3297 | }; |
3273 | Some(res) | 3298 | Some(res) |
@@ -3289,6 +3314,7 @@ impl AstNode for Pat { | |||
3289 | Pat::SlicePat(it) => &it.syntax, | 3314 | Pat::SlicePat(it) => &it.syntax, |
3290 | Pat::TuplePat(it) => &it.syntax, | 3315 | Pat::TuplePat(it) => &it.syntax, |
3291 | Pat::TupleStructPat(it) => &it.syntax, | 3316 | Pat::TupleStructPat(it) => &it.syntax, |
3317 | Pat::ConstBlockPat(it) => &it.syntax, | ||
3292 | } | 3318 | } |
3293 | } | 3319 | } |
3294 | } | 3320 | } |
@@ -4137,6 +4163,11 @@ impl std::fmt::Display for TupleStructPat { | |||
4137 | std::fmt::Display::fmt(self.syntax(), f) | 4163 | std::fmt::Display::fmt(self.syntax(), f) |
4138 | } | 4164 | } |
4139 | } | 4165 | } |
4166 | impl std::fmt::Display for ConstBlockPat { | ||
4167 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
4168 | std::fmt::Display::fmt(self.syntax(), f) | ||
4169 | } | ||
4170 | } | ||
4140 | impl std::fmt::Display for RecordPatFieldList { | 4171 | impl std::fmt::Display for RecordPatFieldList { |
4141 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 4172 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
4142 | std::fmt::Display::fmt(self.syntax(), f) | 4173 | std::fmt::Display::fmt(self.syntax(), f) |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index ba7e5d2fb..cafa4c198 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -4,6 +4,11 @@ | |||
4 | //! Note that all functions here intended to be stupid constructors, which just | 4 | //! Note that all functions here intended to be stupid constructors, which just |
5 | //! assemble a finish node from immediate children. If you want to do something | 5 | //! assemble a finish node from immediate children. If you want to do something |
6 | //! smarter than that, it probably doesn't belong in this module. | 6 | //! smarter than that, it probably doesn't belong in this module. |
7 | //! | ||
8 | //! Keep in mind that `from_text` functions should be kept private. The public | ||
9 | //! API should require to assemble every node piecewise. The trick of | ||
10 | //! `parse(format!())` we use internally is an implementation detail -- long | ||
11 | //! term, it will be replaced with direct tree manipulation. | ||
7 | use itertools::Itertools; | 12 | use itertools::Itertools; |
8 | use stdx::format_to; | 13 | use stdx::format_to; |
9 | 14 | ||
@@ -16,7 +21,8 @@ pub fn name(text: &str) -> ast::Name { | |||
16 | pub fn name_ref(text: &str) -> ast::NameRef { | 21 | pub fn name_ref(text: &str) -> ast::NameRef { |
17 | ast_from_text(&format!("fn f() {{ {}; }}", text)) | 22 | ast_from_text(&format!("fn f() {{ {}; }}", text)) |
18 | } | 23 | } |
19 | 24 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la | |
25 | // `expr_xxx`. | ||
20 | pub fn ty(text: &str) -> ast::Type { | 26 | pub fn ty(text: &str) -> ast::Type { |
21 | ast_from_text(&format!("impl {} for D {{}};", text)) | 27 | ast_from_text(&format!("impl {} for D {{}};", text)) |
22 | } | 28 | } |
diff --git a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast index 6403ff8d5..d3219f0b2 100644 --- a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast +++ b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast | |||
@@ -6,16 +6,16 @@ [email protected] | |||
6 | [email protected] "f" | 6 | [email protected] "f" |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] "(" | 8 | [email protected] "(" |
9 | ATT[email protected]6 | 9 | SELF_PARAM@5..21 |
10 | POUND@5..6 "#" | 10 | ATTR@5..16 |
11 | L_BRACK@6..7 "[" | 11 | POUND@5..6 "#" |
12 | PATH@7..15 | 12 | L_BRACK@6..7 "[" |
13 | PATH_SEGMENT@7..15 | 13 | [email protected] |
14 | NAME_REF@7..15 | 14 | PATH_SEGMENT@7..15 |
15 | IDENT@7..15 "must_use" | 15 | NAME_REF@7..15 |
16 | R_BRACK@15..16 "]" | 16 | IDENT@7..15 "must_use" |
17 | WHITESPACE@16..17 " " | 17 | R_BRACK@15..16 "]" |
18 | SELF_PARAM@17..21 | 18 | WHITESPACE@16..17 " " |
19 | [email protected] "self" | 19 | [email protected] "self" |
20 | [email protected] ")" | 20 | [email protected] ")" |
21 | [email protected] " " | 21 | [email protected] " " |
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast new file mode 100644 index 000000000..edfcb288c --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast | |||
@@ -0,0 +1,43 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "type" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "A" | ||
7 | [email protected] " " | ||
8 | [email protected] "=" | ||
9 | [email protected] " " | ||
10 | [email protected] | ||
11 | [email protected] | ||
12 | [email protected] | ||
13 | [email protected] | ||
14 | [email protected] "for" | ||
15 | [email protected] | ||
16 | [email protected] "<" | ||
17 | [email protected] | ||
18 | [email protected] | ||
19 | [email protected] "\'a" | ||
20 | [email protected] ">" | ||
21 | [email protected] " " | ||
22 | [email protected] | ||
23 | [email protected] | ||
24 | [email protected] | ||
25 | [email protected] | ||
26 | [email protected] "Test" | ||
27 | [email protected] | ||
28 | [email protected] "<" | ||
29 | [email protected] | ||
30 | [email protected] | ||
31 | [email protected] "\'a" | ||
32 | [email protected] ">" | ||
33 | [email protected] " " | ||
34 | [email protected] "+" | ||
35 | [email protected] " " | ||
36 | [email protected] | ||
37 | [email protected] | ||
38 | [email protected] | ||
39 | [email protected] | ||
40 | [email protected] | ||
41 | [email protected] "Send" | ||
42 | [email protected] ";" | ||
43 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs new file mode 100644 index 000000000..47a71fd19 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs | |||
@@ -0,0 +1 @@ | |||
type A = for<'a> Test<'a> + Send; | |||
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast new file mode 100644 index 000000000..d34b21abe --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast | |||
@@ -0,0 +1,50 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "const" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "A" | ||
7 | [email protected] ":" | ||
8 | [email protected] " " | ||
9 | [email protected] | ||
10 | [email protected] "(" | ||
11 | [email protected] | ||
12 | [email protected] | ||
13 | [email protected] | ||
14 | [email protected] | ||
15 | [email protected] "i64" | ||
16 | [email protected] "," | ||
17 | [email protected] " " | ||
18 | [email protected] | ||
19 | [email protected] | ||
20 | [email protected] | ||
21 | [email protected] | ||
22 | [email protected] "i64" | ||
23 | [email protected] ")" | ||
24 | [email protected] " " | ||
25 | [email protected] "=" | ||
26 | [email protected] " " | ||
27 | [email protected] | ||
28 | [email protected] "(" | ||
29 | [email protected] | ||
30 | [email protected] "1" | ||
31 | [email protected] "," | ||
32 | [email protected] " " | ||
33 | [email protected] | ||
34 | [email protected] | ||
35 | [email protected] "#" | ||
36 | [email protected] "[" | ||
37 | [email protected] | ||
38 | [email protected] | ||
39 | [email protected] | ||
40 | [email protected] "cfg" | ||
41 | [email protected] | ||
42 | [email protected] "(" | ||
43 | [email protected] "test" | ||
44 | [email protected] ")" | ||
45 | [email protected] "]" | ||
46 | [email protected] " " | ||
47 | [email protected] "2" | ||
48 | [email protected] ")" | ||
49 | [email protected] ";" | ||
50 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs new file mode 100644 index 000000000..f84b7ab31 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs | |||
@@ -0,0 +1 @@ | |||
const A: (i64, i64) = (1, #[cfg(test)] 2); | |||
diff --git a/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rast b/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rast new file mode 100644 index 000000000..8ff4822c4 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rast | |||
@@ -0,0 +1,76 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "fn" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "main" | ||
7 | [email protected] | ||
8 | [email protected] "(" | ||
9 | [email protected] ")" | ||
10 | [email protected] " " | ||
11 | [email protected] | ||
12 | [email protected] "{" | ||
13 | [email protected] "\n " | ||
14 | [email protected] | ||
15 | [email protected] "let" | ||
16 | [email protected] " " | ||
17 | [email protected] | ||
18 | [email protected] "const" | ||
19 | [email protected] " " | ||
20 | [email protected] | ||
21 | [email protected] "{" | ||
22 | [email protected] " " | ||
23 | [email protected] | ||
24 | [email protected] "15" | ||
25 | [email protected] " " | ||
26 | [email protected] "}" | ||
27 | [email protected] " " | ||
28 | [email protected] "=" | ||
29 | [email protected] " " | ||
30 | [email protected] | ||
31 | [email protected] "(" | ||
32 | [email protected] ")" | ||
33 | [email protected] ";" | ||
34 | [email protected] "\n " | ||
35 | [email protected] | ||
36 | [email protected] "let" | ||
37 | [email protected] " " | ||
38 | [email protected] | ||
39 | [email protected] "const" | ||
40 | [email protected] " " | ||
41 | [email protected] | ||
42 | [email protected] "{" | ||
43 | [email protected] " " | ||
44 | [email protected] | ||
45 | [email protected] | ||
46 | [email protected] | ||
47 | [email protected] | ||
48 | [email protected] | ||
49 | [email protected] | ||
50 | [email protected] "foo" | ||
51 | [email protected] | ||
52 | [email protected] "(" | ||
53 | [email protected] ")" | ||
54 | [email protected] ";" | ||
55 | [email protected] " " | ||
56 | [email protected] | ||
57 | [email protected] | ||
58 | [email protected] | ||
59 | [email protected] | ||
60 | [email protected] | ||
61 | [email protected] "bar" | ||
62 | [email protected] | ||
63 | [email protected] "(" | ||
64 | [email protected] ")" | ||
65 | [email protected] " " | ||
66 | [email protected] "}" | ||
67 | [email protected] " " | ||
68 | [email protected] "=" | ||
69 | [email protected] " " | ||
70 | [email protected] | ||
71 | [email protected] "(" | ||
72 | [email protected] ")" | ||
73 | [email protected] ";" | ||
74 | [email protected] "\n" | ||
75 | [email protected] "}" | ||
76 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rs b/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rs new file mode 100644 index 000000000..dce9defac --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | fn main() { | ||
2 | let const { 15 } = (); | ||
3 | let const { foo(); bar() } = (); | ||
4 | } | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0157_const_block.rast b/crates/syntax/test_data/parser/inline/ok/0157_const_block.rast new file mode 100644 index 000000000..d5d2c8fe3 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0157_const_block.rast | |||
@@ -0,0 +1,23 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "fn" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "f" | ||
7 | [email protected] | ||
8 | [email protected] "(" | ||
9 | [email protected] ")" | ||
10 | [email protected] " " | ||
11 | [email protected] | ||
12 | [email protected] "{" | ||
13 | [email protected] " " | ||
14 | [email protected] | ||
15 | [email protected] "const" | ||
16 | [email protected] " " | ||
17 | [email protected] | ||
18 | [email protected] "{" | ||
19 | [email protected] " " | ||
20 | [email protected] "}" | ||
21 | [email protected] " " | ||
22 | [email protected] "}" | ||
23 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0157_const_block.rs b/crates/syntax/test_data/parser/inline/ok/0157_const_block.rs new file mode 100644 index 000000000..a2e3565a3 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0157_const_block.rs | |||
@@ -0,0 +1 @@ | |||
fn f() { const { } } | |||
diff --git a/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast b/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast new file mode 100644 index 000000000..4a1f712aa --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast | |||
@@ -0,0 +1,57 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "macro_rules" | ||
4 | [email protected] "!" | ||
5 | [email protected] " " | ||
6 | [email protected] | ||
7 | [email protected] "m" | ||
8 | [email protected] " " | ||
9 | [email protected] | ||
10 | [email protected] "(" | ||
11 | [email protected] " " | ||
12 | [email protected] | ||
13 | [email protected] "(" | ||
14 | [email protected] "$" | ||
15 | [email protected] "i" | ||
16 | [email protected] ":" | ||
17 | [email protected] "ident" | ||
18 | [email protected] ")" | ||
19 | [email protected] " " | ||
20 | [email protected] "=" | ||
21 | [email protected] ">" | ||
22 | [email protected] " " | ||
23 | [email protected] | ||
24 | [email protected] "{" | ||
25 | [email protected] "}" | ||
26 | [email protected] " " | ||
27 | [email protected] ")" | ||
28 | [email protected] ";" | ||
29 | [email protected] "\n" | ||
30 | [email protected] | ||
31 | [email protected] "macro_rules" | ||
32 | [email protected] "!" | ||
33 | [email protected] " " | ||
34 | [email protected] | ||
35 | [email protected] "m" | ||
36 | [email protected] " " | ||
37 | [email protected] | ||
38 | [email protected] "[" | ||
39 | [email protected] " " | ||
40 | [email protected] | ||
41 | [email protected] "(" | ||
42 | [email protected] "$" | ||
43 | [email protected] "i" | ||
44 | [email protected] ":" | ||
45 | [email protected] "ident" | ||
46 | [email protected] ")" | ||
47 | [email protected] " " | ||
48 | [email protected] "=" | ||
49 | [email protected] ">" | ||
50 | [email protected] " " | ||
51 | [email protected] | ||
52 | [email protected] "{" | ||
53 | [email protected] "}" | ||
54 | [email protected] " " | ||
55 | [email protected] "]" | ||
56 | [email protected] ";" | ||
57 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs b/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs new file mode 100644 index 000000000..6033a28cd --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs | |||
@@ -0,0 +1,2 @@ | |||
1 | macro_rules! m ( ($i:ident) => {} ); | ||
2 | macro_rules! m [ ($i:ident) => {} ]; | ||
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast index 8974f9e40..3fed11838 100644 --- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast | |||
@@ -107,16 +107,16 @@ [email protected] | |||
107 | [email protected] "i8" | 107 | [email protected] "i8" |
108 | [email protected] "," | 108 | [email protected] "," |
109 | [email protected] " " | 109 | [email protected] " " |
110 | ATT[email protected]3 | 110 | PARAM@106..117 |
111 | POUND@106..107 "#" | 111 | ATTR@106..113 |
112 | L_BRACK@107..108 "[" | 112 | POUND@106..107 "#" |
113 | PATH@108..112 | 113 | L_BRACK@107..108 "[" |
114 | PATH_SEGMENT@108..112 | 114 | [email protected] |
115 | NAME_REF@108..112 | 115 | PATH_SEGMENT@108..112 |
116 | IDENT@108..112 "attr" | 116 | NAME_REF@108..112 |
117 | R_BRACK@112..113 "]" | 117 | IDENT@108..112 "attr" |
118 | WHITESPACE@113..114 " " | 118 | R_BRACK@112..113 "]" |
119 | PARAM@114..117 | 119 | WHITESPACE@113..114 " " |
120 | [email protected] "..." | 120 | [email protected] "..." |
121 | [email protected] ")" | 121 | [email protected] ")" |
122 | [email protected] " " | 122 | [email protected] " " |
@@ -153,16 +153,16 @@ [email protected] | |||
153 | [email protected] "FnMut" | 153 | [email protected] "FnMut" |
154 | [email protected] | 154 | [email protected] |
155 | [email protected] "(" | 155 | [email protected] "(" |
156 | ATT[email protected]53 | 156 | PARAM@146..166 |
157 | POUND@146..147 "#" | 157 | ATTR@146..153 |
158 | L_BRACK@147..148 "[" | 158 | POUND@146..147 "#" |
159 | PATH@148..152 | 159 | L_BRACK@147..148 "[" |
160 | PATH_SEGMENT@148..152 | 160 | [email protected] |
161 | NAME_REF@148..152 | 161 | PATH_SEGMENT@148..152 |
162 | IDENT@148..152 "attr" | 162 | NAME_REF@148..152 |
163 | R_BRACK@152..153 "]" | 163 | IDENT@148..152 "attr" |
164 | WHITESPACE@153..154 " " | 164 | R_BRACK@152..153 "]" |
165 | PARAM@154..166 | 165 | WHITESPACE@153..154 " " |
166 | [email protected] | 166 | [email protected] |
167 | [email protected] "&" | 167 | [email protected] "&" |
168 | [email protected] "mut" | 168 | [email protected] "mut" |
@@ -224,17 +224,17 @@ [email protected] | |||
224 | [email protected] "u64" | 224 | [email protected] "u64" |
225 | [email protected] "," | 225 | [email protected] "," |
226 | [email protected] " " | 226 | [email protected] " " |
227 | ATT[email protected]1 | 227 | PARAM@213..232 |
228 | POUND@213..214 "#" | 228 | ATTR@213..221 |
229 | WHITESPACE@214..215 " " | 229 | POUND@213..214 "#" |
230 | L_BRACK@215..216 "[" | 230 | WHITESPACE@214..215 " " |
231 | PATH@216..220 | 231 | L_BRACK@215..216 "[" |
232 | PATH_SEGMENT@216..220 | 232 | [email protected] |
233 | NAME_REF@216..220 | 233 | PATH_SEGMENT@216..220 |
234 | IDENT@216..220 "attr" | 234 | NAME_REF@216..220 |
235 | R_BRACK@220..221 "]" | 235 | IDENT@216..220 "attr" |
236 | WHITESPACE@221..222 " " | 236 | R_BRACK@220..221 "]" |
237 | PARAM@222..232 | 237 | WHITESPACE@221..222 " " |
238 | [email protected] | 238 | [email protected] |
239 | [email protected] "mut" | 239 | [email protected] "mut" |
240 | [email protected] " " | 240 | [email protected] " " |
@@ -271,16 +271,16 @@ [email protected] | |||
271 | [email protected] "f" | 271 | [email protected] "f" |
272 | [email protected] | 272 | [email protected] |
273 | [email protected] "(" | 273 | [email protected] "(" |
274 | ATT[email protected]68 | 274 | SELF_PARAM@257..273 |
275 | POUND@257..258 "#" | 275 | ATTR@257..268 |
276 | L_BRACK@258..259 "[" | 276 | POUND@257..258 "#" |
277 | PATH@259..267 | 277 | L_BRACK@258..259 "[" |
278 | PATH_SEGMENT@259..267 | 278 | [email protected] |
279 | NAME_REF@259..267 | 279 | PATH_SEGMENT@259..267 |
280 | IDENT@259..267 "must_use" | 280 | NAME_REF@259..267 |
281 | R_BRACK@267..268 "]" | 281 | IDENT@259..267 "must_use" |
282 | WHITESPACE@268..269 " " | 282 | R_BRACK@267..268 "]" |
283 | SELF_PARAM@269..273 | 283 | WHITESPACE@268..269 " " |
284 | [email protected] "self" | 284 | [email protected] "self" |
285 | [email protected] ")" | 285 | [email protected] ")" |
286 | [email protected] " " | 286 | [email protected] " " |
@@ -295,16 +295,16 @@ [email protected] | |||
295 | [email protected] "g1" | 295 | [email protected] "g1" |
296 | [email protected] | 296 | [email protected] |
297 | [email protected] "(" | 297 | [email protected] "(" |
298 | ATTR@289..296 | 298 | SELF_PARAM@289..301 |
299 | POUND@289..290 "#" | 299 | ATTR@289..296 |
300 | L_BRACK@290..291 "[" | 300 | POUND@289..290 "#" |
301 | PATH@291..295 | 301 | L_BRACK@290..291 "[" |
302 | PATH_SEGMENT@291..295 | 302 | [email protected] |
303 | NAME_REF@291..295 | 303 | PATH_SEGMENT@291..295 |
304 | IDENT@291..295 "attr" | 304 | NAME_REF@291..295 |
305 | R_BRACK@295..296 "]" | 305 | IDENT@291..295 "attr" |
306 | WHITESPACE@296..297 " " | 306 | R_BRACK@295..296 "]" |
307 | SELF_PARAM@297..301 | 307 | WHITESPACE@296..297 " " |
308 | [email protected] "self" | 308 | [email protected] "self" |
309 | [email protected] ")" | 309 | [email protected] ")" |
310 | [email protected] " " | 310 | [email protected] " " |
@@ -319,16 +319,16 @@ [email protected] | |||
319 | [email protected] "g2" | 319 | [email protected] "g2" |
320 | [email protected] | 320 | [email protected] |
321 | [email protected] "(" | 321 | [email protected] "(" |
322 | ATT[email protected]24 | 322 | SELF_PARAM@317..330 |
323 | POUND@317..318 "#" | 323 | ATTR@317..324 |
324 | L_BRACK@318..319 "[" | 324 | POUND@317..318 "#" |
325 | PATH@319..323 | 325 | L_BRACK@318..319 "[" |
326 | PATH_SEGMENT@319..323 | 326 | [email protected] |
327 | NAME_REF@319..323 | 327 | PATH_SEGMENT@319..323 |
328 | IDENT@319..323 "attr" | 328 | NAME_REF@319..323 |
329 | R_BRACK@323..324 "]" | 329 | IDENT@319..323 "attr" |
330 | WHITESPACE@324..325 " " | 330 | R_BRACK@323..324 "]" |
331 | SELF_PARAM@325..330 | 331 | WHITESPACE@324..325 " " |
332 | [email protected] "&" | 332 | [email protected] "&" |
333 | [email protected] "self" | 333 | [email protected] "self" |
334 | [email protected] ")" | 334 | [email protected] ")" |
@@ -350,16 +350,16 @@ [email protected] | |||
350 | [email protected] ">" | 350 | [email protected] ">" |
351 | [email protected] | 351 | [email protected] |
352 | [email protected] "(" | 352 | [email protected] "(" |
353 | ATT[email protected]57 | 353 | SELF_PARAM@350..367 |
354 | POUND@350..351 "#" | 354 | ATTR@350..357 |
355 | L_BRACK@351..352 "[" | 355 | POUND@350..351 "#" |
356 | PATH@352..356 | 356 | L_BRACK@351..352 "[" |
357 | PATH_SEGMENT@352..356 | 357 | [email protected] |
358 | NAME_REF@352..356 | 358 | PATH_SEGMENT@352..356 |
359 | IDENT@352..356 "attr" | 359 | NAME_REF@352..356 |
360 | R_BRACK@356..357 "]" | 360 | IDENT@352..356 "attr" |
361 | WHITESPACE@357..358 " " | 361 | R_BRACK@356..357 "]" |
362 | SELF_PARAM@358..367 | 362 | WHITESPACE@357..358 " " |
363 | [email protected] "&" | 363 | [email protected] "&" |
364 | [email protected] "mut" | 364 | [email protected] "mut" |
365 | [email protected] " " | 365 | [email protected] " " |
@@ -383,16 +383,16 @@ [email protected] | |||
383 | [email protected] ">" | 383 | [email protected] ">" |
384 | [email protected] | 384 | [email protected] |
385 | [email protected] "(" | 385 | [email protected] "(" |
386 | ATTR@387..394 | 386 | SELF_PARAM@387..403 |
387 | POUND@387..388 "#" | 387 | ATTR@387..394 |
388 | L_BRACK@388..389 "[" | 388 | POUND@387..388 "#" |
389 | PATH@389..393 | 389 | L_BRACK@388..389 "[" |
390 | PATH_SEGMENT@389..393 | 390 | [email protected] |
391 | NAME_REF@389..393 | 391 | PATH_SEGMENT@389..393 |
392 | IDENT@389..393 "attr" | 392 | NAME_REF@389..393 |
393 | R_BRACK@393..394 "]" | 393 | IDENT@389..393 "attr" |
394 | WHITESPACE@394..395 " " | 394 | R_BRACK@393..394 "]" |
395 | SELF_PARAM@395..403 | 395 | WHITESPACE@394..395 " " |
396 | [email protected] "&" | 396 | [email protected] "&" |
397 | [email protected] | 397 | [email protected] |
398 | [email protected] "\'a" | 398 | [email protected] "\'a" |
@@ -417,16 +417,16 @@ [email protected] | |||
417 | [email protected] ">" | 417 | [email protected] ">" |
418 | [email protected] | 418 | [email protected] |
419 | [email protected] "(" | 419 | [email protected] "(" |
420 | ATT[email protected]0 | 420 | SELF_PARAM@423..443 |
421 | POUND@423..424 "#" | 421 | ATTR@423..430 |
422 | L_BRACK@424..425 "[" | 422 | POUND@423..424 "#" |
423 | PATH@425..429 | 423 | L_BRACK@424..425 "[" |
424 | PATH_SEGMENT@425..429 | 424 | [email protected] |
425 | NAME_REF@425..429 | 425 | PATH_SEGMENT@425..429 |
426 | IDENT@425..429 "attr" | 426 | NAME_REF@425..429 |
427 | R_BRACK@429..430 "]" | 427 | IDENT@425..429 "attr" |
428 | WHITESPACE@430..431 " " | 428 | R_BRACK@429..430 "]" |
429 | SELF_PARAM@431..443 | 429 | WHITESPACE@430..431 " " |
430 | [email protected] "&" | 430 | [email protected] "&" |
431 | [email protected] | 431 | [email protected] |
432 | [email protected] "\'a" | 432 | [email protected] "\'a" |
@@ -447,16 +447,16 @@ [email protected] | |||
447 | [email protected] "c" | 447 | [email protected] "c" |
448 | [email protected] | 448 | [email protected] |
449 | [email protected] "(" | 449 | [email protected] "(" |
450 | ATT[email protected]5 | 450 | SELF_PARAM@458..476 |
451 | POUND@458..459 "#" | 451 | ATTR@458..465 |
452 | L_BRACK@459..460 "[" | 452 | POUND@458..459 "#" |
453 | PATH@460..464 | 453 | L_BRACK@459..460 "[" |
454 | PATH_SEGMENT@460..464 | 454 | [email protected] |
455 | NAME_REF@460..464 | 455 | PATH_SEGMENT@460..464 |
456 | IDENT@460..464 "attr" | 456 | NAME_REF@460..464 |
457 | R_BRACK@464..465 "]" | 457 | IDENT@460..464 "attr" |
458 | WHITESPACE@465..466 " " | 458 | R_BRACK@464..465 "]" |
459 | SELF_PARAM@466..476 | 459 | WHITESPACE@465..466 " " |
460 | [email protected] "self" | 460 | [email protected] "self" |
461 | [email protected] ":" | 461 | [email protected] ":" |
462 | [email protected] " " | 462 | [email protected] " " |
@@ -478,16 +478,16 @@ [email protected] | |||
478 | [email protected] "d" | 478 | [email protected] "d" |
479 | [email protected] | 479 | [email protected] |
480 | [email protected] "(" | 480 | [email protected] "(" |
481 | ATTR@491..498 | 481 | SELF_PARAM@491..513 |
482 | POUND@491..492 "#" | 482 | ATTR@491..498 |
483 | L_BRACK@492..493 "[" | 483 | POUND@491..492 "#" |
484 | PATH@493..497 | 484 | L_BRACK@492..493 "[" |
485 | PATH_SEGMENT@493..497 | 485 | [email protected] |
486 | NAME_REF@493..497 | 486 | PATH_SEGMENT@493..497 |
487 | IDENT@493..497 "attr" | 487 | NAME_REF@493..497 |
488 | R_BRACK@497..498 "]" | 488 | IDENT@493..497 "attr" |
489 | WHITESPACE@498..499 " " | 489 | R_BRACK@497..498 "]" |
490 | SELF_PARAM@499..513 | 490 | WHITESPACE@498..499 " " |
491 | [email protected] "self" | 491 | [email protected] "self" |
492 | [email protected] ":" | 492 | [email protected] ":" |
493 | [email protected] " " | 493 | [email protected] " " |
diff --git a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast index 4009b3ff8..f7c094898 100644 --- a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast +++ b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast | |||
@@ -92,20 +92,20 @@ [email protected] | |||
92 | [email protected] "u8" | 92 | [email protected] "u8" |
93 | [email protected] "," | 93 | [email protected] "," |
94 | [email protected] " " | 94 | [email protected] " " |
95 | ATT[email protected]5 | 95 | PARAM@92..120 |
96 | POUND@92..93 "#" | 96 | ATTR@92..105 |
97 | L_BRACK@93..94 "[" | 97 | POUND@92..93 "#" |
98 | PATH@94..97 | 98 | L_BRACK@93..94 "[" |
99 | PATH_SEGMENT@94..97 | 99 | [email protected] |
100 | NAME_REF@94..97 | 100 | PATH_SEGMENT@94..97 |
101 | IDENT@94..97 "cfg" | 101 | NAME_REF@94..97 |
102 | TOKEN_TREE@97..104 | 102 | IDENT@94..97 "cfg" |
103 | L_PAREN@97..98 "(" | 103 | TOKEN_TREE@97..104 |
104 | IDENT@98..103 "never" | 104 | L_PAREN@97..98 "(" |
105 | R_PAREN@103..104 ")" | 105 | IDENT@98..103 "never" |
106 | R_BRACK@104..105 "]" | 106 | R_PAREN@103..104 ")" |
107 | WHITESPACE@105..106 " " | 107 | R_BRACK@104..105 "]" |
108 | PARAM@106..120 | 108 | WHITESPACE@105..106 " " |
109 | [email protected] | 109 | [email protected] |
110 | [email protected] "[" | 110 | [email protected] "[" |
111 | [email protected] | 111 | [email protected] |