diff options
Diffstat (limited to 'crates')
127 files changed, 2456 insertions, 805 deletions
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml index 3fd8327d6..ed8ad666f 100644 --- a/crates/assists/Cargo.toml +++ b/crates/assists/Cargo.toml | |||
@@ -11,7 +11,7 @@ doctest = false | |||
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
14 | itertools = "0.9.0" | 14 | itertools = "0.10.0" |
15 | either = "1.6.1" | 15 | either = "1.6.1" |
16 | 16 | ||
17 | stdx = { path = "../stdx", version = "0.0.0" } | 17 | stdx = { path = "../stdx", version = "0.0.0" } |
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/extract_variable.rs b/crates/assists/src/handlers/extract_variable.rs index d2ae137cd..9957012fe 100644 --- a/crates/assists/src/handlers/extract_variable.rs +++ b/crates/assists/src/handlers/extract_variable.rs | |||
@@ -91,7 +91,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
91 | // extra newlines in the indent block | 91 | // extra newlines in the indent block |
92 | let text = indent.text(); | 92 | let text = indent.text(); |
93 | if text.starts_with('\n') { | 93 | if text.starts_with('\n') { |
94 | buf.push_str("\n"); | 94 | buf.push('\n'); |
95 | buf.push_str(text.trim_start_matches('\n')); | 95 | buf.push_str(text.trim_start_matches('\n')); |
96 | } else { | 96 | } else { |
97 | buf.push_str(text); | 97 | buf.push_str(text); |
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/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs index 4d6a1956b..cb7a5c104 100644 --- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -62,21 +62,22 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
62 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; | 62 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; |
63 | let current_crate = current_module.krate(); | 63 | let current_crate = current_module.krate(); |
64 | 64 | ||
65 | let found_traits = | 65 | let found_traits = imports_locator::find_exact_imports( |
66 | imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text()) | 66 | &ctx.sema, |
67 | .filter_map( | 67 | current_crate, |
68 | |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | 68 | trait_token.text().to_string(), |
69 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | 69 | ) |
70 | _ => None, | 70 | .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { |
71 | }, | 71 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), |
72 | ) | 72 | _ => None, |
73 | .flat_map(|trait_| { | 73 | }) |
74 | current_module | 74 | .flat_map(|trait_| { |
75 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | 75 | current_module |
76 | .as_ref() | 76 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) |
77 | .map(mod_path_to_ast) | 77 | .as_ref() |
78 | .zip(Some(trait_)) | 78 | .map(mod_path_to_ast) |
79 | }); | 79 | .zip(Some(trait_)) |
80 | }); | ||
80 | 81 | ||
81 | let mut no_traits_found = true; | 82 | let mut no_traits_found = true; |
82 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { | 83 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
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/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs index ff5c0e78e..4ce82c1ba 100644 --- a/crates/assists/src/utils/import_assets.rs +++ b/crates/assists/src/utils/import_assets.rs | |||
@@ -179,25 +179,24 @@ impl ImportAssets { | |||
179 | } | 179 | } |
180 | }; | 180 | }; |
181 | 181 | ||
182 | let mut res = | 182 | let mut res = imports_locator::find_exact_imports( |
183 | imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query()) | 183 | sema, |
184 | .filter_map(filter) | 184 | current_crate, |
185 | .filter_map(|candidate| { | 185 | self.get_search_query().to_string(), |
186 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); | 186 | ) |
187 | if let Some(prefix_kind) = prefixed { | 187 | .filter_map(filter) |
188 | self.module_with_name_to_import.find_use_path_prefixed( | 188 | .filter_map(|candidate| { |
189 | db, | 189 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); |
190 | item, | 190 | if let Some(prefix_kind) = prefixed { |
191 | prefix_kind, | 191 | self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) |
192 | ) | 192 | } else { |
193 | } else { | 193 | self.module_with_name_to_import.find_use_path(db, item) |
194 | self.module_with_name_to_import.find_use_path(db, item) | 194 | } |
195 | } | 195 | .map(|path| (path, item)) |
196 | .map(|path| (path, item)) | 196 | }) |
197 | }) | 197 | .filter(|(use_path, _)| use_path.len() > 1) |
198 | .filter(|(use_path, _)| use_path.len() > 1) | 198 | .take(20) |
199 | .take(20) | 199 | .collect::<Vec<_>>(); |
200 | .collect::<Vec<_>>(); | ||
201 | res.sort_by_key(|(path, _)| path.clone()); | 200 | res.sort_by_key(|(path, _)| path.clone()); |
202 | res | 201 | res |
203 | } | 202 | } |
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index cda5e57dc..9567bcc42 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs | |||
@@ -6,12 +6,12 @@ | |||
6 | //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how | 6 | //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how |
7 | //! actual IO is done and lowered to input. | 7 | //! actual IO is done and lowered to input. |
8 | 8 | ||
9 | use std::{fmt, iter::FromIterator, ops, str::FromStr, sync::Arc}; | 9 | use std::{fmt, iter::FromIterator, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}; |
10 | 10 | ||
11 | use cfg::CfgOptions; | 11 | use cfg::CfgOptions; |
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 12 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | use syntax::SmolStr; | 13 | use syntax::SmolStr; |
14 | use tt::TokenExpander; | 14 | use tt::{ExpansionError, Subtree}; |
15 | use vfs::{file_set::FileSet, FileId, VfsPath}; | 15 | use vfs::{file_set::FileSet, FileId, VfsPath}; |
16 | 16 | ||
17 | /// Files are grouped into source roots. A source root is a directory on the | 17 | /// Files are grouped into source roots. A source root is a directory on the |
@@ -150,11 +150,20 @@ pub enum ProcMacroKind { | |||
150 | Attr, | 150 | Attr, |
151 | } | 151 | } |
152 | 152 | ||
153 | pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { | ||
154 | fn expand( | ||
155 | &self, | ||
156 | subtree: &Subtree, | ||
157 | attrs: Option<&Subtree>, | ||
158 | env: &Env, | ||
159 | ) -> Result<Subtree, ExpansionError>; | ||
160 | } | ||
161 | |||
153 | #[derive(Debug, Clone)] | 162 | #[derive(Debug, Clone)] |
154 | pub struct ProcMacro { | 163 | pub struct ProcMacro { |
155 | pub name: SmolStr, | 164 | pub name: SmolStr, |
156 | pub kind: ProcMacroKind, | 165 | pub kind: ProcMacroKind, |
157 | pub expander: Arc<dyn TokenExpander>, | 166 | pub expander: Arc<dyn ProcMacroExpander>, |
158 | } | 167 | } |
159 | 168 | ||
160 | impl Eq for ProcMacro {} | 169 | impl Eq for ProcMacro {} |
@@ -413,6 +422,10 @@ impl Env { | |||
413 | pub fn get(&self, env: &str) -> Option<String> { | 422 | pub fn get(&self, env: &str) -> Option<String> { |
414 | self.entries.get(env).cloned() | 423 | self.entries.get(env).cloned() |
415 | } | 424 | } |
425 | |||
426 | pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> { | ||
427 | self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str())) | ||
428 | } | ||
416 | } | 429 | } |
417 | 430 | ||
418 | #[derive(Debug)] | 431 | #[derive(Debug)] |
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 595f28ada..5f77a0b1f 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs | |||
@@ -14,7 +14,7 @@ pub use crate::{ | |||
14 | change::Change, | 14 | change::Change, |
15 | input::{ | 15 | input::{ |
16 | CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, | 16 | CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, |
17 | ProcMacro, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId, | 17 | ProcMacro, ProcMacroExpander, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId, |
18 | }, | 18 | }, |
19 | }; | 19 | }; |
20 | pub use salsa; | 20 | pub use salsa; |
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index 35e169a28..78e93e78e 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | itertools = "0.9.0" | 13 | itertools = "0.10.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | either = "1.6.1" | 16 | either = "1.6.1" |
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/attribute.rs b/crates/completion/src/completions/attribute.rs index 19ce2482f..8695eed39 100644 --- a/crates/completion/src/completions/attribute.rs +++ b/crates/completion/src/completions/attribute.rs | |||
@@ -234,7 +234,7 @@ fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<Strin | |||
234 | current_derive = String::new(); | 234 | current_derive = String::new(); |
235 | } | 235 | } |
236 | } else { | 236 | } else { |
237 | current_derive.push_str(token.to_string().trim()); | 237 | current_derive.push_str(token.text().trim()); |
238 | } | 238 | } |
239 | } | 239 | } |
240 | 240 | ||
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/postfix.rs b/crates/completion/src/completions/postfix.rs index d6db82a93..3883d6d21 100644 --- a/crates/completion/src/completions/postfix.rs +++ b/crates/completion/src/completions/postfix.rs | |||
@@ -502,7 +502,7 @@ fn main() { | |||
502 | #[test] | 502 | #[test] |
503 | fn postfix_completion_for_format_like_strings() { | 503 | fn postfix_completion_for_format_like_strings() { |
504 | check_edit( | 504 | check_edit( |
505 | "fmt", | 505 | "format", |
506 | r#"fn main() { "{some_var:?}".<|> }"#, | 506 | r#"fn main() { "{some_var:?}".<|> }"#, |
507 | r#"fn main() { format!("{:?}", some_var) }"#, | 507 | r#"fn main() { format!("{:?}", some_var) }"#, |
508 | ); | 508 | ); |
diff --git a/crates/completion/src/completions/postfix/format_like.rs b/crates/completion/src/completions/postfix/format_like.rs index 88ba86acb..def4b13fb 100644 --- a/crates/completion/src/completions/postfix/format_like.rs +++ b/crates/completion/src/completions/postfix/format_like.rs | |||
@@ -22,7 +22,7 @@ use syntax::ast::{self, AstToken}; | |||
22 | 22 | ||
23 | /// Mapping ("postfix completion item" => "macro to use") | 23 | /// Mapping ("postfix completion item" => "macro to use") |
24 | static KINDS: &[(&str, &str)] = &[ | 24 | static KINDS: &[(&str, &str)] = &[ |
25 | ("fmt", "format!"), | 25 | ("format", "format!"), |
26 | ("panic", "panic!"), | 26 | ("panic", "panic!"), |
27 | ("println", "println!"), | 27 | ("println", "println!"), |
28 | ("eprintln", "eprintln!"), | 28 | ("eprintln", "eprintln!"), |
@@ -108,7 +108,8 @@ impl FormatStrParser { | |||
108 | // "{MyStruct { val_a: 0, val_b: 1 }}". | 108 | // "{MyStruct { val_a: 0, val_b: 1 }}". |
109 | let mut inexpr_open_count = 0; | 109 | let mut inexpr_open_count = 0; |
110 | 110 | ||
111 | for chr in self.input.chars() { | 111 | let mut chars = self.input.chars().peekable(); |
112 | while let Some(chr) = chars.next() { | ||
112 | match (self.state, chr) { | 113 | match (self.state, chr) { |
113 | (State::NotExpr, '{') => { | 114 | (State::NotExpr, '{') => { |
114 | self.output.push(chr); | 115 | self.output.push(chr); |
@@ -157,6 +158,11 @@ impl FormatStrParser { | |||
157 | inexpr_open_count -= 1; | 158 | inexpr_open_count -= 1; |
158 | } | 159 | } |
159 | } | 160 | } |
161 | (State::Expr, ':') if chars.peek().copied() == Some(':') => { | ||
162 | // path seperator | ||
163 | current_expr.push_str("::"); | ||
164 | chars.next(); | ||
165 | } | ||
160 | (State::Expr, ':') => { | 166 | (State::Expr, ':') => { |
161 | if inexpr_open_count == 0 { | 167 | if inexpr_open_count == 0 { |
162 | // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" | 168 | // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" |
@@ -249,6 +255,9 @@ mod tests { | |||
249 | expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]], | 255 | expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]], |
250 | ), | 256 | ), |
251 | ("{ 2 + 2 }", expect![["{}; 2 + 2"]]), | 257 | ("{ 2 + 2 }", expect![["{}; 2 + 2"]]), |
258 | ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]), | ||
259 | ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]), | ||
260 | ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]), | ||
252 | ]; | 261 | ]; |
253 | 262 | ||
254 | for (input, output) in test_vector { | 263 | for (input, output) in test_vector { |
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..81a6d00e2 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() { |
@@ -97,8 +101,9 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
97 | // | 101 | // |
98 | // .Fuzzy search details | 102 | // .Fuzzy search details |
99 | // | 103 | // |
100 | // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the identifiers only | 104 | // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only |
101 | // (i.e. in `HashMap` in the `std::collections::HashMap` path), also not in the module indentifiers. | 105 | // (i.e. in `HashMap` in the `std::collections::HashMap` path). |
106 | // For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols. | ||
102 | // | 107 | // |
103 | // .Merge Behavior | 108 | // .Merge Behavior |
104 | // | 109 | // |
@@ -122,15 +127,20 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() | |||
122 | let _p = profile::span("fuzzy_completion"); | 127 | let _p = profile::span("fuzzy_completion"); |
123 | let potential_import_name = ctx.token.to_string(); | 128 | let potential_import_name = ctx.token.to_string(); |
124 | 129 | ||
130 | if potential_import_name.len() < 2 { | ||
131 | return None; | ||
132 | } | ||
133 | |||
125 | let current_module = ctx.scope.module()?; | 134 | let current_module = ctx.scope.module()?; |
126 | let anchor = ctx.name_ref_syntax.as_ref()?; | 135 | let anchor = ctx.name_ref_syntax.as_ref()?; |
127 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; | 136 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; |
128 | 137 | ||
138 | let user_input_lowercased = potential_import_name.to_lowercase(); | ||
129 | let mut all_mod_paths = imports_locator::find_similar_imports( | 139 | let mut all_mod_paths = imports_locator::find_similar_imports( |
130 | &ctx.sema, | 140 | &ctx.sema, |
131 | ctx.krate?, | 141 | ctx.krate?, |
132 | Some(100), | 142 | Some(40), |
133 | &potential_import_name, | 143 | potential_import_name, |
134 | true, | 144 | true, |
135 | ) | 145 | ) |
136 | .filter_map(|import_candidate| { | 146 | .filter_map(|import_candidate| { |
@@ -146,7 +156,6 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() | |||
146 | .filter(|(mod_path, _)| mod_path.len() > 1) | 156 | .filter(|(mod_path, _)| mod_path.len() > 1) |
147 | .collect::<Vec<_>>(); | 157 | .collect::<Vec<_>>(); |
148 | 158 | ||
149 | let user_input_lowercased = potential_import_name.to_lowercase(); | ||
150 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { | 159 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { |
151 | compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) | 160 | compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) |
152 | }); | 161 | }); |
@@ -701,6 +710,7 @@ fn main() { <|> } | |||
701 | "#]], | 710 | "#]], |
702 | ); | 711 | ); |
703 | } | 712 | } |
713 | |||
704 | #[test] | 714 | #[test] |
705 | fn completes_enum_variant_matcharm() { | 715 | fn completes_enum_variant_matcharm() { |
706 | check( | 716 | check( |
@@ -722,6 +732,26 @@ fn main() { | |||
722 | } | 732 | } |
723 | 733 | ||
724 | #[test] | 734 | #[test] |
735 | fn completes_enum_variant_matcharm_ref() { | ||
736 | check( | ||
737 | r#" | ||
738 | enum Foo { Bar, Baz, Quux } | ||
739 | |||
740 | fn main() { | ||
741 | let foo = Foo::Quux; | ||
742 | match &foo { Qu<|> } | ||
743 | } | ||
744 | "#, | ||
745 | expect![[r#" | ||
746 | ev Foo::Bar () | ||
747 | ev Foo::Baz () | ||
748 | ev Foo::Quux () | ||
749 | en Foo | ||
750 | "#]], | ||
751 | ) | ||
752 | } | ||
753 | |||
754 | #[test] | ||
725 | fn completes_enum_variant_iflet() { | 755 | fn completes_enum_variant_iflet() { |
726 | check( | 756 | check( |
727 | r#" | 757 | 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/lib.rs b/crates/completion/src/lib.rs index 8e27bb153..c57d05bbe 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs | |||
@@ -137,7 +137,7 @@ pub fn resolve_completion_edits( | |||
137 | config: &CompletionConfig, | 137 | config: &CompletionConfig, |
138 | position: FilePosition, | 138 | position: FilePosition, |
139 | full_import_path: &str, | 139 | full_import_path: &str, |
140 | imported_name: &str, | 140 | imported_name: String, |
141 | ) -> Option<Vec<TextEdit>> { | 141 | ) -> Option<Vec<TextEdit>> { |
142 | let ctx = CompletionContext::new(db, position, config)?; | 142 | let ctx = CompletionContext::new(db, position, config)?; |
143 | let anchor = ctx.name_ref_syntax.as_ref()?; | 143 | let anchor = ctx.name_ref_syntax.as_ref()?; |
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/Cargo.toml b/crates/hir/Cargo.toml index 6dc5ad63b..d4ea7327e 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml | |||
@@ -14,7 +14,7 @@ log = "0.4.8" | |||
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | either = "1.5.3" | 15 | either = "1.5.3" |
16 | arrayvec = "0.5.1" | 16 | arrayvec = "0.5.1" |
17 | itertools = "0.9.0" | 17 | itertools = "0.10.0" |
18 | 18 | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 19 | stdx = { path = "../stdx", version = "0.0.0" } |
20 | syntax = { path = "../syntax", version = "0.0.0" } | 20 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 73ca6ba9f..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), |
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/Cargo.toml b/crates/hir_def/Cargo.toml index a88b5f57e..e8b581e2f 100644 --- a/crates/hir_def/Cargo.toml +++ b/crates/hir_def/Cargo.toml | |||
@@ -17,7 +17,7 @@ either = "1.5.3" | |||
17 | anymap = "0.12.1" | 17 | anymap = "0.12.1" |
18 | drop_bomb = "0.1.4" | 18 | drop_bomb = "0.1.4" |
19 | fst = { version = "0.4", default-features = false } | 19 | fst = { version = "0.4", default-features = false } |
20 | itertools = "0.9.0" | 20 | itertools = "0.10.0" |
21 | indexmap = "1.4.0" | 21 | indexmap = "1.4.0" |
22 | smallvec = "1.4.0" | 22 | smallvec = "1.4.0" |
23 | 23 | ||
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/import_map.rs b/crates/hir_def/src/import_map.rs index c0f108848..30b22f51d 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -238,32 +238,53 @@ pub enum ImportKind { | |||
238 | BuiltinType, | 238 | BuiltinType, |
239 | } | 239 | } |
240 | 240 | ||
241 | /// A way to match import map contents against the search query. | ||
242 | #[derive(Debug)] | ||
243 | pub enum SearchMode { | ||
244 | /// Import map entry should strictly match the query string. | ||
245 | Equals, | ||
246 | /// Import map entry should contain the query string. | ||
247 | Contains, | ||
248 | /// Import map entry should contain all letters from the query string, | ||
249 | /// in the same order, but not necessary adjacent. | ||
250 | Fuzzy, | ||
251 | } | ||
252 | |||
241 | #[derive(Debug)] | 253 | #[derive(Debug)] |
242 | pub struct Query { | 254 | pub struct Query { |
243 | query: String, | 255 | query: String, |
244 | lowercased: String, | 256 | lowercased: String, |
245 | anchor_end: bool, | 257 | name_only: bool, |
258 | search_mode: SearchMode, | ||
246 | case_sensitive: bool, | 259 | case_sensitive: bool, |
247 | limit: usize, | 260 | limit: usize, |
248 | exclude_import_kinds: FxHashSet<ImportKind>, | 261 | exclude_import_kinds: FxHashSet<ImportKind>, |
249 | } | 262 | } |
250 | 263 | ||
251 | impl Query { | 264 | impl Query { |
252 | pub fn new(query: &str) -> Self { | 265 | pub fn new(query: String) -> Self { |
266 | let lowercased = query.to_lowercase(); | ||
253 | Self { | 267 | Self { |
254 | lowercased: query.to_lowercase(), | 268 | query, |
255 | query: query.to_string(), | 269 | lowercased, |
256 | anchor_end: false, | 270 | name_only: false, |
271 | search_mode: SearchMode::Contains, | ||
257 | case_sensitive: false, | 272 | case_sensitive: false, |
258 | limit: usize::max_value(), | 273 | limit: usize::max_value(), |
259 | exclude_import_kinds: FxHashSet::default(), | 274 | exclude_import_kinds: FxHashSet::default(), |
260 | } | 275 | } |
261 | } | 276 | } |
262 | 277 | ||
263 | /// Only returns items whose paths end with the (case-insensitive) query string as their last | 278 | /// Matches entries' names only, ignoring the rest of |
264 | /// segment. | 279 | /// the qualifier. |
265 | pub fn anchor_end(self) -> Self { | 280 | /// Example: for `std::marker::PhantomData`, the name is `PhantomData`. |
266 | Self { anchor_end: true, ..self } | 281 | pub fn name_only(self) -> Self { |
282 | Self { name_only: true, ..self } | ||
283 | } | ||
284 | |||
285 | /// Specifies the way to search for the entries using the query. | ||
286 | pub fn search_mode(self, search_mode: SearchMode) -> Self { | ||
287 | Self { search_mode, ..self } | ||
267 | } | 288 | } |
268 | 289 | ||
269 | /// Limits the returned number of items to `limit`. | 290 | /// Limits the returned number of items to `limit`. |
@@ -283,6 +304,40 @@ impl Query { | |||
283 | } | 304 | } |
284 | } | 305 | } |
285 | 306 | ||
307 | fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool { | ||
308 | let mut input = if query.name_only { | ||
309 | input_path.segments.last().unwrap().to_string() | ||
310 | } else { | ||
311 | input_path.to_string() | ||
312 | }; | ||
313 | if enforce_lowercase || !query.case_sensitive { | ||
314 | input.make_ascii_lowercase(); | ||
315 | } | ||
316 | |||
317 | let query_string = | ||
318 | if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased }; | ||
319 | |||
320 | match query.search_mode { | ||
321 | SearchMode::Equals => &input == query_string, | ||
322 | SearchMode::Contains => input.contains(query_string), | ||
323 | SearchMode::Fuzzy => { | ||
324 | let mut unchecked_query_chars = query_string.chars(); | ||
325 | let mut mismatching_query_char = unchecked_query_chars.next(); | ||
326 | |||
327 | for input_char in input.chars() { | ||
328 | match mismatching_query_char { | ||
329 | None => return true, | ||
330 | Some(matching_query_char) if matching_query_char == input_char => { | ||
331 | mismatching_query_char = unchecked_query_chars.next(); | ||
332 | } | ||
333 | _ => (), | ||
334 | } | ||
335 | } | ||
336 | mismatching_query_char.is_none() | ||
337 | } | ||
338 | } | ||
339 | } | ||
340 | |||
286 | /// Searches dependencies of `krate` for an importable path matching `query`. | 341 | /// Searches dependencies of `krate` for an importable path matching `query`. |
287 | /// | 342 | /// |
288 | /// This returns a list of items that could be imported from dependencies of `krate`. | 343 | /// This returns a list of items that could be imported from dependencies of `krate`. |
@@ -312,39 +367,29 @@ pub fn search_dependencies<'a>( | |||
312 | let importables = &import_map.importables[indexed_value.value as usize..]; | 367 | let importables = &import_map.importables[indexed_value.value as usize..]; |
313 | 368 | ||
314 | // Path shared by the importable items in this group. | 369 | // Path shared by the importable items in this group. |
315 | let path = &import_map.map[&importables[0]].path; | 370 | let common_importables_path = &import_map.map[&importables[0]].path; |
316 | 371 | if !contains_query(&query, common_importables_path, true) { | |
317 | if query.anchor_end { | 372 | continue; |
318 | // Last segment must match query. | ||
319 | let last = path.segments.last().unwrap().to_string(); | ||
320 | if last.to_lowercase() != query.lowercased { | ||
321 | continue; | ||
322 | } | ||
323 | } | 373 | } |
324 | 374 | ||
375 | let common_importables_path_fst = fst_path(common_importables_path); | ||
325 | // Add the items from this `ModPath` group. Those are all subsequent items in | 376 | // Add the items from this `ModPath` group. Those are all subsequent items in |
326 | // `importables` whose paths match `path`. | 377 | // `importables` whose paths match `path`. |
327 | let iter = importables | 378 | let iter = importables |
328 | .iter() | 379 | .iter() |
329 | .copied() | 380 | .copied() |
330 | .take_while(|item| { | 381 | .take_while(|item| { |
331 | let item_path = &import_map.map[item].path; | 382 | common_importables_path_fst == fst_path(&import_map.map[item].path) |
332 | fst_path(item_path) == fst_path(path) | ||
333 | }) | 383 | }) |
334 | .filter(|&item| match item_import_kind(item) { | 384 | .filter(|&item| match item_import_kind(item) { |
335 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), | 385 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), |
336 | None => true, | 386 | None => true, |
387 | }) | ||
388 | .filter(|item| { | ||
389 | !query.case_sensitive // we've already checked the common importables path case-insensitively | ||
390 | || contains_query(&query, &import_map.map[item].path, false) | ||
337 | }); | 391 | }); |
338 | 392 | res.extend(iter); | |
339 | if query.case_sensitive { | ||
340 | // FIXME: This does not do a subsequence match. | ||
341 | res.extend(iter.filter(|item| { | ||
342 | let item_path = &import_map.map[item].path; | ||
343 | item_path.to_string().contains(&query.query) | ||
344 | })); | ||
345 | } else { | ||
346 | res.extend(iter); | ||
347 | } | ||
348 | 393 | ||
349 | if res.len() >= query.limit { | 394 | if res.len() >= query.limit { |
350 | res.truncate(query.limit); | 395 | res.truncate(query.limit); |
@@ -387,8 +432,9 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> { | |||
387 | mod tests { | 432 | mod tests { |
388 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; | 433 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; |
389 | use expect_test::{expect, Expect}; | 434 | use expect_test::{expect, Expect}; |
435 | use stdx::format_to; | ||
390 | 436 | ||
391 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; | 437 | use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup}; |
392 | 438 | ||
393 | use super::*; | 439 | use super::*; |
394 | 440 | ||
@@ -407,14 +453,32 @@ mod tests { | |||
407 | .into_iter() | 453 | .into_iter() |
408 | .filter_map(|item| { | 454 | .filter_map(|item| { |
409 | let mark = match item { | 455 | let mark = match item { |
456 | ItemInNs::Types(ModuleDefId::FunctionId(_)) | ||
457 | | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", | ||
410 | ItemInNs::Types(_) => "t", | 458 | ItemInNs::Types(_) => "t", |
411 | ItemInNs::Values(_) => "v", | 459 | ItemInNs::Values(_) => "v", |
412 | ItemInNs::Macros(_) => "m", | 460 | ItemInNs::Macros(_) => "m", |
413 | }; | 461 | }; |
414 | let item = assoc_to_trait(&db, item); | ||
415 | item.krate(db.upcast()).map(|krate| { | 462 | item.krate(db.upcast()).map(|krate| { |
416 | let map = db.import_map(krate); | 463 | let map = db.import_map(krate); |
417 | let path = map.path_of(item).unwrap(); | 464 | |
465 | let path = match assoc_to_trait(&db, item) { | ||
466 | Some(trait_) => { | ||
467 | let mut full_path = map.path_of(trait_).unwrap().to_string(); | ||
468 | if let ItemInNs::Types(ModuleDefId::FunctionId(function_id)) | ||
469 | | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item | ||
470 | { | ||
471 | format_to!( | ||
472 | full_path, | ||
473 | "::{}", | ||
474 | FunctionData::fn_data_query(&db, function_id).name, | ||
475 | ); | ||
476 | } | ||
477 | full_path | ||
478 | } | ||
479 | None => map.path_of(item).unwrap().to_string(), | ||
480 | }; | ||
481 | |||
418 | format!( | 482 | format!( |
419 | "{}::{} ({})\n", | 483 | "{}::{} ({})\n", |
420 | crate_graph[krate].display_name.as_ref().unwrap(), | 484 | crate_graph[krate].display_name.as_ref().unwrap(), |
@@ -427,15 +491,15 @@ mod tests { | |||
427 | expect.assert_eq(&actual) | 491 | expect.assert_eq(&actual) |
428 | } | 492 | } |
429 | 493 | ||
430 | fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs { | 494 | fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> { |
431 | let assoc: AssocItemId = match item { | 495 | let assoc: AssocItemId = match item { |
432 | ItemInNs::Types(it) | ItemInNs::Values(it) => match it { | 496 | ItemInNs::Types(it) | ItemInNs::Values(it) => match it { |
433 | ModuleDefId::TypeAliasId(it) => it.into(), | 497 | ModuleDefId::TypeAliasId(it) => it.into(), |
434 | ModuleDefId::FunctionId(it) => it.into(), | 498 | ModuleDefId::FunctionId(it) => it.into(), |
435 | ModuleDefId::ConstId(it) => it.into(), | 499 | ModuleDefId::ConstId(it) => it.into(), |
436 | _ => return item, | 500 | _ => return None, |
437 | }, | 501 | }, |
438 | _ => return item, | 502 | _ => return None, |
439 | }; | 503 | }; |
440 | 504 | ||
441 | let container = match assoc { | 505 | let container = match assoc { |
@@ -445,8 +509,8 @@ mod tests { | |||
445 | }; | 509 | }; |
446 | 510 | ||
447 | match container { | 511 | match container { |
448 | AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()), | 512 | AssocContainerId::TraitId(it) => Some(ItemInNs::Types(it.into())), |
449 | _ => item, | 513 | _ => None, |
450 | } | 514 | } |
451 | } | 515 | } |
452 | 516 | ||
@@ -685,7 +749,7 @@ mod tests { | |||
685 | } | 749 | } |
686 | 750 | ||
687 | #[test] | 751 | #[test] |
688 | fn search() { | 752 | fn search_mode() { |
689 | let ra_fixture = r#" | 753 | let ra_fixture = r#" |
690 | //- /main.rs crate:main deps:dep | 754 | //- /main.rs crate:main deps:dep |
691 | //- /dep.rs crate:dep deps:tdep | 755 | //- /dep.rs crate:dep deps:tdep |
@@ -713,28 +777,96 @@ mod tests { | |||
713 | check_search( | 777 | check_search( |
714 | ra_fixture, | 778 | ra_fixture, |
715 | "main", | 779 | "main", |
716 | Query::new("fmt"), | 780 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), |
717 | expect![[r#" | 781 | expect![[r#" |
718 | dep::fmt (t) | 782 | dep::fmt (t) |
719 | dep::Fmt (t) | 783 | dep::Fmt (t) |
720 | dep::Fmt (v) | 784 | dep::Fmt (v) |
721 | dep::Fmt (m) | 785 | dep::Fmt (m) |
722 | dep::fmt::Display (t) | 786 | dep::fmt::Display (t) |
723 | dep::format (v) | 787 | dep::format (f) |
788 | dep::fmt::Display::fmt (f) | ||
789 | "#]], | ||
790 | ); | ||
791 | |||
792 | check_search( | ||
793 | ra_fixture, | ||
794 | "main", | ||
795 | Query::new("fmt".to_string()).search_mode(SearchMode::Equals), | ||
796 | expect![[r#" | ||
797 | dep::fmt (t) | ||
798 | dep::Fmt (t) | ||
799 | dep::Fmt (v) | ||
800 | dep::Fmt (m) | ||
801 | dep::fmt::Display::fmt (f) | ||
802 | "#]], | ||
803 | ); | ||
804 | |||
805 | check_search( | ||
806 | ra_fixture, | ||
807 | "main", | ||
808 | Query::new("fmt".to_string()).search_mode(SearchMode::Contains), | ||
809 | expect![[r#" | ||
810 | dep::fmt (t) | ||
811 | dep::Fmt (t) | ||
812 | dep::Fmt (v) | ||
813 | dep::Fmt (m) | ||
724 | dep::fmt::Display (t) | 814 | dep::fmt::Display (t) |
815 | dep::fmt::Display::fmt (f) | ||
725 | "#]], | 816 | "#]], |
726 | ); | 817 | ); |
818 | } | ||
819 | |||
820 | #[test] | ||
821 | fn name_only() { | ||
822 | let ra_fixture = r#" | ||
823 | //- /main.rs crate:main deps:dep | ||
824 | //- /dep.rs crate:dep deps:tdep | ||
825 | use tdep::fmt as fmt_dep; | ||
826 | pub mod fmt { | ||
827 | pub trait Display { | ||
828 | fn fmt(); | ||
829 | } | ||
830 | } | ||
831 | #[macro_export] | ||
832 | macro_rules! Fmt { | ||
833 | () => {}; | ||
834 | } | ||
835 | pub struct Fmt; | ||
836 | |||
837 | pub fn format() {} | ||
838 | pub fn no() {} | ||
839 | |||
840 | //- /tdep.rs crate:tdep | ||
841 | pub mod fmt { | ||
842 | pub struct NotImportableFromMain; | ||
843 | } | ||
844 | "#; | ||
727 | 845 | ||
728 | check_search( | 846 | check_search( |
729 | ra_fixture, | 847 | ra_fixture, |
730 | "main", | 848 | "main", |
731 | Query::new("fmt").anchor_end(), | 849 | Query::new("fmt".to_string()), |
732 | expect![[r#" | 850 | expect![[r#" |
733 | dep::fmt (t) | 851 | dep::fmt (t) |
734 | dep::Fmt (t) | 852 | dep::Fmt (t) |
735 | dep::Fmt (v) | 853 | dep::Fmt (v) |
736 | dep::Fmt (m) | 854 | dep::Fmt (m) |
737 | dep::fmt::Display (t) | 855 | dep::fmt::Display (t) |
856 | dep::fmt::Display::fmt (f) | ||
857 | "#]], | ||
858 | ); | ||
859 | |||
860 | check_search( | ||
861 | ra_fixture, | ||
862 | "main", | ||
863 | Query::new("fmt".to_string()).name_only(), | ||
864 | expect![[r#" | ||
865 | dep::fmt (t) | ||
866 | dep::Fmt (t) | ||
867 | dep::Fmt (v) | ||
868 | dep::Fmt (m) | ||
869 | dep::fmt::Display::fmt (f) | ||
738 | "#]], | 870 | "#]], |
739 | ); | 871 | ); |
740 | } | 872 | } |
@@ -752,7 +884,7 @@ mod tests { | |||
752 | check_search( | 884 | check_search( |
753 | ra_fixture, | 885 | ra_fixture, |
754 | "main", | 886 | "main", |
755 | Query::new("FMT"), | 887 | Query::new("FMT".to_string()), |
756 | expect![[r#" | 888 | expect![[r#" |
757 | dep::fmt (t) | 889 | dep::fmt (t) |
758 | dep::fmt (v) | 890 | dep::fmt (v) |
@@ -764,7 +896,7 @@ mod tests { | |||
764 | check_search( | 896 | check_search( |
765 | ra_fixture, | 897 | ra_fixture, |
766 | "main", | 898 | "main", |
767 | Query::new("FMT").case_sensitive(), | 899 | Query::new("FMT".to_string()).case_sensitive(), |
768 | expect![[r#" | 900 | expect![[r#" |
769 | dep::FMT (t) | 901 | dep::FMT (t) |
770 | dep::FMT (v) | 902 | dep::FMT (v) |
@@ -793,7 +925,7 @@ mod tests { | |||
793 | pub fn no() {} | 925 | pub fn no() {} |
794 | "#, | 926 | "#, |
795 | "main", | 927 | "main", |
796 | Query::new("").limit(2), | 928 | Query::new("".to_string()).limit(2), |
797 | expect![[r#" | 929 | expect![[r#" |
798 | dep::fmt (t) | 930 | dep::fmt (t) |
799 | dep::Fmt (t) | 931 | dep::Fmt (t) |
@@ -814,7 +946,7 @@ mod tests { | |||
814 | check_search( | 946 | check_search( |
815 | ra_fixture, | 947 | ra_fixture, |
816 | "main", | 948 | "main", |
817 | Query::new("FMT"), | 949 | Query::new("FMT".to_string()), |
818 | expect![[r#" | 950 | expect![[r#" |
819 | dep::fmt (t) | 951 | dep::fmt (t) |
820 | dep::fmt (v) | 952 | dep::fmt (v) |
@@ -826,7 +958,7 @@ mod tests { | |||
826 | check_search( | 958 | check_search( |
827 | ra_fixture, | 959 | ra_fixture, |
828 | "main", | 960 | "main", |
829 | Query::new("FMT").exclude_import_kind(ImportKind::Adt), | 961 | Query::new("FMT".to_string()).exclude_import_kind(ImportKind::Adt), |
830 | expect![[r#""#]], | 962 | expect![[r#""#]], |
831 | ); | 963 | ); |
832 | } | 964 | } |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index ffd0381d4..5682e122d 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -249,7 +249,7 @@ impl CrateDefMap { | |||
249 | buf.push_str(" _"); | 249 | buf.push_str(" _"); |
250 | } | 250 | } |
251 | 251 | ||
252 | buf.push_str("\n"); | 252 | buf.push('\n'); |
253 | } | 253 | } |
254 | 254 | ||
255 | for (name, child) in map.modules[module].children.iter() { | 255 | for (name, child) in map.modules[module].children.iter() { |
@@ -454,7 +454,7 @@ mod diagnostics { | |||
454 | }); | 454 | }); |
455 | for token in tokens { | 455 | for token in tokens { |
456 | if token.kind() == SyntaxKind::IDENT | 456 | if token.kind() == SyntaxKind::IDENT |
457 | && token.to_string() == *name | 457 | && token.text() == name.as_str() |
458 | { | 458 | { |
459 | precise_location = Some(token.text_range()); | 459 | precise_location = Some(token.text_range()); |
460 | break 'outer; | 460 | break 'outer; |
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_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index f9bf5bc72..e5e9e8ca1 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -677,7 +677,7 @@ fn macro_expansion_overflow() { | |||
677 | r#" | 677 | r#" |
678 | macro_rules! a { | 678 | macro_rules! a { |
679 | ($e:expr; $($t:tt)*) => { | 679 | ($e:expr; $($t:tt)*) => { |
680 | b!($($t)*); | 680 | b!(static = (); $($t)*); |
681 | }; | 681 | }; |
682 | () => {}; | 682 | () => {}; |
683 | } | 683 | } |
@@ -689,7 +689,7 @@ macro_rules! b { | |||
689 | () => {}; | 689 | () => {}; |
690 | } | 690 | } |
691 | 691 | ||
692 | b! { static = #[] (); } | 692 | b! { static = #[] ();} |
693 | "#, | 693 | "#, |
694 | expect![[r#" | 694 | expect![[r#" |
695 | crate | 695 | crate |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index dddbbcdac..80b60d59f 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -259,7 +259,8 @@ fn format_args_expand( | |||
259 | } | 259 | } |
260 | for arg in &mut args { | 260 | for arg in &mut args { |
261 | // Remove `key =`. | 261 | // Remove `key =`. |
262 | if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { | 262 | if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint) |
263 | { | ||
263 | arg.drain(..2); | 264 | arg.drain(..2); |
264 | } | 265 | } |
265 | } | 266 | } |
@@ -563,6 +564,7 @@ mod tests { | |||
563 | 564 | ||
564 | let args = macro_call.token_tree().unwrap(); | 565 | let args = macro_call.token_tree().unwrap(); |
565 | let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0; | 566 | let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0; |
567 | let call_id = AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)); | ||
566 | 568 | ||
567 | let arg_id = db.intern_eager_expansion({ | 569 | let arg_id = db.intern_eager_expansion({ |
568 | EagerCallLoc { | 570 | EagerCallLoc { |
@@ -570,7 +572,7 @@ mod tests { | |||
570 | fragment: FragmentKind::Expr, | 572 | fragment: FragmentKind::Expr, |
571 | subtree: Arc::new(parsed_args.clone()), | 573 | subtree: Arc::new(parsed_args.clone()), |
572 | krate, | 574 | krate, |
573 | file_id: file_id.into(), | 575 | call: call_id, |
574 | } | 576 | } |
575 | }); | 577 | }); |
576 | 578 | ||
@@ -580,7 +582,7 @@ mod tests { | |||
580 | fragment, | 582 | fragment, |
581 | subtree: Arc::new(subtree), | 583 | subtree: Arc::new(subtree), |
582 | krate, | 584 | krate, |
583 | file_id: file_id.into(), | 585 | call: call_id, |
584 | }; | 586 | }; |
585 | 587 | ||
586 | let id: MacroCallId = db.intern_eager_expansion(eager).into(); | 588 | 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..06f0a3ed9 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 | }; |
@@ -270,7 +271,7 @@ fn expand_proc_macro( | |||
270 | _ => unreachable!(), | 271 | _ => unreachable!(), |
271 | }; | 272 | }; |
272 | 273 | ||
273 | expander.expand(db, lazy_id, ¯o_arg.0) | 274 | expander.expand(db, loc.krate, ¯o_arg.0) |
274 | } | 275 | } |
275 | 276 | ||
276 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | 277 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
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_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index 38882d2b6..7c77f6ce0 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Proc Macro Expander stub | 1 | //! Proc Macro Expander stub |
2 | 2 | ||
3 | use crate::{db::AstDatabase, LazyMacroId}; | 3 | use crate::db::AstDatabase; |
4 | use base_db::{CrateId, ProcMacroId}; | 4 | use base_db::{CrateId, ProcMacroId}; |
5 | use tt::buffer::{Cursor, TokenBuffer}; | 5 | use tt::buffer::{Cursor, TokenBuffer}; |
6 | 6 | ||
@@ -32,7 +32,7 @@ impl ProcMacroExpander { | |||
32 | pub fn expand( | 32 | pub fn expand( |
33 | self, | 33 | self, |
34 | db: &dyn AstDatabase, | 34 | db: &dyn AstDatabase, |
35 | _id: LazyMacroId, | 35 | calling_crate: CrateId, |
36 | tt: &tt::Subtree, | 36 | tt: &tt::Subtree, |
37 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 37 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
38 | match self.proc_macro_id { | 38 | match self.proc_macro_id { |
@@ -47,7 +47,10 @@ impl ProcMacroExpander { | |||
47 | let tt = remove_derive_attrs(tt) | 47 | let tt = remove_derive_attrs(tt) |
48 | .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; | 48 | .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; |
49 | 49 | ||
50 | proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) | 50 | // Proc macros have access to the environment variables of the invoking crate. |
51 | let env = &krate_graph[calling_crate].env; | ||
52 | |||
53 | proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from) | ||
51 | } | 54 | } |
52 | None => Err(mbe::ExpandError::UnresolvedProcMacro), | 55 | None => Err(mbe::ExpandError::UnresolvedProcMacro), |
53 | } | 56 | } |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index 289e812fe..2dfccd191 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -10,16 +10,16 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | itertools = "0.9.0" | 13 | itertools = "0.10.0" |
14 | arrayvec = "0.5.1" | 14 | arrayvec = "0.5.1" |
15 | smallvec = "1.2.0" | 15 | smallvec = "1.2.0" |
16 | ena = "0.14.0" | 16 | 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..f2fc69b2f 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()); |
@@ -648,6 +648,8 @@ impl<'a> InferenceContext<'a> { | |||
648 | } | 648 | } |
649 | Expr::Array(array) => { | 649 | Expr::Array(array) => { |
650 | let elem_ty = match &expected.ty { | 650 | let elem_ty = match &expected.ty { |
651 | // FIXME: remove when https://github.com/rust-lang/rust/issues/80501 is fixed | ||
652 | #[allow(unreachable_patterns)] | ||
651 | ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => { | 653 | ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => { |
652 | st.as_single().clone() | 654 | st.as_single().clone() |
653 | } | 655 | } |
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/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/ide/Cargo.toml b/crates/ide/Cargo.toml index 4d483580d..f1544dbe0 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | [dependencies] | 12 | [dependencies] |
13 | either = "1.5.3" | 13 | either = "1.5.3" |
14 | indexmap = "1.4.0" | 14 | indexmap = "1.4.0" |
15 | itertools = "0.9.0" | 15 | itertools = "0.10.0" |
16 | log = "0.4.8" | 16 | log = "0.4.8" |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | oorandom = "11.1.2" | 18 | oorandom = "11.1.2" |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 049f808dc..79d126ff2 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -199,6 +199,12 @@ fn check_unnecessary_braces_in_use_statement( | |||
199 | ) -> Option<()> { | 199 | ) -> Option<()> { |
200 | let use_tree_list = ast::UseTreeList::cast(node.clone())?; | 200 | let use_tree_list = ast::UseTreeList::cast(node.clone())?; |
201 | if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { | 201 | if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { |
202 | // If there is a comment inside the bracketed `use`, | ||
203 | // assume it is a commented out module path and don't show diagnostic. | ||
204 | if use_tree_list.has_inner_comment() { | ||
205 | return Some(()); | ||
206 | } | ||
207 | |||
202 | let use_range = use_tree_list.syntax().text_range(); | 208 | let use_range = use_tree_list.syntax().text_range(); |
203 | let edit = | 209 | let edit = |
204 | text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree) | 210 | text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree) |
@@ -248,35 +254,7 @@ mod tests { | |||
248 | /// * a diagnostic is produced | 254 | /// * a diagnostic is produced |
249 | /// * this diagnostic fix trigger range touches the input cursor position | 255 | /// * 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 | 256 | /// * 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) { | 257 | 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); | 258 | let after = trim_indent(ra_fixture_after); |
281 | 259 | ||
282 | let (analysis, file_position) = fixture::position(ra_fixture_before); | 260 | let (analysis, file_position) = fixture::position(ra_fixture_before); |
@@ -286,10 +264,12 @@ mod tests { | |||
286 | .pop() | 264 | .pop() |
287 | .unwrap(); | 265 | .unwrap(); |
288 | let fix = diagnostic.fix.unwrap(); | 266 | let fix = diagnostic.fix.unwrap(); |
289 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | ||
290 | let actual = { | 267 | let actual = { |
291 | let mut actual = target_file_contents.to_string(); | 268 | let file_id = fix.source_change.source_file_edits.first().unwrap().file_id; |
269 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); | ||
270 | |||
292 | // Go from the last one to the first one, so that ranges won't be affected by previous edits. | 271 | // Go from the last one to the first one, so that ranges won't be affected by previous edits. |
272 | // FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/4901#issuecomment-644675309 | ||
293 | for edit in fix.source_change.source_file_edits.iter().rev() { | 273 | for edit in fix.source_change.source_file_edits.iter().rev() { |
294 | edit.edit.apply(&mut actual); | 274 | edit.edit.apply(&mut actual); |
295 | } | 275 | } |
@@ -305,29 +285,6 @@ mod tests { | |||
305 | ); | 285 | ); |
306 | } | 286 | } |
307 | 287 | ||
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 | 288 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics |
332 | /// apply to the file containing the cursor. | 289 | /// apply to the file containing the cursor. |
333 | pub(crate) fn check_no_diagnostics(ra_fixture: &str) { | 290 | pub(crate) fn check_no_diagnostics(ra_fixture: &str) { |
@@ -619,6 +576,7 @@ fn test_fn() { | |||
619 | ), | 576 | ), |
620 | path: "foo.rs", | 577 | path: "foo.rs", |
621 | }, | 578 | }, |
579 | initial_contents: "", | ||
622 | }, | 580 | }, |
623 | ], | 581 | ], |
624 | is_snippet: false, | 582 | is_snippet: false, |
@@ -686,6 +644,22 @@ mod a { | |||
686 | } | 644 | } |
687 | "#, | 645 | "#, |
688 | ); | 646 | ); |
647 | check_no_diagnostics( | ||
648 | r#" | ||
649 | use a; | ||
650 | use a::{ | ||
651 | c, | ||
652 | // d::e | ||
653 | }; | ||
654 | |||
655 | mod a { | ||
656 | mod c {} | ||
657 | mod d { | ||
658 | mod e {} | ||
659 | } | ||
660 | } | ||
661 | "#, | ||
662 | ); | ||
689 | check_fix( | 663 | check_fix( |
690 | r" | 664 | r" |
691 | mod b {} | 665 | mod b {} |
@@ -763,25 +737,25 @@ struct Foo { | |||
763 | 737 | ||
764 | #[test] | 738 | #[test] |
765 | fn test_add_field_in_other_file_from_usage() { | 739 | fn test_add_field_in_other_file_from_usage() { |
766 | check_apply_diagnostic_fix_in_other_file( | 740 | check_fix( |
767 | r" | 741 | r#" |
768 | //- /main.rs | 742 | //- /main.rs |
769 | mod foo; | 743 | mod foo; |
770 | 744 | ||
771 | fn main() { | 745 | fn main() { |
772 | <|>foo::Foo { bar: 3, baz: false}; | 746 | foo::Foo { bar: 3, <|>baz: false}; |
773 | } | 747 | } |
774 | //- /foo.rs | 748 | //- /foo.rs |
775 | struct Foo { | 749 | struct Foo { |
776 | bar: i32 | 750 | bar: i32 |
777 | } | 751 | } |
778 | ", | 752 | "#, |
779 | r" | 753 | r#" |
780 | struct Foo { | 754 | struct Foo { |
781 | bar: i32, | 755 | bar: i32, |
782 | pub(crate) baz: bool | 756 | pub(crate) baz: bool |
783 | } | 757 | } |
784 | ", | 758 | "#, |
785 | ) | 759 | ) |
786 | } | 760 | } |
787 | 761 | ||
@@ -801,7 +775,7 @@ struct Foo { | |||
801 | 775 | ||
802 | #[test] | 776 | #[test] |
803 | fn test_rename_incorrect_case() { | 777 | fn test_rename_incorrect_case() { |
804 | check_fixes( | 778 | check_fix( |
805 | r#" | 779 | r#" |
806 | pub struct test_struct<|> { one: i32 } | 780 | pub struct test_struct<|> { one: i32 } |
807 | 781 | ||
@@ -818,7 +792,7 @@ pub fn some_fn(val: TestStruct) -> TestStruct { | |||
818 | "#, | 792 | "#, |
819 | ); | 793 | ); |
820 | 794 | ||
821 | check_fixes( | 795 | check_fix( |
822 | r#" | 796 | r#" |
823 | pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { | 797 | pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { |
824 | NonSnakeCase | 798 | NonSnakeCase |
@@ -831,7 +805,7 @@ pub fn some_fn(non_snake_case: u8) -> u8 { | |||
831 | "#, | 805 | "#, |
832 | ); | 806 | ); |
833 | 807 | ||
834 | check_fixes( | 808 | check_fix( |
835 | r#" | 809 | r#" |
836 | pub fn SomeFn<|>(val: u8) -> u8 { | 810 | pub fn SomeFn<|>(val: u8) -> u8 { |
837 | if val != 0 { SomeFn(val - 1) } else { val } | 811 | if val != 0 { SomeFn(val - 1) } else { val } |
@@ -844,7 +818,7 @@ pub fn some_fn(val: u8) -> u8 { | |||
844 | "#, | 818 | "#, |
845 | ); | 819 | ); |
846 | 820 | ||
847 | check_fixes( | 821 | check_fix( |
848 | r#" | 822 | r#" |
849 | fn some_fn() { | 823 | fn some_fn() { |
850 | let whatAWeird_Formatting<|> = 10; | 824 | let whatAWeird_Formatting<|> = 10; |
@@ -873,7 +847,7 @@ fn foo() { | |||
873 | 847 | ||
874 | #[test] | 848 | #[test] |
875 | fn test_rename_incorrect_case_struct_method() { | 849 | fn test_rename_incorrect_case_struct_method() { |
876 | check_fixes( | 850 | check_fix( |
877 | r#" | 851 | r#" |
878 | pub struct TestStruct; | 852 | pub struct TestStruct; |
879 | 853 | ||
@@ -894,4 +868,17 @@ impl TestStruct { | |||
894 | "#, | 868 | "#, |
895 | ); | 869 | ); |
896 | } | 870 | } |
871 | |||
872 | #[test] | ||
873 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
874 | let input = r#"fn FOO<|>() {}"#; | ||
875 | let expected = r#"fn foo() {}"#; | ||
876 | |||
877 | let (analysis, file_position) = fixture::position(input); | ||
878 | let diagnostics = | ||
879 | analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap(); | ||
880 | assert_eq!(diagnostics.len(), 1); | ||
881 | |||
882 | check_fix(input, expected); | ||
883 | } | ||
897 | } | 884 | } |
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 431da5d9c..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#" |
@@ -1105,4 +1130,19 @@ fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {} | |||
1105 | "#, | 1130 | "#, |
1106 | ); | 1131 | ); |
1107 | } | 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 | } | ||
1108 | } | 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..b3331f03f 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, |
@@ -475,7 +475,7 @@ impl Analysis { | |||
475 | config: &CompletionConfig, | 475 | config: &CompletionConfig, |
476 | position: FilePosition, | 476 | position: FilePosition, |
477 | full_import_path: &str, | 477 | full_import_path: &str, |
478 | imported_name: &str, | 478 | imported_name: String, |
479 | ) -> Cancelable<Vec<TextEdit>> { | 479 | ) -> Cancelable<Vec<TextEdit>> { |
480 | Ok(self | 480 | Ok(self |
481 | .with_db(|db| { | 481 | .with_db(|db| { |
@@ -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/Cargo.toml b/crates/ide_db/Cargo.toml index 0ad6e1000..ebe53c8ee 100644 --- a/crates/ide_db/Cargo.toml +++ b/crates/ide_db/Cargo.toml | |||
@@ -19,7 +19,7 @@ fst = { version = "0.4", default-features = false } | |||
19 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
20 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |
21 | either = "1.6.1" | 21 | either = "1.6.1" |
22 | itertools = "0.9.0" | 22 | itertools = "0.10.0" |
23 | 23 | ||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | syntax = { path = "../syntax", version = "0.0.0" } | 25 | syntax = { path = "../syntax", version = "0.0.0" } |
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/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index b2980a5d6..0f4c2ca47 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs | |||
@@ -15,19 +15,23 @@ use rustc_hash::FxHashSet; | |||
15 | pub fn find_exact_imports<'a>( | 15 | pub fn find_exact_imports<'a>( |
16 | sema: &Semantics<'a, RootDatabase>, | 16 | sema: &Semantics<'a, RootDatabase>, |
17 | krate: Crate, | 17 | krate: Crate, |
18 | name_to_import: &str, | 18 | name_to_import: String, |
19 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 19 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { |
20 | let _p = profile::span("find_exact_imports"); | 20 | let _p = profile::span("find_exact_imports"); |
21 | find_imports( | 21 | find_imports( |
22 | sema, | 22 | sema, |
23 | krate, | 23 | krate, |
24 | { | 24 | { |
25 | let mut local_query = symbol_index::Query::new(name_to_import.to_string()); | 25 | let mut local_query = symbol_index::Query::new(name_to_import.clone()); |
26 | local_query.exact(); | 26 | local_query.exact(); |
27 | local_query.limit(40); | 27 | local_query.limit(40); |
28 | local_query | 28 | local_query |
29 | }, | 29 | }, |
30 | import_map::Query::new(name_to_import).anchor_end().case_sensitive().limit(40), | 30 | import_map::Query::new(name_to_import) |
31 | .limit(40) | ||
32 | .name_only() | ||
33 | .search_mode(import_map::SearchMode::Equals) | ||
34 | .case_sensitive(), | ||
31 | ) | 35 | ) |
32 | } | 36 | } |
33 | 37 | ||
@@ -35,17 +39,18 @@ pub fn find_similar_imports<'a>( | |||
35 | sema: &Semantics<'a, RootDatabase>, | 39 | sema: &Semantics<'a, RootDatabase>, |
36 | krate: Crate, | 40 | krate: Crate, |
37 | limit: Option<usize>, | 41 | limit: Option<usize>, |
38 | name_to_import: &str, | 42 | fuzzy_search_string: String, |
39 | ignore_modules: bool, | 43 | name_only: bool, |
40 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 44 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { |
41 | let _p = profile::span("find_similar_imports"); | 45 | let _p = profile::span("find_similar_imports"); |
42 | 46 | ||
43 | let mut external_query = import_map::Query::new(name_to_import); | 47 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) |
44 | if ignore_modules { | 48 | .search_mode(import_map::SearchMode::Fuzzy); |
45 | external_query = external_query.exclude_import_kind(import_map::ImportKind::Module); | 49 | if name_only { |
50 | external_query = external_query.name_only(); | ||
46 | } | 51 | } |
47 | 52 | ||
48 | let mut local_query = symbol_index::Query::new(name_to_import.to_string()); | 53 | let mut local_query = symbol_index::Query::new(fuzzy_search_string); |
49 | 54 | ||
50 | if let Some(limit) = limit { | 55 | if let Some(limit) = limit { |
51 | local_query.limit(limit); | 56 | local_query.limit(limit); |
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/lib.rs b/crates/mbe/src/lib.rs index 3ad609a00..b3472879d 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -14,10 +14,10 @@ mod tests; | |||
14 | 14 | ||
15 | use std::fmt; | 15 | use std::fmt; |
16 | 16 | ||
17 | pub use tt::{Delimiter, Punct}; | 17 | pub use tt::{Delimiter, DelimiterKind, Punct}; |
18 | 18 | ||
19 | use crate::{ | 19 | use crate::{ |
20 | parser::{parse_pattern, Op}, | 20 | parser::{parse_pattern, parse_template, Op}, |
21 | tt_iter::TtIter, | 21 | tt_iter::TtIter, |
22 | }; | 22 | }; |
23 | 23 | ||
@@ -78,8 +78,24 @@ pub struct MacroRules { | |||
78 | 78 | ||
79 | #[derive(Clone, Debug, PartialEq, Eq)] | 79 | #[derive(Clone, Debug, PartialEq, Eq)] |
80 | struct Rule { | 80 | struct Rule { |
81 | lhs: tt::Subtree, | 81 | lhs: MetaTemplate, |
82 | rhs: tt::Subtree, | 82 | rhs: MetaTemplate, |
83 | } | ||
84 | |||
85 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
86 | struct MetaTemplate { | ||
87 | delimiter: Option<Delimiter>, | ||
88 | tokens: Vec<Result<Op, ExpandError>>, | ||
89 | } | ||
90 | |||
91 | impl<'a> MetaTemplate { | ||
92 | fn iter(&self) -> impl Iterator<Item = &Result<Op, ExpandError>> { | ||
93 | self.tokens.iter() | ||
94 | } | ||
95 | |||
96 | fn delimiter_kind(&self) -> Option<DelimiterKind> { | ||
97 | self.delimiter.map(|it| it.kind) | ||
98 | } | ||
83 | } | 99 | } |
84 | 100 | ||
85 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 101 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
@@ -167,7 +183,7 @@ impl MacroRules { | |||
167 | rules.push(rule); | 183 | rules.push(rule); |
168 | if let Err(()) = src.expect_char(';') { | 184 | if let Err(()) = src.expect_char(';') { |
169 | if src.len() > 0 { | 185 | if src.len() > 0 { |
170 | return Err(ParseError::Expected("expected `:`".to_string())); | 186 | return Err(ParseError::Expected("expected `;`".to_string())); |
171 | } | 187 | } |
172 | break; | 188 | break; |
173 | } | 189 | } |
@@ -201,23 +217,23 @@ impl MacroRules { | |||
201 | 217 | ||
202 | impl Rule { | 218 | impl Rule { |
203 | fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { | 219 | fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { |
204 | let mut lhs = src | 220 | let lhs = src |
205 | .expect_subtree() | 221 | .expect_subtree() |
206 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))? | 222 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; |
207 | .clone(); | ||
208 | lhs.delimiter = None; | ||
209 | src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; | 223 | src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; |
210 | src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; | 224 | src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; |
211 | let mut rhs = src | 225 | let rhs = src |
212 | .expect_subtree() | 226 | .expect_subtree() |
213 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))? | 227 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; |
214 | .clone(); | 228 | |
215 | rhs.delimiter = None; | 229 | let lhs = MetaTemplate { tokens: parse_pattern(&lhs), delimiter: None }; |
230 | let rhs = MetaTemplate { tokens: parse_template(&rhs), delimiter: None }; | ||
231 | |||
216 | Ok(crate::Rule { lhs, rhs }) | 232 | Ok(crate::Rule { lhs, rhs }) |
217 | } | 233 | } |
218 | } | 234 | } |
219 | 235 | ||
220 | fn to_parse_error(e: ExpandError) -> ParseError { | 236 | fn to_parse_error(e: &ExpandError) -> ParseError { |
221 | let msg = match e { | 237 | let msg = match e { |
222 | ExpandError::InvalidRepeat => "invalid repeat".to_string(), | 238 | ExpandError::InvalidRepeat => "invalid repeat".to_string(), |
223 | _ => "invalid macro definition".to_string(), | 239 | _ => "invalid macro definition".to_string(), |
@@ -225,22 +241,22 @@ fn to_parse_error(e: ExpandError) -> ParseError { | |||
225 | ParseError::Expected(msg) | 241 | ParseError::Expected(msg) |
226 | } | 242 | } |
227 | 243 | ||
228 | fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { | 244 | fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { |
229 | for op in parse_pattern(pattern) { | 245 | for op in pattern.iter() { |
230 | let op = op.map_err(to_parse_error)?; | 246 | let op = op.as_ref().map_err(|e| to_parse_error(&e))?; |
231 | 247 | ||
232 | match op { | 248 | match op { |
233 | Op::TokenTree(tt::TokenTree::Subtree(subtree)) => validate(subtree)?, | 249 | Op::Subtree(subtree) => validate(&subtree)?, |
234 | Op::Repeat { subtree, separator, .. } => { | 250 | Op::Repeat { subtree, separator, .. } => { |
235 | // Checks that no repetition which could match an empty token | 251 | // Checks that no repetition which could match an empty token |
236 | // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 | 252 | // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 |
237 | 253 | ||
238 | if separator.is_none() { | 254 | if separator.is_none() { |
239 | if parse_pattern(subtree).all(|child_op| { | 255 | if subtree.iter().all(|child_op| { |
240 | match child_op.map_err(to_parse_error) { | 256 | match child_op.as_ref().map_err(to_parse_error) { |
241 | Ok(Op::Var { kind, .. }) => { | 257 | Ok(Op::Var { kind, .. }) => { |
242 | // vis is optional | 258 | // vis is optional |
243 | if kind.map_or(false, |it| it == "vis") { | 259 | if kind.as_ref().map_or(false, |it| it == "vis") { |
244 | return true; | 260 | return true; |
245 | } | 261 | } |
246 | } | 262 | } |
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs index 7aeef7be5..ab5f87c48 100644 --- a/crates/mbe/src/mbe_expander/matcher.rs +++ b/crates/mbe/src/mbe_expander/matcher.rs | |||
@@ -2,10 +2,10 @@ | |||
2 | 2 | ||
3 | use crate::{ | 3 | use crate::{ |
4 | mbe_expander::{Binding, Bindings, Fragment}, | 4 | mbe_expander::{Binding, Bindings, Fragment}, |
5 | parser::{parse_pattern, Op, RepeatKind, Separator}, | 5 | parser::{Op, RepeatKind, Separator}, |
6 | subtree_source::SubtreeTokenSource, | 6 | subtree_source::SubtreeTokenSource, |
7 | tt_iter::TtIter, | 7 | tt_iter::TtIter, |
8 | ExpandError, | 8 | ExpandError, MetaTemplate, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use super::ExpandResult; | 11 | use super::ExpandResult; |
@@ -83,7 +83,7 @@ impl Match { | |||
83 | // sense to try using it. Matching errors are added to the `Match`. It might | 83 | // sense to try using it. Matching errors are added to the `Match`. It might |
84 | // make sense to make pattern parsing a separate step? | 84 | // make sense to make pattern parsing a separate step? |
85 | 85 | ||
86 | pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match, ExpandError> { | 86 | pub(super) fn match_(pattern: &MetaTemplate, src: &tt::Subtree) -> Result<Match, ExpandError> { |
87 | assert!(pattern.delimiter == None); | 87 | assert!(pattern.delimiter == None); |
88 | 88 | ||
89 | let mut res = Match::default(); | 89 | let mut res = Match::default(); |
@@ -101,12 +101,12 @@ pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match, | |||
101 | 101 | ||
102 | fn match_subtree( | 102 | fn match_subtree( |
103 | res: &mut Match, | 103 | res: &mut Match, |
104 | pattern: &tt::Subtree, | 104 | pattern: &MetaTemplate, |
105 | src: &mut TtIter, | 105 | src: &mut TtIter, |
106 | ) -> Result<(), ExpandError> { | 106 | ) -> Result<(), ExpandError> { |
107 | for op in parse_pattern(pattern) { | 107 | for op in pattern.iter() { |
108 | match op? { | 108 | match op.as_ref().map_err(|err| err.clone())? { |
109 | Op::TokenTree(tt::TokenTree::Leaf(lhs)) => { | 109 | Op::Leaf(lhs) => { |
110 | let rhs = match src.expect_leaf() { | 110 | let rhs = match src.expect_leaf() { |
111 | Ok(l) => l, | 111 | Ok(l) => l, |
112 | Err(()) => { | 112 | Err(()) => { |
@@ -132,7 +132,7 @@ fn match_subtree( | |||
132 | } | 132 | } |
133 | } | 133 | } |
134 | } | 134 | } |
135 | Op::TokenTree(tt::TokenTree::Subtree(lhs)) => { | 135 | Op::Subtree(lhs) => { |
136 | let rhs = match src.expect_subtree() { | 136 | let rhs = match src.expect_subtree() { |
137 | Ok(s) => s, | 137 | Ok(s) => s, |
138 | Err(()) => { | 138 | Err(()) => { |
@@ -172,7 +172,7 @@ fn match_subtree( | |||
172 | } | 172 | } |
173 | } | 173 | } |
174 | Op::Repeat { subtree, kind, separator } => { | 174 | Op::Repeat { subtree, kind, separator } => { |
175 | match_repeat(res, subtree, kind, separator, src)?; | 175 | match_repeat(res, subtree, *kind, separator, src)?; |
176 | } | 176 | } |
177 | } | 177 | } |
178 | } | 178 | } |
@@ -240,26 +240,26 @@ impl<'a> TtIter<'a> { | |||
240 | let tt3 = self.next().unwrap().clone(); | 240 | let tt3 = self.next().unwrap().clone(); |
241 | Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2, tt3] }.into()) | 241 | Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2, tt3] }.into()) |
242 | } | 242 | } |
243 | ('-', '=', None) | 243 | ('-', '=', _) |
244 | | ('-', '>', None) | 244 | | ('-', '>', _) |
245 | | (':', ':', None) | 245 | | (':', ':', _) |
246 | | ('!', '=', None) | 246 | | ('!', '=', _) |
247 | | ('.', '.', None) | 247 | | ('.', '.', _) |
248 | | ('*', '=', None) | 248 | | ('*', '=', _) |
249 | | ('/', '=', None) | 249 | | ('/', '=', _) |
250 | | ('&', '&', None) | 250 | | ('&', '&', _) |
251 | | ('&', '=', None) | 251 | | ('&', '=', _) |
252 | | ('%', '=', None) | 252 | | ('%', '=', _) |
253 | | ('^', '=', None) | 253 | | ('^', '=', _) |
254 | | ('+', '=', None) | 254 | | ('+', '=', _) |
255 | | ('<', '<', None) | 255 | | ('<', '<', _) |
256 | | ('<', '=', None) | 256 | | ('<', '=', _) |
257 | | ('=', '=', None) | 257 | | ('=', '=', _) |
258 | | ('=', '>', None) | 258 | | ('=', '>', _) |
259 | | ('>', '=', None) | 259 | | ('>', '=', _) |
260 | | ('>', '>', None) | 260 | | ('>', '>', _) |
261 | | ('|', '=', None) | 261 | | ('|', '=', _) |
262 | | ('|', '|', None) => { | 262 | | ('|', '|', _) => { |
263 | let tt2 = self.next().unwrap().clone(); | 263 | let tt2 = self.next().unwrap().clone(); |
264 | Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2] }.into()) | 264 | Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2] }.into()) |
265 | } | 265 | } |
@@ -372,9 +372,9 @@ impl<'a> TtIter<'a> { | |||
372 | 372 | ||
373 | pub(super) fn match_repeat( | 373 | pub(super) fn match_repeat( |
374 | res: &mut Match, | 374 | res: &mut Match, |
375 | pattern: &tt::Subtree, | 375 | pattern: &MetaTemplate, |
376 | kind: RepeatKind, | 376 | kind: RepeatKind, |
377 | separator: Option<Separator>, | 377 | separator: &Option<Separator>, |
378 | src: &mut TtIter, | 378 | src: &mut TtIter, |
379 | ) -> Result<(), ExpandError> { | 379 | ) -> Result<(), ExpandError> { |
380 | // Dirty hack to make macro-expansion terminate. | 380 | // Dirty hack to make macro-expansion terminate. |
@@ -489,12 +489,12 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen | |||
489 | result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) }) | 489 | result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) }) |
490 | } | 490 | } |
491 | 491 | ||
492 | fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &tt::Subtree) -> Result<(), ExpandError> { | 492 | fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) -> Result<(), ExpandError> { |
493 | for op in parse_pattern(pattern) { | 493 | for op in pattern.iter() { |
494 | match op? { | 494 | match op.as_ref().map_err(|e| e.clone())? { |
495 | Op::Var { name, .. } => buf.push(name.clone()), | 495 | Op::Var { name, .. } => buf.push(name.clone()), |
496 | Op::TokenTree(tt::TokenTree::Leaf(_)) => (), | 496 | Op::Leaf(_) => (), |
497 | Op::TokenTree(tt::TokenTree::Subtree(subtree)) => collect_vars(buf, subtree)?, | 497 | Op::Subtree(subtree) => collect_vars(buf, subtree)?, |
498 | Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?, | 498 | Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?, |
499 | } | 499 | } |
500 | } | 500 | } |
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs index 57592dc92..720531237 100644 --- a/crates/mbe/src/mbe_expander/transcriber.rs +++ b/crates/mbe/src/mbe_expander/transcriber.rs | |||
@@ -6,8 +6,8 @@ use syntax::SmolStr; | |||
6 | use super::ExpandResult; | 6 | use super::ExpandResult; |
7 | use crate::{ | 7 | use crate::{ |
8 | mbe_expander::{Binding, Bindings, Fragment}, | 8 | mbe_expander::{Binding, Bindings, Fragment}, |
9 | parser::{parse_template, Op, RepeatKind, Separator}, | 9 | parser::{Op, RepeatKind, Separator}, |
10 | ExpandError, | 10 | ExpandError, MetaTemplate, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | impl Bindings { | 13 | impl Bindings { |
@@ -50,7 +50,10 @@ impl Bindings { | |||
50 | } | 50 | } |
51 | } | 51 | } |
52 | 52 | ||
53 | pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> { | 53 | pub(super) fn transcribe( |
54 | template: &MetaTemplate, | ||
55 | bindings: &Bindings, | ||
56 | ) -> ExpandResult<tt::Subtree> { | ||
54 | assert!(template.delimiter == None); | 57 | assert!(template.delimiter == None); |
55 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; | 58 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; |
56 | let mut arena: Vec<tt::TokenTree> = Vec::new(); | 59 | let mut arena: Vec<tt::TokenTree> = Vec::new(); |
@@ -76,35 +79,35 @@ struct ExpandCtx<'a> { | |||
76 | 79 | ||
77 | fn expand_subtree( | 80 | fn expand_subtree( |
78 | ctx: &mut ExpandCtx, | 81 | ctx: &mut ExpandCtx, |
79 | template: &tt::Subtree, | 82 | template: &MetaTemplate, |
80 | arena: &mut Vec<tt::TokenTree>, | 83 | arena: &mut Vec<tt::TokenTree>, |
81 | ) -> ExpandResult<tt::Subtree> { | 84 | ) -> ExpandResult<tt::Subtree> { |
82 | // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation | 85 | // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation |
83 | let start_elements = arena.len(); | 86 | let start_elements = arena.len(); |
84 | let mut err = None; | 87 | let mut err = None; |
85 | for op in parse_template(template) { | 88 | for op in template.iter() { |
86 | let op = match op { | 89 | let op = match op { |
87 | Ok(op) => op, | 90 | Ok(op) => op, |
88 | Err(e) => { | 91 | Err(e) => { |
89 | err = Some(e); | 92 | err = Some(e.clone()); |
90 | break; | 93 | break; |
91 | } | 94 | } |
92 | }; | 95 | }; |
93 | match op { | 96 | match op { |
94 | Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), | 97 | Op::Leaf(tt) => arena.push(tt.clone().into()), |
95 | Op::TokenTree(tt::TokenTree::Subtree(tt)) => { | 98 | Op::Subtree(tt) => { |
96 | let ExpandResult { value: tt, err: e } = expand_subtree(ctx, tt, arena); | 99 | let ExpandResult { value: tt, err: e } = expand_subtree(ctx, &tt, arena); |
97 | err = err.or(e); | 100 | err = err.or(e); |
98 | arena.push(tt.into()); | 101 | arena.push(tt.into()); |
99 | } | 102 | } |
100 | Op::Var { name, .. } => { | 103 | Op::Var { name, .. } => { |
101 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, name); | 104 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name); |
102 | err = err.or(e); | 105 | err = err.or(e); |
103 | push_fragment(arena, fragment); | 106 | push_fragment(arena, fragment); |
104 | } | 107 | } |
105 | Op::Repeat { subtree, kind, separator } => { | 108 | Op::Repeat { subtree, kind, separator } => { |
106 | let ExpandResult { value: fragment, err: e } = | 109 | let ExpandResult { value: fragment, err: e } = |
107 | expand_repeat(ctx, subtree, kind, separator, arena); | 110 | expand_repeat(ctx, subtree, *kind, separator, arena); |
108 | err = err.or(e); | 111 | err = err.or(e); |
109 | push_fragment(arena, fragment) | 112 | push_fragment(arena, fragment) |
110 | } | 113 | } |
@@ -161,9 +164,9 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | |||
161 | 164 | ||
162 | fn expand_repeat( | 165 | fn expand_repeat( |
163 | ctx: &mut ExpandCtx, | 166 | ctx: &mut ExpandCtx, |
164 | template: &tt::Subtree, | 167 | template: &MetaTemplate, |
165 | kind: RepeatKind, | 168 | kind: RepeatKind, |
166 | separator: Option<Separator>, | 169 | separator: &Option<Separator>, |
167 | arena: &mut Vec<tt::TokenTree>, | 170 | arena: &mut Vec<tt::TokenTree>, |
168 | ) -> ExpandResult<Fragment> { | 171 | ) -> ExpandResult<Fragment> { |
169 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | 172 | let mut buf: Vec<tt::TokenTree> = Vec::new(); |
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index c3fdd4040..2f3ebc831 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs | |||
@@ -4,16 +4,17 @@ | |||
4 | use smallvec::SmallVec; | 4 | use smallvec::SmallVec; |
5 | use syntax::SmolStr; | 5 | use syntax::SmolStr; |
6 | 6 | ||
7 | use crate::{tt_iter::TtIter, ExpandError}; | 7 | use crate::{tt_iter::TtIter, ExpandError, MetaTemplate}; |
8 | 8 | ||
9 | #[derive(Debug)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
10 | pub(crate) enum Op<'a> { | 10 | pub(crate) enum Op { |
11 | Var { name: &'a SmolStr, kind: Option<&'a SmolStr> }, | 11 | Var { name: SmolStr, kind: Option<SmolStr> }, |
12 | Repeat { subtree: &'a tt::Subtree, kind: RepeatKind, separator: Option<Separator> }, | 12 | Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> }, |
13 | TokenTree(&'a tt::TokenTree), | 13 | Leaf(tt::Leaf), |
14 | Subtree(MetaTemplate), | ||
14 | } | 15 | } |
15 | 16 | ||
16 | #[derive(Clone, Debug, PartialEq, Eq)] | 17 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
17 | pub(crate) enum RepeatKind { | 18 | pub(crate) enum RepeatKind { |
18 | ZeroOrMore, | 19 | ZeroOrMore, |
19 | OneOrMore, | 20 | OneOrMore, |
@@ -45,16 +46,12 @@ impl PartialEq for Separator { | |||
45 | } | 46 | } |
46 | } | 47 | } |
47 | 48 | ||
48 | pub(crate) fn parse_template( | 49 | pub(crate) fn parse_template(template: &tt::Subtree) -> Vec<Result<Op, ExpandError>> { |
49 | template: &tt::Subtree, | 50 | parse_inner(&template, Mode::Template) |
50 | ) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { | ||
51 | parse_inner(template, Mode::Template) | ||
52 | } | 51 | } |
53 | 52 | ||
54 | pub(crate) fn parse_pattern( | 53 | pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Vec<Result<Op, ExpandError>> { |
55 | pattern: &tt::Subtree, | 54 | parse_inner(&pattern, Mode::Pattern) |
56 | ) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { | ||
57 | parse_inner(pattern, Mode::Pattern) | ||
58 | } | 55 | } |
59 | 56 | ||
60 | #[derive(Clone, Copy)] | 57 | #[derive(Clone, Copy)] |
@@ -63,12 +60,13 @@ enum Mode { | |||
63 | Template, | 60 | Template, |
64 | } | 61 | } |
65 | 62 | ||
66 | fn parse_inner(src: &tt::Subtree, mode: Mode) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { | 63 | fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ExpandError>> { |
67 | let mut src = TtIter::new(src); | 64 | let mut src = TtIter::new(&tt); |
68 | std::iter::from_fn(move || { | 65 | std::iter::from_fn(move || { |
69 | let first = src.next()?; | 66 | let first = src.next()?; |
70 | Some(next_op(first, &mut src, mode)) | 67 | Some(next_op(first, &mut src, mode)) |
71 | }) | 68 | }) |
69 | .collect() | ||
72 | } | 70 | } |
73 | 71 | ||
74 | macro_rules! err { | 72 | macro_rules! err { |
@@ -83,35 +81,41 @@ macro_rules! bail { | |||
83 | }; | 81 | }; |
84 | } | 82 | } |
85 | 83 | ||
86 | fn next_op<'a>( | 84 | fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Result<Op, ExpandError> { |
87 | first: &'a tt::TokenTree, | ||
88 | src: &mut TtIter<'a>, | ||
89 | mode: Mode, | ||
90 | ) -> Result<Op<'a>, ExpandError> { | ||
91 | let res = match first { | 85 | let res = match first { |
92 | tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { | 86 | tt::TokenTree::Leaf(leaf @ tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { |
93 | // Note that the '$' itself is a valid token inside macro_rules. | 87 | // Note that the '$' itself is a valid token inside macro_rules. |
94 | let second = match src.next() { | 88 | let second = match src.next() { |
95 | None => return Ok(Op::TokenTree(first)), | 89 | None => return Ok(Op::Leaf(leaf.clone())), |
96 | Some(it) => it, | 90 | Some(it) => it, |
97 | }; | 91 | }; |
98 | match second { | 92 | match second { |
99 | tt::TokenTree::Subtree(subtree) => { | 93 | tt::TokenTree::Subtree(subtree) => { |
100 | let (separator, kind) = parse_repeat(src)?; | 94 | let (separator, kind) = parse_repeat(src)?; |
95 | let delimiter = subtree.delimiter; | ||
96 | let tokens = parse_inner(&subtree, mode); | ||
97 | let subtree = MetaTemplate { tokens, delimiter }; | ||
101 | Op::Repeat { subtree, separator, kind } | 98 | Op::Repeat { subtree, separator, kind } |
102 | } | 99 | } |
103 | tt::TokenTree::Leaf(leaf) => match leaf { | 100 | tt::TokenTree::Leaf(leaf) => match leaf { |
104 | tt::Leaf::Punct(_) => { | 101 | tt::Leaf::Punct(punct) => { |
105 | return Err(ExpandError::UnexpectedToken); | 102 | static UNDERSCORE: SmolStr = SmolStr::new_inline("_"); |
103 | |||
104 | if punct.char != '_' { | ||
105 | return Err(ExpandError::UnexpectedToken); | ||
106 | } | ||
107 | let name = UNDERSCORE.clone(); | ||
108 | let kind = eat_fragment_kind(src, mode)?; | ||
109 | Op::Var { name, kind } | ||
106 | } | 110 | } |
107 | tt::Leaf::Ident(ident) => { | 111 | tt::Leaf::Ident(ident) => { |
108 | let name = &ident.text; | 112 | let name = ident.text.clone(); |
109 | let kind = eat_fragment_kind(src, mode)?; | 113 | let kind = eat_fragment_kind(src, mode)?; |
110 | Op::Var { name, kind } | 114 | Op::Var { name, kind } |
111 | } | 115 | } |
112 | tt::Leaf::Literal(lit) => { | 116 | tt::Leaf::Literal(lit) => { |
113 | if is_boolean_literal(lit) { | 117 | if is_boolean_literal(&lit) { |
114 | let name = &lit.text; | 118 | let name = lit.text.clone(); |
115 | let kind = eat_fragment_kind(src, mode)?; | 119 | let kind = eat_fragment_kind(src, mode)?; |
116 | Op::Var { name, kind } | 120 | Op::Var { name, kind } |
117 | } else { | 121 | } else { |
@@ -121,19 +125,22 @@ fn next_op<'a>( | |||
121 | }, | 125 | }, |
122 | } | 126 | } |
123 | } | 127 | } |
124 | tt => Op::TokenTree(tt), | 128 | tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()), |
129 | tt::TokenTree::Subtree(subtree) => { | ||
130 | let delimiter = subtree.delimiter; | ||
131 | let tokens = parse_inner(&subtree, mode); | ||
132 | let subtree = MetaTemplate { tokens, delimiter }; | ||
133 | Op::Subtree(subtree) | ||
134 | } | ||
125 | }; | 135 | }; |
126 | Ok(res) | 136 | Ok(res) |
127 | } | 137 | } |
128 | 138 | ||
129 | fn eat_fragment_kind<'a>( | 139 | fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ExpandError> { |
130 | src: &mut TtIter<'a>, | ||
131 | mode: Mode, | ||
132 | ) -> Result<Option<&'a SmolStr>, ExpandError> { | ||
133 | if let Mode::Pattern = mode { | 140 | if let Mode::Pattern = mode { |
134 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; | 141 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; |
135 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; | 142 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; |
136 | return Ok(Some(&ident.text)); | 143 | return Ok(Some(ident.text.clone())); |
137 | }; | 144 | }; |
138 | Ok(None) | 145 | Ok(None) |
139 | } | 146 | } |
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 2bec7fd49..265c0d63d 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -313,7 +313,7 @@ trait TokenConvertor { | |||
313 | return; | 313 | return; |
314 | } | 314 | } |
315 | 315 | ||
316 | result.push(if k.is_punct() && k != UNDERSCORE { | 316 | result.push(if k.is_punct() { |
317 | assert_eq!(range.len(), TextSize::of('.')); | 317 | assert_eq!(range.len(), TextSize::of('.')); |
318 | let delim = match k { | 318 | let delim = match k { |
319 | T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), | 319 | T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), |
@@ -378,7 +378,6 @@ trait TokenConvertor { | |||
378 | let leaf: tt::Leaf = match k { | 378 | let leaf: tt::Leaf = match k { |
379 | T![true] | T![false] => make_leaf!(Ident), | 379 | T![true] | T![false] => make_leaf!(Ident), |
380 | IDENT => make_leaf!(Ident), | 380 | IDENT => make_leaf!(Ident), |
381 | UNDERSCORE => make_leaf!(Ident), | ||
382 | k if k.is_keyword() => make_leaf!(Ident), | 381 | k if k.is_keyword() => make_leaf!(Ident), |
383 | k if k.is_literal() => make_leaf!(Literal), | 382 | k if k.is_literal() => make_leaf!(Literal), |
384 | LIFETIME_IDENT => { | 383 | LIFETIME_IDENT => { |
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 451fa1456..1d9afb4fb 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -761,6 +761,18 @@ fn test_last_expr() { | |||
761 | } | 761 | } |
762 | 762 | ||
763 | #[test] | 763 | #[test] |
764 | fn test_expr_with_attr() { | ||
765 | parse_macro( | ||
766 | r#" | ||
767 | macro_rules! m { | ||
768 | ($a:expr) => {0} | ||
769 | } | ||
770 | "#, | ||
771 | ) | ||
772 | .assert_expand_items("m!(#[allow(a)]())", "0"); | ||
773 | } | ||
774 | |||
775 | #[test] | ||
764 | fn test_ty() { | 776 | fn test_ty() { |
765 | parse_macro( | 777 | parse_macro( |
766 | r#" | 778 | r#" |
@@ -992,6 +1004,22 @@ fn test_tt_composite2() { | |||
992 | } | 1004 | } |
993 | 1005 | ||
994 | #[test] | 1006 | #[test] |
1007 | fn test_tt_with_composite_without_space() { | ||
1008 | parse_macro( | ||
1009 | r#" | ||
1010 | macro_rules! foo { | ||
1011 | ($ op:tt, $j:path) => ( | ||
1012 | 0 | ||
1013 | ) | ||
1014 | } | ||
1015 | "#, | ||
1016 | ) | ||
1017 | // Test macro input without any spaces | ||
1018 | // See https://github.com/rust-analyzer/rust-analyzer/issues/6692 | ||
1019 | .assert_expand_items("foo!(==,Foo::Bool)", "0"); | ||
1020 | } | ||
1021 | |||
1022 | #[test] | ||
995 | fn test_underscore() { | 1023 | fn test_underscore() { |
996 | parse_macro( | 1024 | parse_macro( |
997 | r#" | 1025 | r#" |
@@ -1004,6 +1032,42 @@ fn test_underscore() { | |||
1004 | } | 1032 | } |
1005 | 1033 | ||
1006 | #[test] | 1034 | #[test] |
1035 | fn test_underscore_not_greedily() { | ||
1036 | parse_macro( | ||
1037 | r#" | ||
1038 | macro_rules! q { | ||
1039 | ($($a:ident)* _) => {0}; | ||
1040 | } | ||
1041 | "#, | ||
1042 | ) | ||
1043 | // `_` overlaps with `$a:ident` but rustc matches it under the `_` token | ||
1044 | .assert_expand_items(r#"q![a b c d _]"#, r#"0"#); | ||
1045 | |||
1046 | parse_macro( | ||
1047 | r#" | ||
1048 | macro_rules! q { | ||
1049 | ($($a:expr => $b:ident)* _ => $c:expr) => {0}; | ||
1050 | } | ||
1051 | "#, | ||
1052 | ) | ||
1053 | // `_ => ou` overlaps with `$a:expr => $b:ident` but rustc matches it under `_ => $c:expr` | ||
1054 | .assert_expand_items(r#"q![a => b c => d _ => ou]"#, r#"0"#); | ||
1055 | } | ||
1056 | |||
1057 | #[test] | ||
1058 | fn test_underscore_as_type() { | ||
1059 | parse_macro( | ||
1060 | r#" | ||
1061 | macro_rules! q { | ||
1062 | ($a:ty) => {0}; | ||
1063 | } | ||
1064 | "#, | ||
1065 | ) | ||
1066 | // Underscore is a type | ||
1067 | .assert_expand_items(r#"q![_]"#, r#"0"#); | ||
1068 | } | ||
1069 | |||
1070 | #[test] | ||
1007 | fn test_vertical_bar_with_pat() { | 1071 | fn test_vertical_bar_with_pat() { |
1008 | parse_macro( | 1072 | parse_macro( |
1009 | r#" | 1073 | r#" |
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index f08c8bab7..63cc90027 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -59,7 +59,7 @@ pub(crate) mod fragments { | |||
59 | }; | 59 | }; |
60 | 60 | ||
61 | pub(crate) fn expr(p: &mut Parser) { | 61 | pub(crate) fn expr(p: &mut Parser) { |
62 | let _ = expressions::expr(p); | 62 | let _ = expressions::expr_with_attrs(p); |
63 | } | 63 | } |
64 | 64 | ||
65 | pub(crate) fn stmt(p: &mut Parser) { | 65 | pub(crate) fn stmt(p: &mut Parser) { |
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index e897d5a52..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() { |
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/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/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_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs index 0d061fd53..2ea456fb0 100644 --- a/crates/proc_macro_api/src/lib.rs +++ b/crates/proc_macro_api/src/lib.rs | |||
@@ -16,7 +16,7 @@ use std::{ | |||
16 | sync::Arc, | 16 | sync::Arc, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | use base_db::ProcMacro; | 19 | use base_db::{Env, ProcMacro}; |
20 | use tt::{SmolStr, Subtree}; | 20 | use tt::{SmolStr, Subtree}; |
21 | 21 | ||
22 | use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; | 22 | use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; |
@@ -39,17 +39,19 @@ impl PartialEq for ProcMacroProcessExpander { | |||
39 | } | 39 | } |
40 | } | 40 | } |
41 | 41 | ||
42 | impl tt::TokenExpander for ProcMacroProcessExpander { | 42 | impl base_db::ProcMacroExpander for ProcMacroProcessExpander { |
43 | fn expand( | 43 | fn expand( |
44 | &self, | 44 | &self, |
45 | subtree: &Subtree, | 45 | subtree: &Subtree, |
46 | attr: Option<&Subtree>, | 46 | attr: Option<&Subtree>, |
47 | env: &Env, | ||
47 | ) -> Result<Subtree, tt::ExpansionError> { | 48 | ) -> Result<Subtree, tt::ExpansionError> { |
48 | let task = ExpansionTask { | 49 | let task = ExpansionTask { |
49 | macro_body: subtree.clone(), | 50 | macro_body: subtree.clone(), |
50 | macro_name: self.name.to_string(), | 51 | macro_name: self.name.to_string(), |
51 | attributes: attr.cloned(), | 52 | attributes: attr.cloned(), |
52 | lib: self.dylib_path.to_path_buf(), | 53 | lib: self.dylib_path.to_path_buf(), |
54 | env: env.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(), | ||
53 | }; | 55 | }; |
54 | 56 | ||
55 | let result: ExpansionResult = self.process.send_task(msg::Request::ExpansionMacro(task))?; | 57 | let result: ExpansionResult = self.process.send_task(msg::Request::ExpansionMacro(task))?; |
@@ -90,7 +92,7 @@ impl ProcMacroClient { | |||
90 | ProcMacroKind::FuncLike => base_db::ProcMacroKind::FuncLike, | 92 | ProcMacroKind::FuncLike => base_db::ProcMacroKind::FuncLike, |
91 | ProcMacroKind::Attr => base_db::ProcMacroKind::Attr, | 93 | ProcMacroKind::Attr => base_db::ProcMacroKind::Attr, |
92 | }; | 94 | }; |
93 | let expander: Arc<dyn tt::TokenExpander> = Arc::new(ProcMacroProcessExpander { | 95 | let expander = Arc::new(ProcMacroProcessExpander { |
94 | process: self.process.clone(), | 96 | process: self.process.clone(), |
95 | name: name.clone(), | 97 | name: name.clone(), |
96 | dylib_path: dylib_path.into(), | 98 | dylib_path: dylib_path.into(), |
diff --git a/crates/proc_macro_api/src/rpc.rs b/crates/proc_macro_api/src/rpc.rs index b85f92eea..cf830b59f 100644 --- a/crates/proc_macro_api/src/rpc.rs +++ b/crates/proc_macro_api/src/rpc.rs | |||
@@ -51,6 +51,9 @@ pub struct ExpansionTask { | |||
51 | pub attributes: Option<Subtree>, | 51 | pub attributes: Option<Subtree>, |
52 | 52 | ||
53 | pub lib: PathBuf, | 53 | pub lib: PathBuf, |
54 | |||
55 | /// Environment variables to set during macro expansion. | ||
56 | pub env: Vec<(String, String)>, | ||
54 | } | 57 | } |
55 | 58 | ||
56 | #[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] | 59 | #[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] |
@@ -251,6 +254,7 @@ mod tests { | |||
251 | macro_name: Default::default(), | 254 | macro_name: Default::default(), |
252 | attributes: None, | 255 | attributes: None, |
253 | lib: Default::default(), | 256 | lib: Default::default(), |
257 | env: Default::default(), | ||
254 | }; | 258 | }; |
255 | 259 | ||
256 | let json = serde_json::to_string(&task).unwrap(); | 260 | let json = serde_json::to_string(&task).unwrap(); |
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/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs index 2afb973cc..4e719f3aa 100644 --- a/crates/proc_macro_srv/src/dylib.rs +++ b/crates/proc_macro_srv/src/dylib.rs | |||
@@ -136,6 +136,7 @@ impl Expander { | |||
136 | &crate::proc_macro::bridge::server::SameThread, | 136 | &crate::proc_macro::bridge::server::SameThread, |
137 | crate::rustc_server::Rustc::default(), | 137 | crate::rustc_server::Rustc::default(), |
138 | parsed_body, | 138 | parsed_body, |
139 | false, | ||
139 | ); | 140 | ); |
140 | return res.map(|it| it.subtree); | 141 | return res.map(|it| it.subtree); |
141 | } | 142 | } |
@@ -144,6 +145,7 @@ impl Expander { | |||
144 | &crate::proc_macro::bridge::server::SameThread, | 145 | &crate::proc_macro::bridge::server::SameThread, |
145 | crate::rustc_server::Rustc::default(), | 146 | crate::rustc_server::Rustc::default(), |
146 | parsed_body, | 147 | parsed_body, |
148 | false, | ||
147 | ); | 149 | ); |
148 | return res.map(|it| it.subtree); | 150 | return res.map(|it| it.subtree); |
149 | } | 151 | } |
@@ -153,6 +155,7 @@ impl Expander { | |||
153 | crate::rustc_server::Rustc::default(), | 155 | crate::rustc_server::Rustc::default(), |
154 | parsed_attributes, | 156 | parsed_attributes, |
155 | parsed_body, | 157 | parsed_body, |
158 | false, | ||
156 | ); | 159 | ); |
157 | return res.map(|it| it.subtree); | 160 | return res.map(|it| it.subtree); |
158 | } | 161 | } |
diff --git a/crates/proc_macro_srv/src/lib.rs b/crates/proc_macro_srv/src/lib.rs index 9cca96994..d4f04ee06 100644 --- a/crates/proc_macro_srv/src/lib.rs +++ b/crates/proc_macro_srv/src/lib.rs | |||
@@ -24,7 +24,7 @@ use proc_macro::bridge::client::TokenStream; | |||
24 | use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; | 24 | use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; |
25 | use std::{ | 25 | use std::{ |
26 | collections::{hash_map::Entry, HashMap}, | 26 | collections::{hash_map::Entry, HashMap}, |
27 | fs, | 27 | env, fs, |
28 | path::{Path, PathBuf}, | 28 | path::{Path, PathBuf}, |
29 | time::SystemTime, | 29 | time::SystemTime, |
30 | }; | 30 | }; |
@@ -37,7 +37,23 @@ pub(crate) struct ProcMacroSrv { | |||
37 | impl ProcMacroSrv { | 37 | impl ProcMacroSrv { |
38 | pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> { | 38 | pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> { |
39 | let expander = self.expander(&task.lib)?; | 39 | let expander = self.expander(&task.lib)?; |
40 | match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) { | 40 | |
41 | let mut prev_env = HashMap::new(); | ||
42 | for (k, v) in &task.env { | ||
43 | prev_env.insert(k.as_str(), env::var_os(k)); | ||
44 | env::set_var(k, v); | ||
45 | } | ||
46 | |||
47 | let result = expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()); | ||
48 | |||
49 | for (k, _) in &task.env { | ||
50 | match &prev_env[k.as_str()] { | ||
51 | Some(v) => env::set_var(k, v), | ||
52 | None => env::remove_var(k), | ||
53 | } | ||
54 | } | ||
55 | |||
56 | match result { | ||
41 | Ok(expansion) => Ok(ExpansionResult { expansion }), | 57 | Ok(expansion) => Ok(ExpansionResult { expansion }), |
42 | Err(msg) => { | 58 | Err(msg) => { |
43 | let msg = msg.as_str().unwrap_or("<unknown error>"); | 59 | let msg = msg.as_str().unwrap_or("<unknown error>"); |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs index 55d6330cc..ca6749b9b 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs | |||
@@ -303,17 +303,21 @@ impl BridgeState<'_> { | |||
303 | 303 | ||
304 | impl Bridge<'_> { | 304 | impl Bridge<'_> { |
305 | fn enter<R>(self, f: impl FnOnce() -> R) -> R { | 305 | fn enter<R>(self, f: impl FnOnce() -> R) -> R { |
306 | let force_show_panics = self.force_show_panics; | ||
307 | |||
306 | // Hide the default panic output within `proc_macro` expansions. | 308 | // Hide the default panic output within `proc_macro` expansions. |
307 | // NB. the server can't do this because it may use a different libstd. | 309 | // NB. the server can't do this because it may use a different libstd. |
308 | static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); | 310 | static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); |
309 | HIDE_PANICS_DURING_EXPANSION.call_once(|| { | 311 | HIDE_PANICS_DURING_EXPANSION.call_once(|| { |
310 | let prev = panic::take_hook(); | 312 | let prev = panic::take_hook(); |
311 | panic::set_hook(Box::new(move |info| { | 313 | panic::set_hook(Box::new(move |info| { |
312 | let hide = BridgeState::with(|state| match state { | 314 | let show = BridgeState::with(|state| match state { |
313 | BridgeState::NotConnected => false, | 315 | BridgeState::NotConnected => true, |
314 | BridgeState::Connected(_) | BridgeState::InUse => true, | 316 | // Something weird is going on, so don't suppress any backtraces |
317 | BridgeState::InUse => true, | ||
318 | BridgeState::Connected(bridge) => force_show_panics, | ||
315 | }); | 319 | }); |
316 | if !hide { | 320 | if show { |
317 | prev(info) | 321 | prev(info) |
318 | } | 322 | } |
319 | })); | 323 | })); |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs index b97886eb9..e4006a5ab 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs | |||
@@ -225,6 +225,9 @@ pub struct Bridge<'a> { | |||
225 | 225 | ||
226 | /// Server-side function that the client uses to make requests. | 226 | /// Server-side function that the client uses to make requests. |
227 | dispatch: closure::Closure<'a, Buffer<u8>, Buffer<u8>>, | 227 | dispatch: closure::Closure<'a, Buffer<u8>, Buffer<u8>>, |
228 | |||
229 | /// If 'true', always invoke the default panic hook | ||
230 | force_show_panics: bool, | ||
228 | } | 231 | } |
229 | 232 | ||
230 | // impl<'a> !Sync for Bridge<'a> {} | 233 | // impl<'a> !Sync for Bridge<'a> {} |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs index 3acb239af..88fbdc078 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs | |||
@@ -138,6 +138,7 @@ pub trait ExecutionStrategy { | |||
138 | input: Buffer<u8>, | 138 | input: Buffer<u8>, |
139 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | 139 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, |
140 | client_data: D, | 140 | client_data: D, |
141 | force_show_panics: bool, | ||
141 | ) -> Buffer<u8>; | 142 | ) -> Buffer<u8>; |
142 | } | 143 | } |
143 | 144 | ||
@@ -150,10 +151,14 @@ impl ExecutionStrategy for SameThread { | |||
150 | input: Buffer<u8>, | 151 | input: Buffer<u8>, |
151 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | 152 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, |
152 | client_data: D, | 153 | client_data: D, |
154 | force_show_panics: bool, | ||
153 | ) -> Buffer<u8> { | 155 | ) -> Buffer<u8> { |
154 | let mut dispatch = |b| dispatcher.dispatch(b); | 156 | let mut dispatch = |b| dispatcher.dispatch(b); |
155 | 157 | ||
156 | run_client(Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, client_data) | 158 | run_client( |
159 | Bridge { cached_buffer: input, dispatch: (&mut dispatch).into(), force_show_panics }, | ||
160 | client_data, | ||
161 | ) | ||
157 | } | 162 | } |
158 | } | 163 | } |
159 | 164 | ||
@@ -169,6 +174,7 @@ impl ExecutionStrategy for CrossThread1 { | |||
169 | input: Buffer<u8>, | 174 | input: Buffer<u8>, |
170 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | 175 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, |
171 | client_data: D, | 176 | client_data: D, |
177 | force_show_panics: bool, | ||
172 | ) -> Buffer<u8> { | 178 | ) -> Buffer<u8> { |
173 | use std::sync::mpsc::channel; | 179 | use std::sync::mpsc::channel; |
174 | 180 | ||
@@ -182,7 +188,11 @@ impl ExecutionStrategy for CrossThread1 { | |||
182 | }; | 188 | }; |
183 | 189 | ||
184 | run_client( | 190 | run_client( |
185 | Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, | 191 | Bridge { |
192 | cached_buffer: input, | ||
193 | dispatch: (&mut dispatch).into(), | ||
194 | force_show_panics, | ||
195 | }, | ||
186 | client_data, | 196 | client_data, |
187 | ) | 197 | ) |
188 | }); | 198 | }); |
@@ -204,6 +214,7 @@ impl ExecutionStrategy for CrossThread2 { | |||
204 | input: Buffer<u8>, | 214 | input: Buffer<u8>, |
205 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | 215 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, |
206 | client_data: D, | 216 | client_data: D, |
217 | force_show_panics: bool, | ||
207 | ) -> Buffer<u8> { | 218 | ) -> Buffer<u8> { |
208 | use std::sync::{Arc, Mutex}; | 219 | use std::sync::{Arc, Mutex}; |
209 | 220 | ||
@@ -229,7 +240,11 @@ impl ExecutionStrategy for CrossThread2 { | |||
229 | }; | 240 | }; |
230 | 241 | ||
231 | let r = run_client( | 242 | let r = run_client( |
232 | Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, | 243 | Bridge { |
244 | cached_buffer: input, | ||
245 | dispatch: (&mut dispatch).into(), | ||
246 | force_show_panics, | ||
247 | }, | ||
233 | client_data, | 248 | client_data, |
234 | ); | 249 | ); |
235 | 250 | ||
@@ -268,6 +283,7 @@ fn run_server< | |||
268 | input: I, | 283 | input: I, |
269 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | 284 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, |
270 | client_data: D, | 285 | client_data: D, |
286 | force_show_panics: bool, | ||
271 | ) -> Result<O, PanicMessage> { | 287 | ) -> Result<O, PanicMessage> { |
272 | let mut dispatcher = | 288 | let mut dispatcher = |
273 | Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; | 289 | Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; |
@@ -275,7 +291,13 @@ fn run_server< | |||
275 | let mut b = Buffer::new(); | 291 | let mut b = Buffer::new(); |
276 | input.encode(&mut b, &mut dispatcher.handle_store); | 292 | input.encode(&mut b, &mut dispatcher.handle_store); |
277 | 293 | ||
278 | b = strategy.run_bridge_and_client(&mut dispatcher, b, run_client, client_data); | 294 | b = strategy.run_bridge_and_client( |
295 | &mut dispatcher, | ||
296 | b, | ||
297 | run_client, | ||
298 | client_data, | ||
299 | force_show_panics, | ||
300 | ); | ||
279 | 301 | ||
280 | Result::decode(&mut &b[..], &mut dispatcher.handle_store) | 302 | Result::decode(&mut &b[..], &mut dispatcher.handle_store) |
281 | } | 303 | } |
@@ -286,6 +308,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> { | |||
286 | strategy: &impl ExecutionStrategy, | 308 | strategy: &impl ExecutionStrategy, |
287 | server: S, | 309 | server: S, |
288 | input: S::TokenStream, | 310 | input: S::TokenStream, |
311 | force_show_panics: bool, | ||
289 | ) -> Result<S::TokenStream, PanicMessage> { | 312 | ) -> Result<S::TokenStream, PanicMessage> { |
290 | let client::Client { get_handle_counters, run, f } = *self; | 313 | let client::Client { get_handle_counters, run, f } = *self; |
291 | run_server( | 314 | run_server( |
@@ -295,6 +318,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> { | |||
295 | <MarkedTypes<S> as Types>::TokenStream::mark(input), | 318 | <MarkedTypes<S> as Types>::TokenStream::mark(input), |
296 | run, | 319 | run, |
297 | f, | 320 | f, |
321 | force_show_panics, | ||
298 | ) | 322 | ) |
299 | .map(<MarkedTypes<S> as Types>::TokenStream::unmark) | 323 | .map(<MarkedTypes<S> as Types>::TokenStream::unmark) |
300 | } | 324 | } |
@@ -307,6 +331,7 @@ impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenSt | |||
307 | server: S, | 331 | server: S, |
308 | input: S::TokenStream, | 332 | input: S::TokenStream, |
309 | input2: S::TokenStream, | 333 | input2: S::TokenStream, |
334 | force_show_panics: bool, | ||
310 | ) -> Result<S::TokenStream, PanicMessage> { | 335 | ) -> Result<S::TokenStream, PanicMessage> { |
311 | let client::Client { get_handle_counters, run, f } = *self; | 336 | let client::Client { get_handle_counters, run, f } = *self; |
312 | run_server( | 337 | run_server( |
@@ -319,6 +344,7 @@ impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenSt | |||
319 | ), | 344 | ), |
320 | run, | 345 | run, |
321 | f, | 346 | f, |
347 | force_show_panics, | ||
322 | ) | 348 | ) |
323 | .map(<MarkedTypes<S> as Types>::TokenStream::unmark) | 349 | .map(<MarkedTypes<S> as Types>::TokenStream::unmark) |
324 | } | 350 | } |
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs index 503f4c101..b54aa1f3b 100644 --- a/crates/proc_macro_srv/src/rustc_server.rs +++ b/crates/proc_macro_srv/src/rustc_server.rs | |||
@@ -204,17 +204,18 @@ pub mod token_stream { | |||
204 | let content = subtree | 204 | let content = subtree |
205 | .token_trees | 205 | .token_trees |
206 | .iter() | 206 | .iter() |
207 | .map(|tkn| { | 207 | .fold((String::new(), true), |(last, last_to_joint), tkn| { |
208 | let s = to_text(tkn); | 208 | let s = [last, to_text(tkn)].join(if last_to_joint { "" } else { " " }); |
209 | let mut is_joint = false; | ||
209 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { | 210 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { |
210 | if punct.spacing == tt::Spacing::Alone { | 211 | if punct.spacing == tt::Spacing::Joint { |
211 | return s + " "; | 212 | is_joint = true; |
212 | } | 213 | } |
213 | } | 214 | } |
214 | s | 215 | (s, is_joint) |
215 | }) | 216 | }) |
216 | .collect::<Vec<_>>() | 217 | .0; |
217 | .concat(); | 218 | |
218 | let (open, close) = match subtree.delimiter.map(|it| it.kind) { | 219 | let (open, close) = match subtree.delimiter.map(|it| it.kind) { |
219 | None => ("", ""), | 220 | None => ("", ""), |
220 | Some(tt::DelimiterKind::Brace) => ("{", "}"), | 221 | Some(tt::DelimiterKind::Brace) => ("{", "}"), |
@@ -710,4 +711,32 @@ mod tests { | |||
710 | assert_eq!(srv.character('c').text, "'c'"); | 711 | assert_eq!(srv.character('c').text, "'c'"); |
711 | assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\""); | 712 | assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\""); |
712 | } | 713 | } |
714 | |||
715 | #[test] | ||
716 | fn test_rustc_server_to_string() { | ||
717 | let s = TokenStream { | ||
718 | subtree: tt::Subtree { | ||
719 | delimiter: None, | ||
720 | token_trees: vec![ | ||
721 | tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { | ||
722 | text: "struct".into(), | ||
723 | id: tt::TokenId::unspecified(), | ||
724 | })), | ||
725 | tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { | ||
726 | text: "T".into(), | ||
727 | id: tt::TokenId::unspecified(), | ||
728 | })), | ||
729 | tt::TokenTree::Subtree(tt::Subtree { | ||
730 | delimiter: Some(tt::Delimiter { | ||
731 | id: tt::TokenId::unspecified(), | ||
732 | kind: tt::DelimiterKind::Brace, | ||
733 | }), | ||
734 | token_trees: vec![], | ||
735 | }), | ||
736 | ], | ||
737 | }, | ||
738 | }; | ||
739 | |||
740 | assert_eq!(s.to_string(), "struct T {}"); | ||
741 | } | ||
713 | } | 742 | } |
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml index c55e85709..a65e42261 100644 --- a/crates/project_model/Cargo.toml +++ b/crates/project_model/Cargo.toml | |||
@@ -16,7 +16,7 @@ cargo_metadata = "=0.12.0" | |||
16 | serde = { version = "1.0.106", features = ["derive"] } | 16 | serde = { version = "1.0.106", features = ["derive"] } |
17 | serde_json = "1.0.48" | 17 | serde_json = "1.0.48" |
18 | anyhow = "1.0.26" | 18 | anyhow = "1.0.26" |
19 | itertools = "0.9.0" | 19 | itertools = "0.10.0" |
20 | 20 | ||
21 | arena = { path = "../arena", version = "0.0.0" } | 21 | arena = { path = "../arena", version = "0.0.0" } |
22 | cfg = { path = "../cfg", version = "0.0.0" } | 22 | cfg = { path = "../cfg", version = "0.0.0" } |
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index bb3b6f2ef..b991b59a6 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | convert::TryInto, | ||
4 | ffi::OsStr, | 5 | ffi::OsStr, |
5 | ops, | 6 | ops, |
6 | path::{Path, PathBuf}, | 7 | path::{Path, PathBuf}, |
@@ -196,8 +197,23 @@ impl CargoWorkspace { | |||
196 | if let Some(target) = target { | 197 | if let Some(target) = target { |
197 | meta.other_options(vec![String::from("--filter-platform"), target]); | 198 | meta.other_options(vec![String::from("--filter-platform"), target]); |
198 | } | 199 | } |
200 | |||
199 | let mut meta = meta.exec().with_context(|| { | 201 | let mut meta = meta.exec().with_context(|| { |
200 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) | 202 | let cwd: Option<AbsPathBuf> = |
203 | std::env::current_dir().ok().and_then(|p| p.try_into().ok()); | ||
204 | |||
205 | let workdir = cargo_toml | ||
206 | .parent() | ||
207 | .map(|p| p.to_path_buf()) | ||
208 | .or(cwd) | ||
209 | .map(|dir| dir.to_string_lossy().to_string()) | ||
210 | .unwrap_or_else(|| "<failed to get path>".into()); | ||
211 | |||
212 | format!( | ||
213 | "Failed to run `cargo metadata --manifest-path {}` in `{}`", | ||
214 | cargo_toml.display(), | ||
215 | workdir | ||
216 | ) | ||
201 | })?; | 217 | })?; |
202 | 218 | ||
203 | let mut out_dir_by_id = FxHashMap::default(); | 219 | let mut out_dir_by_id = FxHashMap::default(); |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 470c1d458..0a63593fb 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -19,7 +19,7 @@ anyhow = "1.0.26" | |||
19 | crossbeam-channel = "0.5.0" | 19 | crossbeam-channel = "0.5.0" |
20 | dissimilar = "1.0.2" | 20 | dissimilar = "1.0.2" |
21 | env_logger = { version = "0.8.1", default-features = false } | 21 | env_logger = { version = "0.8.1", default-features = false } |
22 | itertools = "0.9.0" | 22 | itertools = "0.10.0" |
23 | jod-thread = "0.1.0" | 23 | jod-thread = "0.1.0" |
24 | log = "0.4.8" | 24 | log = "0.4.8" |
25 | lsp-types = { version = "0.86.0", features = ["proposed"] } | 25 | lsp-types = { version = "0.86.0", features = ["proposed"] } |
@@ -29,6 +29,7 @@ oorandom = "11.1.2" | |||
29 | rustc-hash = "1.1.0" | 29 | rustc-hash = "1.1.0" |
30 | serde = { version = "1.0.106", features = ["derive"] } | 30 | serde = { version = "1.0.106", features = ["derive"] } |
31 | serde_json = { version = "1.0.48", features = ["preserve_order"] } | 31 | serde_json = { version = "1.0.48", features = ["preserve_order"] } |
32 | serde_path_to_error = "0.1" | ||
32 | threadpool = "1.7.1" | 33 | threadpool = "1.7.1" |
33 | rayon = "1.5" | 34 | rayon = "1.5" |
34 | mimalloc = { version = "0.1.19", default-features = false, optional = true } | 35 | 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 ec37fba04..948cfc17c 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; |
@@ -401,6 +403,45 @@ pub(crate) fn handle_workspace_symbol( | |||
401 | } | 403 | } |
402 | } | 404 | } |
403 | 405 | ||
406 | pub(crate) fn handle_will_rename_files( | ||
407 | snap: GlobalStateSnapshot, | ||
408 | params: lsp_types::RenameFilesParams, | ||
409 | ) -> Result<Option<lsp_types::WorkspaceEdit>> { | ||
410 | let _p = profile::span("handle_will_rename_files"); | ||
411 | |||
412 | let source_changes: Vec<SourceChange> = params | ||
413 | .files | ||
414 | .into_iter() | ||
415 | .filter_map(|file_rename| { | ||
416 | let from = Url::parse(&file_rename.old_uri).ok()?; | ||
417 | let to = Url::parse(&file_rename.new_uri).ok()?; | ||
418 | |||
419 | let from_path = from.to_file_path().ok()?; | ||
420 | let to_path = to.to_file_path().ok()?; | ||
421 | |||
422 | // Limit to single-level moves for now. | ||
423 | match (from_path.parent(), to_path.parent()) { | ||
424 | (Some(p1), Some(p2)) if p1 == p2 => { | ||
425 | let new_name = to_path.file_stem()?; | ||
426 | let new_name = new_name.to_str()?; | ||
427 | Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())) | ||
428 | } | ||
429 | _ => None, | ||
430 | } | ||
431 | }) | ||
432 | .filter_map(|(file_id, new_name)| { | ||
433 | snap.analysis.will_rename_file(file_id, &new_name).ok()? | ||
434 | }) | ||
435 | .collect(); | ||
436 | |||
437 | // Drop file system edits since we're just renaming things on the same level | ||
438 | let edits = source_changes.into_iter().map(|it| it.source_file_edits).flatten().collect(); | ||
439 | let source_change = SourceChange::from_edits(edits, Vec::new()); | ||
440 | |||
441 | let workspace_edit = to_proto::workspace_edit(&snap, source_change)?; | ||
442 | Ok(Some(workspace_edit)) | ||
443 | } | ||
444 | |||
404 | pub(crate) fn handle_goto_definition( | 445 | pub(crate) fn handle_goto_definition( |
405 | snap: GlobalStateSnapshot, | 446 | snap: GlobalStateSnapshot, |
406 | params: lsp_types::GotoDefinitionParams, | 447 | params: lsp_types::GotoDefinitionParams, |
@@ -641,7 +682,7 @@ pub(crate) fn handle_completion_resolve( | |||
641 | &snap.config.completion, | 682 | &snap.config.completion, |
642 | FilePosition { file_id, offset }, | 683 | FilePosition { file_id, offset }, |
643 | &resolve_data.full_import_path, | 684 | &resolve_data.full_import_path, |
644 | &resolve_data.imported_name, | 685 | resolve_data.imported_name, |
645 | )? | 686 | )? |
646 | .into_iter() | 687 | .into_iter() |
647 | .flat_map(|edit| { | 688 | .flat_map(|edit| { |
@@ -821,16 +862,18 @@ pub(crate) fn handle_formatting( | |||
821 | } | 862 | } |
822 | }; | 863 | }; |
823 | 864 | ||
824 | let mut rustfmt = rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?; | 865 | let mut rustfmt = |
866 | rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?; | ||
825 | 867 | ||
826 | rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; | 868 | rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; |
827 | 869 | ||
828 | let output = rustfmt.wait_with_output()?; | 870 | let output = rustfmt.wait_with_output()?; |
829 | let captured_stdout = String::from_utf8(output.stdout)?; | 871 | let captured_stdout = String::from_utf8(output.stdout)?; |
872 | let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default(); | ||
830 | 873 | ||
831 | if !output.status.success() { | 874 | if !output.status.success() { |
832 | match output.status.code() { | 875 | match output.status.code() { |
833 | Some(1) => { | 876 | Some(1) if !captured_stderr.contains("not installed") => { |
834 | // While `rustfmt` doesn't have a specific exit code for parse errors this is the | 877 | // While `rustfmt` doesn't have a specific exit code for parse errors this is the |
835 | // likely cause exiting with 1. Most Language Servers swallow parse errors on | 878 | // likely cause exiting with 1. Most Language Servers swallow parse errors on |
836 | // formatting because otherwise an error is surfaced to the user on top of the | 879 | // formatting because otherwise an error is surfaced to the user on top of the |
@@ -846,8 +889,9 @@ pub(crate) fn handle_formatting( | |||
846 | format!( | 889 | format!( |
847 | r#"rustfmt exited with: | 890 | r#"rustfmt exited with: |
848 | Status: {} | 891 | Status: {} |
849 | stdout: {}"#, | 892 | stdout: {} |
850 | output.status, captured_stdout, | 893 | stderr: {}"#, |
894 | output.status, captured_stdout, captured_stderr, | ||
851 | ), | 895 | ), |
852 | ) | 896 | ) |
853 | .into()); | 897 | .into()); |
@@ -867,58 +911,8 @@ pub(crate) fn handle_formatting( | |||
867 | } | 911 | } |
868 | } | 912 | } |
869 | 913 | ||
870 | fn handle_fixes( | ||
871 | snap: &GlobalStateSnapshot, | ||
872 | params: &lsp_types::CodeActionParams, | ||
873 | res: &mut Vec<lsp_ext::CodeAction>, | ||
874 | ) -> Result<()> { | ||
875 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | ||
876 | let line_index = snap.analysis.file_line_index(file_id)?; | ||
877 | let range = from_proto::text_range(&line_index, params.range); | ||
878 | |||
879 | match ¶ms.context.only { | ||
880 | Some(v) => { | ||
881 | if !v.iter().any(|it| { | ||
882 | it == &lsp_types::CodeActionKind::EMPTY | ||
883 | || it == &lsp_types::CodeActionKind::QUICKFIX | ||
884 | }) { | ||
885 | return Ok(()); | ||
886 | } | ||
887 | } | ||
888 | None => {} | ||
889 | }; | ||
890 | |||
891 | let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, file_id)?; | ||
892 | |||
893 | for fix in diagnostics | ||
894 | .into_iter() | ||
895 | .filter_map(|d| d.fix) | ||
896 | .filter(|fix| fix.fix_trigger_range.intersect(range).is_some()) | ||
897 | { | ||
898 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; | ||
899 | let action = lsp_ext::CodeAction { | ||
900 | title: fix.label.to_string(), | ||
901 | group: None, | ||
902 | kind: Some(CodeActionKind::QUICKFIX), | ||
903 | edit: Some(edit), | ||
904 | is_preferred: Some(false), | ||
905 | data: None, | ||
906 | }; | ||
907 | res.push(action); | ||
908 | } | ||
909 | |||
910 | for fix in snap.check_fixes.get(&file_id).into_iter().flatten() { | ||
911 | let fix_range = from_proto::text_range(&line_index, fix.range); | ||
912 | if fix_range.intersect(range).is_none() { | ||
913 | continue; | ||
914 | } | ||
915 | res.push(fix.action.clone()); | ||
916 | } | ||
917 | Ok(()) | ||
918 | } | ||
919 | |||
920 | pub(crate) fn handle_code_action( | 914 | pub(crate) fn handle_code_action( |
921 | mut snap: GlobalStateSnapshot, | 915 | snap: GlobalStateSnapshot, |
922 | params: lsp_types::CodeActionParams, | 916 | params: lsp_types::CodeActionParams, |
923 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { | 917 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { |
924 | let _p = profile::span("handle_code_action"); | 918 | let _p = profile::span("handle_code_action"); |
@@ -934,24 +928,35 @@ pub(crate) fn handle_code_action( | |||
934 | let range = from_proto::text_range(&line_index, params.range); | 928 | let range = from_proto::text_range(&line_index, params.range); |
935 | let frange = FileRange { file_id, range }; | 929 | let frange = FileRange { file_id, range }; |
936 | 930 | ||
937 | snap.config.assist.allowed = params | 931 | let assists_config = AssistConfig { |
938 | .clone() | 932 | allowed: params |
939 | .context | 933 | .clone() |
940 | .only | 934 | .context |
941 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | 935 | .only |
936 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()), | ||
937 | ..snap.config.assist | ||
938 | }; | ||
942 | 939 | ||
943 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | 940 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
944 | 941 | ||
945 | handle_fixes(&snap, ¶ms, &mut res)?; | 942 | let include_quick_fixes = match ¶ms.context.only { |
943 | Some(v) => v.iter().any(|it| { | ||
944 | it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX | ||
945 | }), | ||
946 | None => true, | ||
947 | }; | ||
948 | if include_quick_fixes { | ||
949 | add_quick_fixes(&snap, frange, &line_index, &mut res)?; | ||
950 | } | ||
946 | 951 | ||
947 | if snap.config.client_caps.code_action_resolve { | 952 | if snap.config.client_caps.code_action_resolve { |
948 | for (index, assist) in | 953 | for (index, assist) in |
949 | snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate() | 954 | snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() |
950 | { | 955 | { |
951 | res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); | 956 | res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); |
952 | } | 957 | } |
953 | } else { | 958 | } else { |
954 | for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() { | 959 | for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() { |
955 | res.push(to_proto::resolved_code_action(&snap, assist)?); | 960 | res.push(to_proto::resolved_code_action(&snap, assist)?); |
956 | } | 961 | } |
957 | } | 962 | } |
@@ -959,6 +964,40 @@ pub(crate) fn handle_code_action( | |||
959 | Ok(Some(res)) | 964 | Ok(Some(res)) |
960 | } | 965 | } |
961 | 966 | ||
967 | fn add_quick_fixes( | ||
968 | snap: &GlobalStateSnapshot, | ||
969 | frange: FileRange, | ||
970 | line_index: &Arc<LineIndex>, | ||
971 | acc: &mut Vec<lsp_ext::CodeAction>, | ||
972 | ) -> Result<()> { | ||
973 | let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, frange.file_id)?; | ||
974 | |||
975 | for fix in diagnostics | ||
976 | .into_iter() | ||
977 | .filter_map(|d| d.fix) | ||
978 | .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some()) | ||
979 | { | ||
980 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; | ||
981 | let action = lsp_ext::CodeAction { | ||
982 | title: fix.label.to_string(), | ||
983 | group: None, | ||
984 | kind: Some(CodeActionKind::QUICKFIX), | ||
985 | edit: Some(edit), | ||
986 | is_preferred: Some(false), | ||
987 | data: None, | ||
988 | }; | ||
989 | acc.push(action); | ||
990 | } | ||
991 | |||
992 | for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() { | ||
993 | let fix_range = from_proto::text_range(&line_index, fix.range); | ||
994 | if fix_range.intersect(frange.range).is_some() { | ||
995 | acc.push(fix.action.clone()); | ||
996 | } | ||
997 | } | ||
998 | Ok(()) | ||
999 | } | ||
1000 | |||
962 | pub(crate) fn handle_code_action_resolve( | 1001 | pub(crate) fn handle_code_action_resolve( |
963 | mut snap: GlobalStateSnapshot, | 1002 | mut snap: GlobalStateSnapshot, |
964 | mut code_action: lsp_ext::CodeAction, | 1003 | mut code_action: lsp_ext::CodeAction, |
@@ -980,11 +1019,11 @@ pub(crate) fn handle_code_action_resolve( | |||
980 | .only | 1019 | .only |
981 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | 1020 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); |
982 | 1021 | ||
983 | let assists = snap.analysis.resolved_assists(&snap.config.assist, frange)?; | 1022 | let assists = snap.analysis.assists(&snap.config.assist, true, frange)?; |
984 | let (id, index) = split_once(¶ms.id, ':').unwrap(); | 1023 | let (id, index) = split_once(¶ms.id, ':').unwrap(); |
985 | let index = index.parse::<usize>().unwrap(); | 1024 | let index = index.parse::<usize>().unwrap(); |
986 | let assist = &assists[index]; | 1025 | let assist = &assists[index]; |
987 | assert!(assist.assist.id.0 == id); | 1026 | assert!(assist.id.0 == id); |
988 | let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; | 1027 | let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; |
989 | code_action.edit = edit; | 1028 | code_action.edit = edit; |
990 | Ok(code_action) | 1029 | Ok(code_action) |
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 682fa5f7f..c9494e300 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -47,7 +47,7 @@ pub type Error = Box<dyn std::error::Error + Send + Sync>; | |||
47 | pub type Result<T, E = Error> = std::result::Result<T, E>; | 47 | pub type Result<T, E = Error> = std::result::Result<T, E>; |
48 | 48 | ||
49 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { | 49 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { |
50 | let res = T::deserialize(&json) | 50 | let res = serde_path_to_error::deserialize(&json) |
51 | .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?; | 51 | .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?; |
52 | Ok(res) | 52 | Ok(res) |
53 | } | 53 | } |
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/ssr/Cargo.toml b/crates/ssr/Cargo.toml index 98ed25fb6..339eda86a 100644 --- a/crates/ssr/Cargo.toml +++ b/crates/ssr/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | itertools = "0.9.0" | 15 | itertools = "0.10.0" |
16 | 16 | ||
17 | text_edit = { path = "../text_edit", version = "0.0.0" } | 17 | text_edit = { path = "../text_edit", version = "0.0.0" } |
18 | syntax = { path = "../syntax", version = "0.0.0" } | 18 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/ssr/src/matching.rs b/crates/ssr/src/matching.rs index 99b187311..6cf831431 100644 --- a/crates/ssr/src/matching.rs +++ b/crates/ssr/src/matching.rs | |||
@@ -473,7 +473,9 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
473 | } | 473 | } |
474 | SyntaxElement::Node(n) => { | 474 | SyntaxElement::Node(n) => { |
475 | if let Some(first_token) = n.first_token() { | 475 | if let Some(first_token) = n.first_token() { |
476 | if Some(first_token.to_string()) == next_pattern_token { | 476 | if Some(first_token.text().as_str()) |
477 | == next_pattern_token.as_deref() | ||
478 | { | ||
477 | if let Some(SyntaxElement::Node(p)) = pattern.next() { | 479 | if let Some(SyntaxElement::Node(p)) = pattern.next() { |
478 | // We have a subtree that starts with the next token in our pattern. | 480 | // We have a subtree that starts with the next token in our pattern. |
479 | self.attempt_match_token_tree(phase, &p, &n)?; | 481 | self.attempt_match_token_tree(phase, &p, &n)?; |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index c6a6f11e1..5d8389ade 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -11,9 +11,9 @@ edition = "2018" | |||
11 | doctest = false | 11 | doctest = false |
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.9.0" | 14 | itertools = "0.10.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/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index c45cb514a..2aa472fb4 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -193,6 +193,14 @@ impl ast::UseTreeList { | |||
193 | .and_then(ast::UseTree::cast) | 193 | .and_then(ast::UseTree::cast) |
194 | .expect("UseTreeLists are always nested in UseTrees") | 194 | .expect("UseTreeLists are always nested in UseTrees") |
195 | } | 195 | } |
196 | |||
197 | pub fn has_inner_comment(&self) -> bool { | ||
198 | self.syntax() | ||
199 | .children_with_tokens() | ||
200 | .filter_map(|it| it.into_token()) | ||
201 | .find_map(ast::Comment::cast) | ||
202 | .is_some() | ||
203 | } | ||
196 | } | 204 | } |
197 | 205 | ||
198 | impl ast::Impl { | 206 | impl ast::Impl { |
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/tt/src/lib.rs b/crates/tt/src/lib.rs index 6c1bf8d09..8301dc28a 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! `tt` crate defines a `TokenTree` data structure: this is the interface (both | 1 | //! `tt` crate defines a `TokenTree` data structure: this is the interface (both |
2 | //! input and output) of macros. It closely mirrors `proc_macro` crate's | 2 | //! input and output) of macros. It closely mirrors `proc_macro` crate's |
3 | //! `TokenTree`. | 3 | //! `TokenTree`. |
4 | use std::{fmt, panic::RefUnwindSafe}; | 4 | use std::fmt; |
5 | 5 | ||
6 | use stdx::impl_from; | 6 | use stdx::impl_from; |
7 | 7 | ||
@@ -247,8 +247,3 @@ impl fmt::Display for ExpansionError { | |||
247 | } | 247 | } |
248 | } | 248 | } |
249 | } | 249 | } |
250 | |||
251 | pub trait TokenExpander: fmt::Debug + Send + Sync + RefUnwindSafe { | ||
252 | fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) | ||
253 | -> Result<Subtree, ExpansionError>; | ||
254 | } | ||