aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/Cargo.toml2
-rw-r--r--crates/assists/src/assist_context.rs64
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs16
-rw-r--r--crates/assists/src/handlers/extract_module_to_file.rs133
-rw-r--r--crates/assists/src/handlers/extract_variable.rs2
-rw-r--r--crates/assists/src/handlers/invert_if.rs9
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs81
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs31
-rw-r--r--crates/assists/src/lib.rs43
-rw-r--r--crates/assists/src/tests.rs72
-rw-r--r--crates/assists/src/tests/generated.rs31
-rw-r--r--crates/assists/src/utils.rs8
-rw-r--r--crates/assists/src/utils/import_assets.rs37
-rw-r--r--crates/base_db/src/input.rs19
-rw-r--r--crates/base_db/src/lib.rs2
-rw-r--r--crates/completion/Cargo.toml2
-rw-r--r--crates/completion/src/completions.rs33
-rw-r--r--crates/completion/src/completions/attribute.rs2
-rw-r--r--crates/completion/src/completions/pattern.rs178
-rw-r--r--crates/completion/src/completions/postfix.rs2
-rw-r--r--crates/completion/src/completions/postfix/format_like.rs13
-rw-r--r--crates/completion/src/completions/qualified_path.rs29
-rw-r--r--crates/completion/src/completions/unqualified_path.rs42
-rw-r--r--crates/completion/src/context.rs21
-rw-r--r--crates/completion/src/lib.rs2
-rw-r--r--crates/completion/src/render.rs7
-rw-r--r--crates/completion/src/render/builder_ext.rs1
-rw-r--r--crates/completion/src/render/enum_variant.rs45
-rw-r--r--crates/completion/src/render/pattern.rs148
-rw-r--r--crates/hir/Cargo.toml2
-rw-r--r--crates/hir/src/code_model.rs40
-rw-r--r--crates/hir/src/from_id.rs15
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/hir/src/semantics.rs33
-rw-r--r--crates/hir/src/semantics/source_to_def.rs15
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/body.rs24
-rw-r--r--crates/hir_def/src/body/lower.rs104
-rw-r--r--crates/hir_def/src/expr.rs30
-rw-r--r--crates/hir_def/src/import_map.rs224
-rw-r--r--crates/hir_def/src/nameres.rs4
-rw-r--r--crates/hir_def/src/nameres/mod_resolution.rs2
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs4
-rw-r--r--crates/hir_expand/src/builtin_macro.rs8
-rw-r--r--crates/hir_expand/src/db.rs11
-rw-r--r--crates/hir_expand/src/eager.rs14
-rw-r--r--crates/hir_expand/src/lib.rs27
-rw-r--r--crates/hir_expand/src/proc_macro.rs9
-rw-r--r--crates/hir_ty/Cargo.toml8
-rw-r--r--crates/hir_ty/src/infer/expr.rs12
-rw-r--r--crates/hir_ty/src/infer/pat.rs6
-rw-r--r--crates/hir_ty/src/tests/patterns.rs30
-rw-r--r--crates/hir_ty/src/tests/simple.rs13
-rw-r--r--crates/hir_ty/src/traits/chalk.rs11
-rw-r--r--crates/ide/Cargo.toml2
-rw-r--r--crates/ide/src/diagnostics.rs141
-rw-r--r--crates/ide/src/diagnostics/fixes.rs1
-rw-r--r--crates/ide/src/display/navigation_target.rs25
-rw-r--r--crates/ide/src/doc_links.rs3
-rw-r--r--crates/ide/src/goto_definition.rs40
-rw-r--r--crates/ide/src/hover.rs2
-rw-r--r--crates/ide/src/lib.rs32
-rw-r--r--crates/ide/src/references.rs72
-rw-r--r--crates/ide/src/references/rename.rs44
-rw-r--r--crates/ide/src/runnables.rs40
-rw-r--r--crates/ide/src/syntax_highlighting.rs19
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs5
-rw-r--r--crates/ide_db/Cargo.toml2
-rw-r--r--crates/ide_db/src/defs.rs18
-rw-r--r--crates/ide_db/src/imports_locator.rs23
-rw-r--r--crates/ide_db/src/source_change.rs2
-rw-r--r--crates/mbe/src/lib.rs58
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs72
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs29
-rw-r--r--crates/mbe/src/parser.rs77
-rw-r--r--crates/mbe/src/syntax_bridge.rs3
-rw-r--r--crates/mbe/src/tests.rs64
-rw-r--r--crates/parser/src/grammar.rs2
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs9
-rw-r--r--crates/parser/src/grammar/items.rs16
-rw-r--r--crates/parser/src/grammar/patterns.rs14
-rw-r--r--crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--crates/proc_macro_api/src/lib.rs8
-rw-r--r--crates/proc_macro_api/src/rpc.rs4
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/proc_macro_srv/src/dylib.rs3
-rw-r--r--crates/proc_macro_srv/src/lib.rs20
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/client.rs12
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/mod.rs3
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/server.rs34
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs43
-rw-r--r--crates/project_model/Cargo.toml2
-rw-r--r--crates/project_model/src/cargo_workspace.rs18
-rw-r--r--crates/rust-analyzer/Cargo.toml3
-rw-r--r--crates/rust-analyzer/src/caps.rs29
-rw-r--r--crates/rust-analyzer/src/cli/progress_report.rs8
-rw-r--r--crates/rust-analyzer/src/config.rs38
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs4
-rw-r--r--crates/rust-analyzer/src/handlers.rs175
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs60
-rw-r--r--crates/ssr/Cargo.toml2
-rw-r--r--crates/ssr/src/matching.rs4
-rw-r--r--crates/syntax/Cargo.toml4
-rw-r--r--crates/syntax/src/ast/expr_ext.rs4
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs33
-rw-r--r--crates/syntax/src/ast/make.rs8
-rw-r--r--crates/syntax/src/ast/node_ext.rs8
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rast76
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rs4
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0157_const_block.rast23
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0157_const_block.rs1
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast57
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs2
-rw-r--r--crates/tt/src/lib.rs7
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]
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.9.0" 14itertools = "0.10.0"
15either = "1.6.1" 15either = "1.6.1"
16 16
17stdx = { path = "../stdx", version = "0.0.0" } 17stdx = { 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
5use algo::find_covering_element; 5use algo::find_covering_element;
6use hir::Semantics; 6use hir::Semantics;
7use ide_db::base_db::{FileId, FileRange}; 7use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange};
8use ide_db::{ 8use 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};
13use syntax::{ 13use syntax::{
@@ -19,7 +19,7 @@ use text_edit::{TextEdit, TextEditBuilder};
19 19
20use crate::{ 20use 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> {
105pub(crate) struct Assists { 105pub(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
112impl Assists { 112impl 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
214impl AssistBuilder { 188impl 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 @@
1use ast::edit::IndentLevel;
2use ide_db::base_db::AnchoredPathBuf;
3use syntax::{
4 ast::{self, edit::AstNodeEdit, NameOwner},
5 AstNode,
6};
7
8use 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// ```
23pub(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)]
65mod 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#"
75mod tests {<|>
76 #[test] fn t() {}
77}
78"#,
79 r#"
80//- /main.rs
81mod 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
94mod submod;
95//- /submod.rs
96mod inner<|> {
97 fn f() {}
98}
99fn g() {}
100"#,
101 r#"
102//- /submod.rs
103mod inner;
104fn g() {}
105//- /submod/inner.rs
106fn 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
117mod submodule;
118//- /submodule/mod.rs
119mod inner<|> {
120 fn f() {}
121}
122fn g() {}
123"#,
124 r#"
125//- /submodule/mod.rs
126mod inner;
127fn g() {}
128//- /submodule/inner.rs
129fn 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};
2use syntax::{ 2use 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};
7use test_utils::mark; 7use test_utils::mark;
8use SyntaxKind::WHITESPACE;
8 9
9use crate::{ 10use 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
88fn range_with_coma(node: &SyntaxNode) -> TextRange { 89fn 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#"
142fn foo(<|>x: i32, y: i32) { y; }
143fn a() { foo(1, 2) }
144fn b() { foo(1, 2,) }
145"#,
146 r#"
147fn foo(y: i32) { y; }
148fn a() { foo(2) }
149fn b() { foo(2,) }
150"#,
151 );
152 }
153
154 #[test]
155 fn remove_unused_single_param() {
156 check_assist(
157 remove_unused_param,
158 r#"
159fn foo(<|>x: i32) { 0; }
160fn a() { foo(1) }
161fn b() { foo(1, ) }
162"#,
163 r#"
164fn foo() { 0; }
165fn a() { foo() }
166fn b() { foo( ) }
167"#,
168 );
169 }
170
171 #[test]
172 fn remove_unused_surrounded_by_parms() {
173 check_assist(
174 remove_unused_param,
175 r#"
176fn foo(x: i32, <|>y: i32, z: i32) { x; }
177fn a() { foo(1, 2, 3) }
178fn b() { foo(1, 2, 3,) }
179"#,
180 r#"
181fn foo(x: i32, z: i32) { x; }
182fn a() { foo(1, 3) }
183fn 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
79pub 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
84impl Assist { 87impl 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
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; 4use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
5use ide_db::source_change::FileSystemEdit;
5use ide_db::RootDatabase; 6use ide_db::RootDatabase;
6use syntax::TextRange; 7use syntax::TextRange;
7use test_utils::{assert_eq_text, extract_offset, extract_range}; 8use 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#####"
44trait Trait { 44trait 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
50impl Trait for () { 50impl 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#####"
57trait Trait { 57trait 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
63impl Trait for () { 63impl 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#####"
78trait Trait<T> { 78trait 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#####"
89trait Trait<T> { 89trait 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
95impl Trait<u32> for () { 95impl 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]
241fn doctest_extract_module_to_file() {
242 check_doc_test(
243 "extract_module_to_file",
244 r#####"
245mod foo {<|>
246 fn t() {}
247}
248"#####,
249 r#####"
250mod foo;
251"#####,
252 )
253}
254
255#[test]
239fn doctest_extract_struct_from_enum_variant() { 256fn 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
9use std::{fmt, iter::FromIterator, ops, str::FromStr, sync::Arc}; 9use std::{fmt, iter::FromIterator, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
10 10
11use cfg::CfgOptions; 11use cfg::CfgOptions;
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
13use syntax::SmolStr; 13use syntax::SmolStr;
14use tt::TokenExpander; 14use tt::{ExpansionError, Subtree};
15use vfs::{file_set::FileSet, FileId, VfsPath}; 15use 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
153pub 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)]
154pub struct ProcMacro { 163pub 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
160impl Eq for ProcMacro {} 169impl 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};
20pub use salsa; 20pub 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"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13itertools = "0.9.0" 13itertools = "0.10.0"
14log = "0.4.8" 14log = "0.4.8"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16either = "1.6.1" 16either = "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};
19use crate::{ 19use 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
3use crate::{CompletionContext, Completions}; 3use crate::{CompletionContext, Completions};
4 4
5/// Completes constats and paths in patterns. 5/// Completes constants and paths in patterns.
6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { 6pub(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) {
42mod tests { 43mod 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#"
131enum E { X }
132
133static FOO: E = E::X;
134struct Bar { f: u32 }
135
136fn 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#"
149struct Bar { f: u32 }
150
151fn 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#"
165struct Foo { bar: String, baz: String }
166struct Bar(String, String);
167struct Baz;
168fn 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#"
181struct Foo { bar: String, baz: String }
182struct Bar(String, String);
183struct Baz;
184fn 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#"
199struct Foo { bar: i32, baz: i32 }
200struct Bar(String, String);
201struct Baz;
202fn 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#"
219mod foo {
220 pub struct Foo { pub bar: i32, baz: i32 }
221 pub struct Bar(pub String, String);
222 pub struct Invisible(String, String);
223}
224use foo::*;
225
226fn 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#"
244struct Foo(i32);
245fn main() {
246 match Foo(92) {
247 <|>(92) => (),
248 }
249}
250"#,
251 r#"
252struct Foo(i32);
253fn 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")
24static KINDS: &[(&str, &str)] = &[ 24static 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#"
766enum Foo {
767 Bar,
768 Baz,
769}
770
771impl 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
3use std::iter;
4
3use either::Either; 5use either::Either;
4use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type}; 6use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type};
5use ide_db::helpers::insert_use::ImportScope; 7use 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
52fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { 54fn 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#"
738enum Foo { Bar, Baz, Quux }
739
740fn 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_;
5pub(crate) mod function; 5pub(crate) mod function;
6pub(crate) mod enum_variant; 6pub(crate) mod enum_variant;
7pub(crate) mod const_; 7pub(crate) mod const_;
8pub(crate) mod pattern;
8pub(crate) mod type_alias; 9pub(crate) mod type_alias;
9 10
10mod builder_ext; 11mod 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#"
132enum Option<T> { Some(T), None }
133use Option::*;
134fn main(value: Option<i32>) {
135 match value {
136 Som<|>
137 }
138}
139"#,
140 r#"
141enum Option<T> { Some(T), None }
142use Option::*;
143fn 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#"
158enum E { Var(i32) }
159fn main() {
160 match E::Var(92) {
161 E::<|>(92) => (),
162 }
163}
164"#,
165 r#"
166enum E { Var(i32) }
167fn 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
3use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
4use itertools::Itertools;
5
6use crate::{
7 config::SnippetCap, item::CompletionKind, render::RenderContext, CompletionItem,
8 CompletionItemKind,
9};
10
11fn 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
29pub(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
50pub(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
66fn 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
85fn 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
113fn 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
141fn 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"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15either = "1.5.3" 15either = "1.5.3"
16arrayvec = "0.5.1" 16arrayvec = "0.5.1"
17itertools = "0.9.0" 17itertools = "0.10.0"
18 18
19stdx = { path = "../stdx", version = "0.0.0" } 19stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" } 20syntax = { 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)]
1209pub struct Label {
1210 pub(crate) parent: DefWithBodyId,
1211 pub(crate) label_id: LabelId,
1212}
1213
1214impl 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)]
1205pub enum GenericParam { 1237pub 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
6use hir_def::{ 6use 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
11use crate::{ 13use 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
15macro_rules! from_id { 18macro_rules! from_id {
@@ -228,6 +231,12 @@ impl From<(DefWithBodyId, PatId)> for Local {
228 } 231 }
229} 232}
230 233
234impl From<(DefWithBodyId, LabelId)> for Label {
235 fn from((parent, label_id): (DefWithBodyId, LabelId)) -> Self {
236 Label { parent, label_id }
237 }
238}
239
231impl From<MacroDef> for ItemInNs { 240impl 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;
15use rustc_hash::{FxHashMap, FxHashSet}; 15use rustc_hash::{FxHashMap, FxHashSet};
16use syntax::{ 16use 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
725fn find_root(node: &SyntaxNode) -> SyntaxNode { 752fn 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;
4use hir_def::{ 4use 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"
17anymap = "0.12.1" 17anymap = "0.12.1"
18drop_bomb = "0.1.4" 18drop_bomb = "0.1.4"
19fst = { version = "0.4", default-features = false } 19fst = { version = "0.4", default-features = false }
20itertools = "0.9.0" 20itertools = "0.10.0"
21indexmap = "1.4.0" 21indexmap = "1.4.0"
22smallvec = "1.4.0" 22smallvec = "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;
26use crate::{ 26use 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 {
226pub struct Body { 226pub 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>;
244pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; 245pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
245pub type PatSource = InFile<PatPtr>; 246pub type PatSource = InFile<PatPtr>;
246 247
248pub type LabelPtr = AstPtr<ast::Label>;
249pub 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
342impl Index<LabelId> for Body {
343 type Output = Label;
344
345 fn index(&self, label: LabelId) -> &Label {
346 &self.labels[label]
347 }
348}
349
337impl BodySourceMap { 350impl 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
23use crate::{ 23use 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 {
30pub type PatId = Idx<Pat>; 30pub type PatId = Idx<Pat>;
31 31
32#[derive(Debug, Clone, Eq, PartialEq)] 32#[derive(Debug, Clone, Eq, PartialEq)]
33pub struct Label {
34 pub name: Name,
35}
36pub type LabelId = Idx<Label>;
37
38#[derive(Debug, Clone, Eq, PartialEq)]
33pub enum Literal { 39pub 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
404impl Pat { 417impl 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)]
243pub 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)]
242pub struct Query { 254pub 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
251impl Query { 264impl 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
307fn 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> {
387mod tests { 432mod 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#"
678macro_rules! a { 678macro_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
692b! { static = #[] (); } 692b! { 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(&macro_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;
5use base_db::{salsa, SourceDatabase}; 5use base_db::{salsa, SourceDatabase};
6use mbe::{ExpandError, ExpandResult, MacroRules}; 6use mbe::{ExpandError, ExpandResult, MacroRules};
7use parser::FragmentKind; 7use parser::FragmentKind;
8use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; 8use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
9 9
10use crate::{ 10use 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> {
129fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { 129fn 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, &macro_arg.0) 274 expander.expand(db, loc.krate, &macro_arg.0)
274} 275}
275 276
276fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { 277fn 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(&macro_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
3use crate::{db::AstDatabase, LazyMacroId}; 3use crate::db::AstDatabase;
4use base_db::{CrateId, ProcMacroId}; 4use base_db::{CrateId, ProcMacroId};
5use tt::buffer::{Cursor, TokenBuffer}; 5use 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"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13itertools = "0.9.0" 13itertools = "0.10.0"
14arrayvec = "0.5.1" 14arrayvec = "0.5.1"
15smallvec = "1.2.0" 15smallvec = "1.2.0"
16ena = "0.14.0" 16ena = "0.14.0"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.43", default-features = false } 20chalk-solve = { version = "0.45", default-features = false }
21chalk-ir = "0.43" 21chalk-ir = "0.45"
22chalk-recursive = "0.43" 22chalk-recursive = "0.45"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_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]
779fn const_block_pattern() {
780 check_infer(
781 r#"
782struct Foo(usize);
783fn 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]
13either = "1.5.3" 13either = "1.5.3"
14indexmap = "1.4.0" 14indexmap = "1.4.0"
15itertools = "0.9.0" 15itertools = "0.10.0"
16log = "0.4.8" 16log = "0.4.8"
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18oorandom = "11.1.2" 18oorandom = "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#"
649use a;
650use a::{
651 c,
652 // d::e
653};
654
655mod 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; 743mod foo;
770 744
771 fn main() { 745fn 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 { 749struct Foo {
776 bar: i32 750 bar: i32
777 } 751}
778 ", 752"#,
779 r" 753 r#"
780 struct Foo { 754struct 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#"
806pub struct test_struct<|> { one: i32 } 780pub 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#"
823pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { 797pub 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#"
836pub fn SomeFn<|>(val: u8) -> u8 { 810pub 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#"
849fn some_fn() { 823fn 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#"
878pub struct TestStruct; 852pub 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;
5use either::Either; 5use either::Either;
6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; 6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource};
7use ide_db::{ 7use ide_db::{
8 base_db::{FileId, SourceDatabase}, 8 base_db::{FileId, FileRange, SourceDatabase},
9 symbol_index::FileSymbolKind, 9 symbol_index::FileSymbolKind,
10}; 10};
11use ide_db::{defs::Definition, RootDatabase}; 11use 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
426impl 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
424impl ToNav for hir::TypeParam { 447impl 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]
758macro_rules! include {}
759
760 include!("foo.rs");
761//^^^^^^^^^^^^^^^^^^^
762
763fn f() {
764 foo<|>();
765}
766
767mod confuse_index {
768 pub fn foo() {}
769}
770
771//- /foo.rs
772fn 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#"
1138fn 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};
82pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; 82pub use assists::{Assist, AssistConfig, AssistId, AssistKind};
83pub use completion::{ 83pub 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
178fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { 178fn 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#"
1094macro_rules! foo {($i:ident) => {$i} }
1095fn 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#"
1112macro_rules! foo {($i:ident) => {$i} }
1113fn 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#"
1130fn 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
8use hir::{Module, ModuleDef, ModuleSource, Semantics}; 8use hir::{Module, ModuleDef, ModuleSource, Semantics};
9use ide_db::base_db::{AnchoredPathBuf, FileRange, SourceDatabaseExt}; 9use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
10use ide_db::{ 10use 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
113pub(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
113fn find_module_at_offset( 130fn 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#"
1549fn foo<'a>() -> &'a () {
1550 'a: {
1551 'b: loop {
1552 break 'a<|>;
1553 }
1554 }
1555}
1556"#,
1557 r#"
1558fn 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
940mod foo;
941//- /foo.rs
942struct Foo;<|>
943impl 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; }
64pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 64pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 205<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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
173enum Option<T> { 178enum 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 }
19rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
20once_cell = "1.3.1" 20once_cell = "1.3.1"
21either = "1.6.1" 21either = "1.6.1"
22itertools = "0.9.0" 22itertools = "0.10.0"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25syntax = { path = "../syntax", version = "0.0.0" } 25syntax = { 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
8use hir::{ 8use 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};
12use syntax::{ 12use 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
32impl Definition { 32impl 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;
15pub fn find_exact_imports<'a>( 15pub 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)]
46pub enum FileSystemEdit { 46pub 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
15use std::fmt; 15use std::fmt;
16 16
17pub use tt::{Delimiter, Punct}; 17pub use tt::{Delimiter, DelimiterKind, Punct};
18 18
19use crate::{ 19use 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)]
80struct Rule { 80struct Rule {
81 lhs: tt::Subtree, 81 lhs: MetaTemplate,
82 rhs: tt::Subtree, 82 rhs: MetaTemplate,
83}
84
85#[derive(Clone, Debug, PartialEq, Eq)]
86struct MetaTemplate {
87 delimiter: Option<Delimiter>,
88 tokens: Vec<Result<Op, ExpandError>>,
89}
90
91impl<'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
202impl Rule { 218impl 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
220fn to_parse_error(e: ExpandError) -> ParseError { 236fn 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
228fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { 244fn 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
3use crate::{ 3use 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
11use super::ExpandResult; 11use 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
86pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match, ExpandError> { 86pub(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
102fn match_subtree( 102fn 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
373pub(super) fn match_repeat( 373pub(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
492fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &tt::Subtree) -> Result<(), ExpandError> { 492fn 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;
6use super::ExpandResult; 6use super::ExpandResult;
7use crate::{ 7use 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
13impl Bindings { 13impl Bindings {
@@ -50,7 +50,10 @@ impl Bindings {
50 } 50 }
51} 51}
52 52
53pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> { 53pub(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
77fn expand_subtree( 80fn 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
162fn expand_repeat( 165fn 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 @@
4use smallvec::SmallVec; 4use smallvec::SmallVec;
5use syntax::SmolStr; 5use syntax::SmolStr;
6 6
7use crate::{tt_iter::TtIter, ExpandError}; 7use crate::{tt_iter::TtIter, ExpandError, MetaTemplate};
8 8
9#[derive(Debug)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub(crate) enum Op<'a> { 10pub(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)]
17pub(crate) enum RepeatKind { 18pub(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
48pub(crate) fn parse_template( 49pub(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
54pub(crate) fn parse_pattern( 53pub(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
66fn parse_inner(src: &tt::Subtree, mode: Mode) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { 63fn 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
74macro_rules! err { 72macro_rules! err {
@@ -83,35 +81,41 @@ macro_rules! bail {
83 }; 81 };
84} 82}
85 83
86fn next_op<'a>( 84fn 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
129fn eat_fragment_kind<'a>( 139fn 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]
764fn test_expr_with_attr() {
765 parse_macro(
766 r#"
767macro_rules! m {
768 ($a:expr) => {0}
769}
770"#,
771 )
772 .assert_expand_items("m!(#[allow(a)]())", "0");
773}
774
775#[test]
764fn test_ty() { 776fn 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]
1007fn 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]
995fn test_underscore() { 1023fn 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]
1035fn test_underscore_not_greedily() {
1036 parse_macro(
1037 r#"
1038macro_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#"
1048macro_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]
1058fn test_underscore_as_type() {
1059 parse_macro(
1060 r#"
1061macro_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]
1007fn test_vertical_bar_with_pat() { 1071fn 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// }
396fn 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
19use base_db::ProcMacro; 19use base_db::{Env, ProcMacro};
20use tt::{SmolStr, Subtree}; 20use tt::{SmolStr, Subtree};
21 21
22use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; 22use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
@@ -39,17 +39,19 @@ impl PartialEq for ProcMacroProcessExpander {
39 } 39 }
40} 40}
41 41
42impl tt::TokenExpander for ProcMacroProcessExpander { 42impl 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"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13object = { version = "0.23", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } 13object = { version = "0.23", default-features = false, features = ["std", "read_core", "elf", "macho", "pe"] }
14libloading = "0.6.0" 14libloading = "0.6.0"
15memmap = "0.7" 15memmap = "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;
24use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; 24use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
25use std::{ 25use 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 {
37impl ProcMacroSrv { 37impl 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
304impl Bridge<'_> { 304impl 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"
16serde = { version = "1.0.106", features = ["derive"] } 16serde = { version = "1.0.106", features = ["derive"] }
17serde_json = "1.0.48" 17serde_json = "1.0.48"
18anyhow = "1.0.26" 18anyhow = "1.0.26"
19itertools = "0.9.0" 19itertools = "0.10.0"
20 20
21arena = { path = "../arena", version = "0.0.0" } 21arena = { path = "../arena", version = "0.0.0" }
22cfg = { path = "../cfg", version = "0.0.0" } 22cfg = { 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
3use std::{ 3use 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"
19crossbeam-channel = "0.5.0" 19crossbeam-channel = "0.5.0"
20dissimilar = "1.0.2" 20dissimilar = "1.0.2"
21env_logger = { version = "0.8.1", default-features = false } 21env_logger = { version = "0.8.1", default-features = false }
22itertools = "0.9.0" 22itertools = "0.10.0"
23jod-thread = "0.1.0" 23jod-thread = "0.1.0"
24log = "0.4.8" 24log = "0.4.8"
25lsp-types = { version = "0.86.0", features = ["proposed"] } 25lsp-types = { version = "0.86.0", features = ["proposed"] }
@@ -29,6 +29,7 @@ oorandom = "11.1.2"
29rustc-hash = "1.1.0" 29rustc-hash = "1.1.0"
30serde = { version = "1.0.106", features = ["derive"] } 30serde = { version = "1.0.106", features = ["derive"] }
31serde_json = { version = "1.0.48", features = ["preserve_order"] } 31serde_json = { version = "1.0.48", features = ["preserve_order"] }
32serde_path_to_error = "0.1"
32threadpool = "1.7.1" 33threadpool = "1.7.1"
33rayon = "1.5" 34rayon = "1.5"
34mimalloc = { version = "0.1.19", default-features = false, optional = true } 35mimalloc = { 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;
5use lsp_types::{ 5use 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};
15use rustc_hash::FxHashSet; 17use rustc_hash::FxHashSet;
16use serde_json::json; 18use 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
4use std::io::Write; 4use std::io::{self, Write};
5 5
6/// A Simple ASCII Progress Bar 6/// A Simple ASCII Progress Bar
7pub(crate) struct ProgressReport { 7pub(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 @@
5use std::{ 5use std::{
6 io::Write as _, 6 io::Write as _,
7 process::{self, Stdio}, 7 process::{self, Stdio},
8 sync::Arc,
8}; 9};
9 10
10use ide::{ 11use 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};
14use itertools::Itertools; 16use itertools::Itertools;
15use lsp_server::ErrorCode; 17use lsp_server::ErrorCode;
@@ -401,6 +403,45 @@ pub(crate) fn handle_workspace_symbol(
401 } 403 }
402} 404}
403 405
406pub(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
404pub(crate) fn handle_goto_definition( 445pub(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
870fn 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, &params.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 &params.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
920pub(crate) fn handle_code_action( 914pub(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, &params, &mut res)?; 942 let include_quick_fixes = match &params.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
967fn 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
962pub(crate) fn handle_code_action_resolve( 1001pub(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(&params.id, ':').unwrap(); 1023 let (id, index) = split_once(&params.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>;
47pub type Result<T, E = Error> = std::result::Result<T, E>; 47pub type Result<T, E = Error> = std::result::Result<T, E>;
48 48
49pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { 49pub 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};
14use itertools::Itertools; 14use 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
637pub(crate) fn resource_op( 639pub(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
663pub(crate) fn snippet_workspace_edit( 682pub(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
778pub(crate) fn resolved_code_action( 798pub(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]
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15itertools = "0.9.0" 15itertools = "0.10.0"
16 16
17text_edit = { path = "../text_edit", version = "0.0.0" } 17text_edit = { path = "../text_edit", version = "0.0.0" }
18syntax = { path = "../syntax", version = "0.0.0" } 18syntax = { 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"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.10.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "691.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "695.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_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)]
1255pub struct ConstBlockPat {
1256 pub(crate) syntax: SyntaxNode,
1257}
1258impl 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)]
1254pub struct RecordPatFieldList { 1263pub 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)]
1374pub enum FieldList { 1384pub 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}
2785impl 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}
2775impl AstNode for RecordPatFieldList { 2796impl 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 {
3242impl From<TupleStructPat> for Pat { 3263impl From<TupleStructPat> for Pat {
3243 fn from(node: TupleStructPat) -> Pat { Pat::TupleStructPat(node) } 3264 fn from(node: TupleStructPat) -> Pat { Pat::TupleStructPat(node) }
3244} 3265}
3266impl From<ConstBlockPat> for Pat {
3267 fn from(node: ConstBlockPat) -> Pat { Pat::ConstBlockPat(node) }
3268}
3245impl AstNode for Pat { 3269impl 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}
4166impl 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}
4140impl std::fmt::Display for RecordPatFieldList { 4171impl 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.
7use itertools::Itertools; 12use itertools::Itertools;
8use stdx::format_to; 13use stdx::format_to;
9 14
@@ -16,7 +21,8 @@ pub fn name(text: &str) -> ast::Name {
16pub fn name_ref(text: &str) -> ast::NameRef { 21pub 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`.
20pub fn ty(text: &str) -> ast::Type { 26pub 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
198impl ast::Impl { 206impl 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 @@
1fn 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 @@
1macro_rules! m ( ($i:ident) => {} );
2macro_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`.
4use std::{fmt, panic::RefUnwindSafe}; 4use std::fmt;
5 5
6use stdx::impl_from; 6use stdx::impl_from;
7 7
@@ -247,8 +247,3 @@ impl fmt::Display for ExpansionError {
247 } 247 }
248 } 248 }
249} 249}
250
251pub trait TokenExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
252 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
253 -> Result<Subtree, ExpansionError>;
254}