aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-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/invert_if.rs9
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs81
-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/completion/src/completions.rs33
-rw-r--r--crates/completion/src/completions/pattern.rs178
-rw-r--r--crates/completion/src/completions/qualified_path.rs29
-rw-r--r--crates/completion/src/completions/unqualified_path.rs27
-rw-r--r--crates/completion/src/context.rs21
-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/src/code_model.rs41
-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/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/generics.rs37
-rw-r--r--crates/hir_def/src/nameres/mod_resolution.rs2
-rw-r--r--crates/hir_expand/src/builtin_macro.rs5
-rw-r--r--crates/hir_expand/src/db.rs9
-rw-r--r--crates/hir_expand/src/eager.rs14
-rw-r--r--crates/hir_expand/src/lib.rs27
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/infer/expr.rs10
-rw-r--r--crates/hir_ty/src/infer/pat.rs6
-rw-r--r--crates/hir_ty/src/lower.rs18
-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/hir_ty/src/utils.rs9
-rw-r--r--crates/ide/src/diagnostics.rs119
-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.rs68
-rw-r--r--crates/ide/src/hover.rs2
-rw-r--r--crates/ide/src/lib.rs30
-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/src/defs.rs18
-rw-r--r--crates/ide_db/src/source_change.rs2
-rw-r--r--crates/mbe/src/tests.rs12
-rw-r--r--crates/parser/src/grammar.rs2
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs17
-rw-r--r--crates/parser/src/grammar/items.rs16
-rw-r--r--crates/parser/src/grammar/params.rs17
-rw-r--r--crates/parser/src/grammar/patterns.rs14
-rw-r--r--crates/parser/src/grammar/type_params.rs2
-rw-r--r--crates/parser/src/grammar/types.rs26
-rw-r--r--crates/parser/src/lib.rs2
-rw-r--r--crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/rust-analyzer/Cargo.toml1
-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.rs162
-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/syntax/Cargo.toml2
-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/test_data/parser/inline/ok/0138_self_param_outer_attr.rast20
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast43
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs1
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast50
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs1
-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/syntax/test_data/parser/ok/0051_parameter_attrs.rast222
-rw-r--r--crates/syntax/test_data/parser/ok/0063_variadic_fun.rast28
102 files changed, 2148 insertions, 716 deletions
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 69499ea32..4f59d39a9 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -4,10 +4,10 @@ use std::mem;
4 4
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/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/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/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/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/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..d09849752 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -1,5 +1,7 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
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() {
@@ -701,6 +705,7 @@ fn main() { <|> }
701 "#]], 705 "#]],
702 ); 706 );
703 } 707 }
708
704 #[test] 709 #[test]
705 fn completes_enum_variant_matcharm() { 710 fn completes_enum_variant_matcharm() {
706 check( 711 check(
@@ -722,6 +727,26 @@ fn main() {
722 } 727 }
723 728
724 #[test] 729 #[test]
730 fn completes_enum_variant_matcharm_ref() {
731 check(
732 r#"
733enum Foo { Bar, Baz, Quux }
734
735fn main() {
736 let foo = Foo::Quux;
737 match &foo { Qu<|> }
738}
739"#,
740 expect![[r#"
741 ev Foo::Bar ()
742 ev Foo::Baz ()
743 ev Foo::Quux ()
744 en Foo
745 "#]],
746 )
747 }
748
749 #[test]
725 fn completes_enum_variant_iflet() { 750 fn completes_enum_variant_iflet() {
726 check( 751 check(
727 r#" 752 r#"
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index 5cd11cf77..41de324d8 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -51,7 +51,7 @@ pub(crate) struct CompletionContext<'a> {
51 /// If a name-binding or reference to a const in a pattern. 51 /// If a name-binding or reference to a const in a pattern.
52 /// Irrefutable patterns (like let) are excluded. 52 /// Irrefutable patterns (like let) are excluded.
53 pub(super) is_pat_binding_or_const: bool, 53 pub(super) is_pat_binding_or_const: bool,
54 pub(super) is_irrefutable_let_pat_binding: bool, 54 pub(super) is_irrefutable_pat_binding: bool,
55 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. 55 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
56 pub(super) is_trivial_path: bool, 56 pub(super) is_trivial_path: bool,
57 /// If not a trivial path, the prefix (qualifier). 57 /// If not a trivial path, the prefix (qualifier).
@@ -147,7 +147,7 @@ impl<'a> CompletionContext<'a> {
147 active_parameter: ActiveParameter::at(db, position), 147 active_parameter: ActiveParameter::at(db, position),
148 is_param: false, 148 is_param: false,
149 is_pat_binding_or_const: false, 149 is_pat_binding_or_const: false,
150 is_irrefutable_let_pat_binding: false, 150 is_irrefutable_pat_binding: false,
151 is_trivial_path: false, 151 is_trivial_path: false,
152 path_qual: None, 152 path_qual: None,
153 after_if: false, 153 after_if: false,
@@ -327,14 +327,19 @@ impl<'a> CompletionContext<'a> {
327 if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { 327 if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
328 self.is_pat_binding_or_const = false; 328 self.is_pat_binding_or_const = false;
329 } 329 }
330 if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) { 330 if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| {
331 if let Some(pat) = let_stmt.pat() { 331 match_ast! {
332 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) 332 match node {
333 { 333 ast::LetStmt(it) => Some(it.pat()),
334 self.is_pat_binding_or_const = false; 334 ast::Param(it) => Some(it.pat()),
335 self.is_irrefutable_let_pat_binding = true; 335 _ => None,
336 } 336 }
337 } 337 }
338 }) {
339 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
340 self.is_pat_binding_or_const = false;
341 self.is_irrefutable_pat_binding = true;
342 }
338 } 343 }
339 } 344 }
340 if is_node::<ast::Param>(name.syntax()) { 345 if is_node::<ast::Param>(name.syntax()) {
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 1092a4825..1ba7201a1 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -5,6 +5,7 @@ pub(crate) mod macro_;
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/src/code_model.rs b/crates/hir/src/code_model.rs
index d6c7e71ea..b7ded3478 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -9,7 +9,7 @@ use hir_def::{
9 adt::StructKind, 9 adt::StructKind,
10 adt::VariantData, 10 adt::VariantData,
11 builtin_type::BuiltinType, 11 builtin_type::BuiltinType,
12 expr::{BindingAnnotation, Pat, PatId}, 12 expr::{BindingAnnotation, LabelId, Pat, PatId},
13 import_map, 13 import_map,
14 item_tree::ItemTreeNode, 14 item_tree::ItemTreeNode,
15 lang_item::LangItemTarget, 15 lang_item::LangItemTarget,
@@ -374,8 +374,6 @@ impl Module {
374 let crate_def_map = db.crate_def_map(self.id.krate); 374 let crate_def_map = db.crate_def_map(self.id.krate);
375 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); 375 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
376 for decl in self.declarations(db) { 376 for decl in self.declarations(db) {
377 decl.diagnostics(db, sink);
378
379 match decl { 377 match decl {
380 crate::ModuleDef::Function(f) => f.diagnostics(db, sink), 378 crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
381 crate::ModuleDef::Module(m) => { 379 crate::ModuleDef::Module(m) => {
@@ -384,7 +382,9 @@ impl Module {
384 m.diagnostics(db, sink) 382 m.diagnostics(db, sink)
385 } 383 }
386 } 384 }
387 _ => (), 385 _ => {
386 decl.diagnostics(db, sink);
387 }
388 } 388 }
389 } 389 }
390 390
@@ -511,6 +511,10 @@ impl Struct {
511 db.struct_data(self.id).repr.clone() 511 db.struct_data(self.id).repr.clone()
512 } 512 }
513 513
514 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
515 self.variant_data(db).kind()
516 }
517
514 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { 518 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
515 db.struct_data(self.id).variant_data.clone() 519 db.struct_data(self.id).variant_data.clone()
516 } 520 }
@@ -1202,6 +1206,34 @@ impl Local {
1202} 1206}
1203 1207
1204#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1208#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
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),
@@ -1268,7 +1300,6 @@ impl LifetimeParam {
1268 } 1300 }
1269} 1301}
1270 1302
1271// FIXME: rename from `ImplDef` to `Impl`
1272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1273pub struct Impl { 1304pub struct Impl {
1274 pub(crate) id: ImplId, 1305 pub(crate) id: ImplId,
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index 8e0c571b8..a0792b9a6 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -4,12 +4,15 @@
4//! are splitting the hir. 4//! are splitting the hir.
5 5
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/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/generics.rs b/crates/hir_def/src/generics.rs
index 41134d23b..bb8fca009 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -62,6 +62,7 @@ pub struct GenericParams {
62pub enum WherePredicate { 62pub enum WherePredicate {
63 TypeBound { target: WherePredicateTypeTarget, bound: TypeBound }, 63 TypeBound { target: WherePredicateTypeTarget, bound: TypeBound },
64 Lifetime { target: LifetimeRef, bound: LifetimeRef }, 64 Lifetime { target: LifetimeRef, bound: LifetimeRef },
65 ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound },
65} 66}
66 67
67#[derive(Clone, PartialEq, Eq, Debug)] 68#[derive(Clone, PartialEq, Eq, Debug)]
@@ -69,7 +70,6 @@ pub enum WherePredicateTypeTarget {
69 TypeRef(TypeRef), 70 TypeRef(TypeRef),
70 /// For desugared where predicates that can directly refer to a type param. 71 /// For desugared where predicates that can directly refer to a type param.
71 TypeParam(LocalTypeParamId), 72 TypeParam(LocalTypeParamId),
72 // FIXME: ForLifetime(Vec<LifetimeParamId>, TypeRef)
73} 73}
74 74
75#[derive(Default)] 75#[derive(Default)]
@@ -234,7 +234,7 @@ impl GenericParams {
234 for bound in 234 for bound in
235 node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) 235 node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
236 { 236 {
237 self.add_where_predicate_from_bound(lower_ctx, bound, target.clone()); 237 self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
238 } 238 }
239 } 239 }
240 240
@@ -279,8 +279,25 @@ impl GenericParams {
279 } else { 279 } else {
280 continue; 280 continue;
281 }; 281 };
282
283 let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| {
284 // Higher-Ranked Trait Bounds
285 param_list
286 .lifetime_params()
287 .map(|lifetime_param| {
288 lifetime_param
289 .lifetime()
290 .map_or_else(Name::missing, |lt| Name::new_lifetime(&lt))
291 })
292 .collect()
293 });
282 for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { 294 for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
283 self.add_where_predicate_from_bound(lower_ctx, bound, target.clone()); 295 self.add_where_predicate_from_bound(
296 lower_ctx,
297 bound,
298 lifetimes.as_ref(),
299 target.clone(),
300 );
284 } 301 }
285 } 302 }
286 } 303 }
@@ -289,6 +306,7 @@ impl GenericParams {
289 &mut self, 306 &mut self,
290 lower_ctx: &LowerCtx, 307 lower_ctx: &LowerCtx,
291 bound: ast::TypeBound, 308 bound: ast::TypeBound,
309 hrtb_lifetimes: Option<&Box<[Name]>>,
292 target: Either<TypeRef, LifetimeRef>, 310 target: Either<TypeRef, LifetimeRef>,
293 ) { 311 ) {
294 if bound.question_mark_token().is_some() { 312 if bound.question_mark_token().is_some() {
@@ -297,9 +315,16 @@ impl GenericParams {
297 } 315 }
298 let bound = TypeBound::from_ast(lower_ctx, bound); 316 let bound = TypeBound::from_ast(lower_ctx, bound);
299 let predicate = match (target, bound) { 317 let predicate = match (target, bound) {
300 (Either::Left(type_ref), bound) => WherePredicate::TypeBound { 318 (Either::Left(type_ref), bound) => match hrtb_lifetimes {
301 target: WherePredicateTypeTarget::TypeRef(type_ref), 319 Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
302 bound, 320 lifetimes: hrtb_lifetimes.clone(),
321 target: WherePredicateTypeTarget::TypeRef(type_ref),
322 bound,
323 },
324 None => WherePredicate::TypeBound {
325 target: WherePredicateTypeTarget::TypeRef(type_ref),
326 bound,
327 },
303 }, 328 },
304 (Either::Right(lifetime), TypeBound::Lifetime(bound)) => { 329 (Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
305 WherePredicate::Lifetime { target: lifetime, bound } 330 WherePredicate::Lifetime { target: lifetime, bound }
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs
index b4ccd4488..af3262439 100644
--- a/crates/hir_def/src/nameres/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/mod_resolution.rs
@@ -79,7 +79,7 @@ impl ModDir {
79 for candidate in candidate_files.iter() { 79 for candidate in candidate_files.iter() {
80 let path = AnchoredPath { anchor: file_id, path: candidate.as_str() }; 80 let path = AnchoredPath { anchor: file_id, path: candidate.as_str() };
81 if let Some(file_id) = db.resolve_path(path) { 81 if let Some(file_id) = db.resolve_path(path) {
82 let is_mod_rs = candidate.ends_with("mod.rs"); 82 let is_mod_rs = candidate.ends_with("/mod.rs");
83 83
84 let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() { 84 let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
85 (DirPath::empty(), false) 85 (DirPath::empty(), false)
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index dddbbcdac..6382521fb 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -563,6 +563,7 @@ mod tests {
563 563
564 let args = macro_call.token_tree().unwrap(); 564 let args = macro_call.token_tree().unwrap();
565 let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0; 565 let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0;
566 let call_id = AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call));
566 567
567 let arg_id = db.intern_eager_expansion({ 568 let arg_id = db.intern_eager_expansion({
568 EagerCallLoc { 569 EagerCallLoc {
@@ -570,7 +571,7 @@ mod tests {
570 fragment: FragmentKind::Expr, 571 fragment: FragmentKind::Expr,
571 subtree: Arc::new(parsed_args.clone()), 572 subtree: Arc::new(parsed_args.clone()),
572 krate, 573 krate,
573 file_id: file_id.into(), 574 call: call_id,
574 } 575 }
575 }); 576 });
576 577
@@ -580,7 +581,7 @@ mod tests {
580 fragment, 581 fragment,
581 subtree: Arc::new(subtree), 582 subtree: Arc::new(subtree),
582 krate, 583 krate,
583 file_id: file_id.into(), 584 call: call_id,
584 }; 585 };
585 586
586 let id: MacroCallId = db.intern_eager_expansion(eager).into(); 587 let id: MacroCallId = db.intern_eager_expansion(eager).into();
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 4477d867f..077de3727 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
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 };
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_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 289e812fe..965c1780a 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -17,9 +17,9 @@ ena = "0.14.0"
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..70a3f3075 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -143,7 +143,7 @@ impl<'a> InferenceContext<'a> {
143 self.breakables.push(BreakableContext { 143 self.breakables.push(BreakableContext {
144 may_break: false, 144 may_break: false,
145 break_ty: break_ty.clone(), 145 break_ty: break_ty.clone(),
146 label: label.clone(), 146 label: label.map(|label| self.body[label].name.clone()),
147 }); 147 });
148 let ty = self.infer_block(statements, *tail, &Expectation::has_type(break_ty)); 148 let ty = self.infer_block(statements, *tail, &Expectation::has_type(break_ty));
149 let ctxt = self.breakables.pop().expect("breakable stack broken"); 149 let ctxt = self.breakables.pop().expect("breakable stack broken");
@@ -155,7 +155,7 @@ impl<'a> InferenceContext<'a> {
155 } 155 }
156 None => self.infer_block(statements, *tail, expected), 156 None => self.infer_block(statements, *tail, expected),
157 }, 157 },
158 Expr::Unsafe { body } => self.infer_expr(*body, expected), 158 Expr::Unsafe { body } | Expr::Const { body } => self.infer_expr(*body, expected),
159 Expr::TryBlock { body } => { 159 Expr::TryBlock { body } => {
160 let _inner = self.infer_expr(*body, expected); 160 let _inner = self.infer_expr(*body, expected);
161 // FIXME should be std::result::Result<{inner}, _> 161 // FIXME should be std::result::Result<{inner}, _>
@@ -172,7 +172,7 @@ impl<'a> InferenceContext<'a> {
172 self.breakables.push(BreakableContext { 172 self.breakables.push(BreakableContext {
173 may_break: false, 173 may_break: false,
174 break_ty: self.table.new_type_var(), 174 break_ty: self.table.new_type_var(),
175 label: label.clone(), 175 label: label.map(|label| self.body[label].name.clone()),
176 }); 176 });
177 self.infer_expr(*body, &Expectation::has_type(Ty::unit())); 177 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
178 178
@@ -191,7 +191,7 @@ impl<'a> InferenceContext<'a> {
191 self.breakables.push(BreakableContext { 191 self.breakables.push(BreakableContext {
192 may_break: false, 192 may_break: false,
193 break_ty: Ty::Unknown, 193 break_ty: Ty::Unknown,
194 label: label.clone(), 194 label: label.map(|label| self.body[label].name.clone()),
195 }); 195 });
196 // while let is desugared to a match loop, so this is always simple while 196 // while let is desugared to a match loop, so this is always simple while
197 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); 197 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
@@ -207,7 +207,7 @@ impl<'a> InferenceContext<'a> {
207 self.breakables.push(BreakableContext { 207 self.breakables.push(BreakableContext {
208 may_break: false, 208 may_break: false,
209 break_ty: Ty::Unknown, 209 break_ty: Ty::Unknown,
210 label: label.clone(), 210 label: label.map(|label| self.body[label].name.clone()),
211 }); 211 });
212 let pat_ty = 212 let pat_ty =
213 self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); 213 self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index b70ec55eb..d974f805b 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -243,6 +243,9 @@ impl<'a> InferenceContext<'a> {
243 } 243 }
244 None => Ty::Unknown, 244 None => Ty::Unknown,
245 }, 245 },
246 Pat::ConstBlock(expr) => {
247 self.infer_expr(*expr, &Expectation::has_type(expected.clone()))
248 }
246 Pat::Missing => Ty::Unknown, 249 Pat::Missing => Ty::Unknown,
247 }; 250 };
248 // use a new type variable if we got Ty::Unknown here 251 // use a new type variable if we got Ty::Unknown here
@@ -264,8 +267,9 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
264 | Pat::Range { .. } 267 | Pat::Range { .. }
265 | Pat::Slice { .. } => true, 268 | Pat::Slice { .. } => true,
266 Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)), 269 Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)),
267 // FIXME: Path/Lit might actually evaluate to ref, but inference is unimplemented. 270 // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
268 Pat::Path(..) => true, 271 Pat::Path(..) => true,
272 Pat::ConstBlock(..) => true,
269 Pat::Lit(expr) => match body[*expr] { 273 Pat::Lit(expr) => match body[*expr] {
270 Expr::Literal(Literal::String(..)) => false, 274 Expr::Literal(Literal::String(..)) => false,
271 _ => true, 275 _ => true,
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 8392cb770..8da56cd11 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -675,7 +675,8 @@ impl GenericPredicate {
675 where_predicate: &'a WherePredicate, 675 where_predicate: &'a WherePredicate,
676 ) -> impl Iterator<Item = GenericPredicate> + 'a { 676 ) -> impl Iterator<Item = GenericPredicate> + 'a {
677 match where_predicate { 677 match where_predicate {
678 WherePredicate::TypeBound { target, bound } => { 678 WherePredicate::ForLifetime { target, bound, .. }
679 | WherePredicate::TypeBound { target, bound } => {
679 let self_ty = match target { 680 let self_ty = match target {
680 WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref), 681 WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref),
681 WherePredicateTypeTarget::TypeParam(param_id) => { 682 WherePredicateTypeTarget::TypeParam(param_id) => {
@@ -888,14 +889,13 @@ pub(crate) fn generic_predicates_for_param_query(
888 .where_predicates_in_scope() 889 .where_predicates_in_scope()
889 // we have to filter out all other predicates *first*, before attempting to lower them 890 // we have to filter out all other predicates *first*, before attempting to lower them
890 .filter(|pred| match pred { 891 .filter(|pred| match pred {
891 WherePredicate::TypeBound { 892 WherePredicate::ForLifetime { target, .. }
892 target: WherePredicateTypeTarget::TypeRef(type_ref), 893 | WherePredicate::TypeBound { target, .. } => match target {
893 .. 894 WherePredicateTypeTarget::TypeRef(type_ref) => {
894 } => Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id), 895 Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id)
895 WherePredicate::TypeBound { 896 }
896 target: WherePredicateTypeTarget::TypeParam(local_id), 897 WherePredicateTypeTarget::TypeParam(local_id) => *local_id == param_id.local_id,
897 .. 898 },
898 } => *local_id == param_id.local_id,
899 WherePredicate::Lifetime { .. } => false, 899 WherePredicate::Lifetime { .. } => false,
900 }) 900 })
901 .flat_map(|pred| { 901 .flat_map(|pred| {
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 5a5f48fd0..2053d8f56 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -774,3 +774,33 @@ fn foo(tuple: Tuple) {
774 "#]], 774 "#]],
775 ); 775 );
776} 776}
777
778#[test]
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/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index af880c065..65b79df0d 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -5,7 +5,9 @@ use std::sync::Arc;
5use hir_def::{ 5use hir_def::{
6 adt::VariantData, 6 adt::VariantData,
7 db::DefDatabase, 7 db::DefDatabase,
8 generics::{GenericParams, TypeParamData, TypeParamProvenance, WherePredicateTypeTarget}, 8 generics::{
9 GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
10 },
9 path::Path, 11 path::Path,
10 resolver::{HasResolver, TypeNs}, 12 resolver::{HasResolver, TypeNs},
11 type_ref::TypeRef, 13 type_ref::TypeRef,
@@ -27,7 +29,8 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
27 .where_predicates 29 .where_predicates
28 .iter() 30 .iter()
29 .filter_map(|pred| match pred { 31 .filter_map(|pred| match pred {
30 hir_def::generics::WherePredicate::TypeBound { target, bound } => match target { 32 WherePredicate::ForLifetime { target, bound, .. }
33 | WherePredicate::TypeBound { target, bound } => match target {
31 WherePredicateTypeTarget::TypeRef(TypeRef::Path(p)) 34 WherePredicateTypeTarget::TypeRef(TypeRef::Path(p))
32 if p == &Path::from(name![Self]) => 35 if p == &Path::from(name![Self]) =>
33 { 36 {
@@ -38,7 +41,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
38 } 41 }
39 _ => None, 42 _ => None,
40 }, 43 },
41 hir_def::generics::WherePredicate::Lifetime { .. } => None, 44 WherePredicate::Lifetime { .. } => None,
42 }) 45 })
43 .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { 46 .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) {
44 Some(TypeNs::TraitId(t)) => Some(t), 47 Some(TypeNs::TraitId(t)) => Some(t),
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 049f808dc..038273750 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -248,35 +248,7 @@ mod tests {
248 /// * a diagnostic is produced 248 /// * a diagnostic is produced
249 /// * this diagnostic fix trigger range touches the input cursor position 249 /// * this diagnostic fix trigger range touches the input cursor position
250 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 250 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
251 pub(super) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { 251 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
252 let after = trim_indent(ra_fixture_after);
253
254 let (analysis, file_position) = fixture::position(ra_fixture_before);
255 let diagnostic = analysis
256 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
257 .unwrap()
258 .pop()
259 .unwrap();
260 let mut fix = diagnostic.fix.unwrap();
261 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
262 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
263 let actual = {
264 let mut actual = target_file_contents.to_string();
265 edit.apply(&mut actual);
266 actual
267 };
268
269 assert_eq_text!(&after, &actual);
270 assert!(
271 fix.fix_trigger_range.contains_inclusive(file_position.offset),
272 "diagnostic fix range {:?} does not touch cursor position {:?}",
273 fix.fix_trigger_range,
274 file_position.offset
275 );
276 }
277
278 /// Similar to `check_fix`, but applies all the available fixes.
279 fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) {
280 let after = trim_indent(ra_fixture_after); 252 let after = trim_indent(ra_fixture_after);
281 253
282 let (analysis, file_position) = fixture::position(ra_fixture_before); 254 let (analysis, file_position) = fixture::position(ra_fixture_before);
@@ -286,10 +258,12 @@ mod tests {
286 .pop() 258 .pop()
287 .unwrap(); 259 .unwrap();
288 let fix = diagnostic.fix.unwrap(); 260 let fix = diagnostic.fix.unwrap();
289 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
290 let actual = { 261 let actual = {
291 let mut actual = target_file_contents.to_string(); 262 let file_id = fix.source_change.source_file_edits.first().unwrap().file_id;
263 let mut actual = analysis.file_text(file_id).unwrap().to_string();
264
292 // Go from the last one to the first one, so that ranges won't be affected by previous edits. 265 // Go from the last one to the first one, so that ranges won't be affected by previous edits.
266 // FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/4901#issuecomment-644675309
293 for edit in fix.source_change.source_file_edits.iter().rev() { 267 for edit in fix.source_change.source_file_edits.iter().rev() {
294 edit.edit.apply(&mut actual); 268 edit.edit.apply(&mut actual);
295 } 269 }
@@ -305,29 +279,6 @@ mod tests {
305 ); 279 );
306 } 280 }
307 281
308 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
309 /// which has a fix that can apply to other files.
310 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
311 let ra_fixture_after = &trim_indent(ra_fixture_after);
312 let (analysis, file_pos) = fixture::position(ra_fixture_before);
313 let current_file_id = file_pos.file_id;
314 let diagnostic = analysis
315 .diagnostics(&DiagnosticsConfig::default(), current_file_id)
316 .unwrap()
317 .pop()
318 .unwrap();
319 let mut fix = diagnostic.fix.unwrap();
320 let edit = fix.source_change.source_file_edits.pop().unwrap();
321 let changed_file_id = edit.file_id;
322 let before = analysis.file_text(changed_file_id).unwrap();
323 let actual = {
324 let mut actual = before.to_string();
325 edit.edit.apply(&mut actual);
326 actual
327 };
328 assert_eq_text!(ra_fixture_after, &actual);
329 }
330
331 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 282 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
332 /// apply to the file containing the cursor. 283 /// apply to the file containing the cursor.
333 pub(crate) fn check_no_diagnostics(ra_fixture: &str) { 284 pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
@@ -619,6 +570,7 @@ fn test_fn() {
619 ), 570 ),
620 path: "foo.rs", 571 path: "foo.rs",
621 }, 572 },
573 initial_contents: "",
622 }, 574 },
623 ], 575 ],
624 is_snippet: false, 576 is_snippet: false,
@@ -763,25 +715,25 @@ struct Foo {
763 715
764 #[test] 716 #[test]
765 fn test_add_field_in_other_file_from_usage() { 717 fn test_add_field_in_other_file_from_usage() {
766 check_apply_diagnostic_fix_in_other_file( 718 check_fix(
767 r" 719 r#"
768 //- /main.rs 720//- /main.rs
769 mod foo; 721mod foo;
770 722
771 fn main() { 723fn main() {
772 <|>foo::Foo { bar: 3, baz: false}; 724 foo::Foo { bar: 3, <|>baz: false};
773 } 725}
774 //- /foo.rs 726//- /foo.rs
775 struct Foo { 727struct Foo {
776 bar: i32 728 bar: i32
777 } 729}
778 ", 730"#,
779 r" 731 r#"
780 struct Foo { 732struct Foo {
781 bar: i32, 733 bar: i32,
782 pub(crate) baz: bool 734 pub(crate) baz: bool
783 } 735}
784 ", 736"#,
785 ) 737 )
786 } 738 }
787 739
@@ -801,7 +753,7 @@ struct Foo {
801 753
802 #[test] 754 #[test]
803 fn test_rename_incorrect_case() { 755 fn test_rename_incorrect_case() {
804 check_fixes( 756 check_fix(
805 r#" 757 r#"
806pub struct test_struct<|> { one: i32 } 758pub struct test_struct<|> { one: i32 }
807 759
@@ -818,7 +770,7 @@ pub fn some_fn(val: TestStruct) -> TestStruct {
818"#, 770"#,
819 ); 771 );
820 772
821 check_fixes( 773 check_fix(
822 r#" 774 r#"
823pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { 775pub fn some_fn(NonSnakeCase<|>: u8) -> u8 {
824 NonSnakeCase 776 NonSnakeCase
@@ -831,7 +783,7 @@ pub fn some_fn(non_snake_case: u8) -> u8 {
831"#, 783"#,
832 ); 784 );
833 785
834 check_fixes( 786 check_fix(
835 r#" 787 r#"
836pub fn SomeFn<|>(val: u8) -> u8 { 788pub fn SomeFn<|>(val: u8) -> u8 {
837 if val != 0 { SomeFn(val - 1) } else { val } 789 if val != 0 { SomeFn(val - 1) } else { val }
@@ -844,7 +796,7 @@ pub fn some_fn(val: u8) -> u8 {
844"#, 796"#,
845 ); 797 );
846 798
847 check_fixes( 799 check_fix(
848 r#" 800 r#"
849fn some_fn() { 801fn some_fn() {
850 let whatAWeird_Formatting<|> = 10; 802 let whatAWeird_Formatting<|> = 10;
@@ -873,7 +825,7 @@ fn foo() {
873 825
874 #[test] 826 #[test]
875 fn test_rename_incorrect_case_struct_method() { 827 fn test_rename_incorrect_case_struct_method() {
876 check_fixes( 828 check_fix(
877 r#" 829 r#"
878pub struct TestStruct; 830pub struct TestStruct;
879 831
@@ -894,4 +846,17 @@ impl TestStruct {
894"#, 846"#,
895 ); 847 );
896 } 848 }
849
850 #[test]
851 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
852 let input = r#"fn FOO<|>() {}"#;
853 let expected = r#"fn foo() {}"#;
854
855 let (analysis, file_position) = fixture::position(input);
856 let diagnostics =
857 analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap();
858 assert_eq!(diagnostics.len(), 1);
859
860 check_fix(input, expected);
861 }
897} 862}
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index e8b896623..d79f5c170 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -40,6 +40,7 @@ impl DiagnosticWithFix for UnresolvedModule {
40 anchor: self.file.original_file(sema.db), 40 anchor: self.file.original_file(sema.db),
41 path: self.candidate.clone(), 41 path: self.candidate.clone(),
42 }, 42 },
43 initial_contents: "".to_string(),
43 } 44 }
44 .into(), 45 .into(),
45 unresolved_module.syntax().text_range(), 46 unresolved_module.syntax().text_range(),
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index cd8ec54fa..6431e7d6d 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -5,7 +5,7 @@ use std::fmt;
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 7a12e9965..912144f8b 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -750,6 +750,31 @@ fn test() {
750 } 750 }
751 751
752 #[test] 752 #[test]
753 fn goto_through_included_file() {
754 check(
755 r#"
756//- /main.rs
757#[rustc_builtin_macro]
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#"
@@ -1077,4 +1102,47 @@ fn foo<'foobar>(_: &'foobar ()) {
1077}"#, 1102}"#,
1078 ) 1103 )
1079 } 1104 }
1105
1106 #[test]
1107 #[ignore] // requires the HIR to somehow track these hrtb lifetimes
1108 fn goto_lifetime_hrtb() {
1109 check(
1110 r#"trait Foo<T> {}
1111fn foo<T>() where for<'a> T: Foo<&'a<|> (u8, u16)>, {}
1112 //^^
1113"#,
1114 );
1115 check(
1116 r#"trait Foo<T> {}
1117fn foo<T>() where for<'a<|>> T: Foo<&'a (u8, u16)>, {}
1118 //^^
1119"#,
1120 );
1121 }
1122
1123 #[test]
1124 #[ignore] // requires ForTypes to be implemented
1125 fn goto_lifetime_hrtb_for_type() {
1126 check(
1127 r#"trait Foo<T> {}
1128fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {}
1129 //^^
1130"#,
1131 );
1132 }
1133
1134 #[test]
1135 fn goto_label() {
1136 check(
1137 r#"
1138fn foo<'foo>(_: &'foo ()) {
1139 'foo: {
1140 //^^^^
1141 'bar: loop {
1142 break 'foo<|>;
1143 }
1144 }
1145}"#,
1146 )
1147 }
1080} 1148}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 52f993cc9..73245fbe7 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -370,7 +370,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
370 Adt::Enum(it) => from_def_source(db, it, mod_path), 370 Adt::Enum(it) => from_def_source(db, it, mod_path),
371 }) 371 })
372 } 372 }
373 Definition::TypeParam(_) | Definition::LifetimeParam(_) => { 373 Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => {
374 // FIXME: Hover for generic param 374 // FIXME: Hover for generic param
375 None 375 None
376 } 376 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index dbad9a84f..41eb139d1 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -79,7 +79,7 @@ pub use crate::{
79 HighlightedRange, 79 HighlightedRange,
80 }, 80 },
81}; 81};
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,
@@ -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/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/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/tests.rs b/crates/mbe/src/tests.rs
index f10e7a9b6..451fa1456 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -1004,6 +1004,18 @@ fn test_underscore() {
1004} 1004}
1005 1005
1006#[test] 1006#[test]
1007fn test_vertical_bar_with_pat() {
1008 parse_macro(
1009 r#"
1010 macro_rules! foo {
1011 (| $pat:pat | ) => { 0 }
1012 }
1013 "#,
1014 )
1015 .assert_expand_items(r#"foo! { | x | }"#, r#"0"#);
1016}
1017
1018#[test]
1007fn test_lifetime() { 1019fn test_lifetime() {
1008 parse_macro( 1020 parse_macro(
1009 r#" 1021 r#"
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 1a078f6b4..f08c8bab7 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -55,7 +55,7 @@ pub(crate) mod fragments {
55 use super::*; 55 use super::*;
56 56
57 pub(crate) use super::{ 57 pub(crate) use super::{
58 expressions::block_expr, paths::type_path as path, patterns::pattern, types::type_, 58 expressions::block_expr, paths::type_path as path, patterns::pattern_single, types::type_,
59 }; 59 };
60 60
61 pub(crate) fn expr(p: &mut Parser) { 61 pub(crate) fn expr(p: &mut Parser) {
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 18b63feb7..c7a3556a7 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -46,6 +46,7 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
46 T![continue], 46 T![continue],
47 T![async], 47 T![async],
48 T![try], 48 T![try],
49 T![const],
49 T![loop], 50 T![loop],
50 T![for], 51 T![for],
51 LIFETIME_IDENT, 52 LIFETIME_IDENT,
@@ -115,6 +116,14 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
115 block_expr(p); 116 block_expr(p);
116 m.complete(p, EFFECT_EXPR) 117 m.complete(p, EFFECT_EXPR)
117 } 118 }
119 // test const_block
120 // fn f() { const { } }
121 T![const] if la == T!['{'] => {
122 let m = p.start();
123 p.bump(T![const]);
124 block_expr(p);
125 m.complete(p, EFFECT_EXPR)
126 }
118 T!['{'] => { 127 T!['{'] => {
119 // test for_range_from 128 // test for_range_from
120 // fn foo() { 129 // fn foo() {
@@ -156,11 +165,13 @@ fn tuple_expr(p: &mut Parser) -> CompletedMarker {
156 let mut saw_expr = false; 165 let mut saw_expr = false;
157 while !p.at(EOF) && !p.at(T![')']) { 166 while !p.at(EOF) && !p.at(T![')']) {
158 saw_expr = true; 167 saw_expr = true;
159 if !p.at_ts(EXPR_FIRST) { 168
160 p.error("expected expression"); 169 // test tuple_attrs
170 // const A: (i64, i64) = (1, #[cfg(test)] 2);
171 if !expr_with_attrs(p) {
161 break; 172 break;
162 } 173 }
163 expr(p); 174
164 if !p.at(T![')']) { 175 if !p.at(T![')']) {
165 saw_comma = true; 176 saw_comma = true;
166 p.expect(T![,]); 177 p.expect(T![,]);
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 8999829b4..cf4168d32 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -96,7 +96,10 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
96 let mut has_mods = false; 96 let mut has_mods = false;
97 97
98 // modifiers 98 // modifiers
99 has_mods |= p.eat(T![const]); 99 if p.at(T![const]) && p.nth(1) != T!['{'] {
100 p.eat(T![const]);
101 has_mods = true;
102 }
100 103
101 // test_err async_without_semicolon 104 // test_err async_without_semicolon
102 // fn foo() { let _ = async {} } 105 // fn foo() { let _ = async {} }
@@ -167,7 +170,7 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
167 m.complete(p, TRAIT); 170 m.complete(p, TRAIT);
168 } 171 }
169 172
170 T![const] => { 173 T![const] if p.nth(1) != T!['{'] => {
171 consts::konst(p, m); 174 consts::konst(p, m);
172 } 175 }
173 176
@@ -386,10 +389,15 @@ fn macro_rules(p: &mut Parser, m: Marker) {
386 } 389 }
387 390
388 match p.current() { 391 match p.current() {
389 T!['{'] => { 392 // test macro_rules_non_brace
393 // macro_rules! m ( ($i:ident) => {} );
394 // macro_rules! m [ ($i:ident) => {} ];
395 T!['['] | T!['('] => {
390 token_tree(p); 396 token_tree(p);
397 p.expect(T![;]);
391 } 398 }
392 _ => p.error("expected `{`"), 399 T!['{'] => token_tree(p),
400 _ => p.error("expected `{`, `[`, `(`"),
393 } 401 }
394 m.complete(p, MACRO_RULES); 402 m.complete(p, MACRO_RULES);
395} 403}
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs
index 3ee4e4fca..2d006a1d5 100644
--- a/crates/parser/src/grammar/params.rs
+++ b/crates/parser/src/grammar/params.rs
@@ -47,20 +47,23 @@ fn list_(p: &mut Parser, flavor: Flavor) {
47 if let FnDef = flavor { 47 if let FnDef = flavor {
48 // test self_param_outer_attr 48 // test self_param_outer_attr
49 // fn f(#[must_use] self) {} 49 // fn f(#[must_use] self) {}
50 let m = p.start();
50 attributes::outer_attrs(p); 51 attributes::outer_attrs(p);
51 opt_self_param(p); 52 opt_self_param(p, m);
52 } 53 }
53 54
54 while !p.at(EOF) && !p.at(ket) { 55 while !p.at(EOF) && !p.at(ket) {
55 // test param_outer_arg 56 // test param_outer_arg
56 // fn f(#[attr1] pat: Type) {} 57 // fn f(#[attr1] pat: Type) {}
58 let m = p.start();
57 attributes::outer_attrs(p); 59 attributes::outer_attrs(p);
58 60
59 if !p.at_ts(PARAM_FIRST) { 61 if !p.at_ts(PARAM_FIRST) {
60 p.error("expected value parameter"); 62 p.error("expected value parameter");
63 m.abandon(p);
61 break; 64 break;
62 } 65 }
63 let param = param(p, flavor); 66 let param = param(p, m, flavor);
64 if !p.at(ket) { 67 if !p.at(ket) {
65 p.expect(T![,]); 68 p.expect(T![,]);
66 } 69 }
@@ -77,9 +80,8 @@ const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST);
77 80
78struct Variadic(bool); 81struct Variadic(bool);
79 82
80fn param(p: &mut Parser, flavor: Flavor) -> Variadic { 83fn param(p: &mut Parser, m: Marker, flavor: Flavor) -> Variadic {
81 let mut res = Variadic(false); 84 let mut res = Variadic(false);
82 let m = p.start();
83 match flavor { 85 match flavor {
84 // test param_list_vararg 86 // test param_list_vararg
85 // extern "C" { fn printf(format: *const i8, ...) -> i32; } 87 // extern "C" { fn printf(format: *const i8, ...) -> i32; }
@@ -151,10 +153,8 @@ fn variadic_param(p: &mut Parser) -> bool {
151// fn d(&'a mut self, x: i32) {} 153// fn d(&'a mut self, x: i32) {}
152// fn e(mut self) {} 154// fn e(mut self) {}
153// } 155// }
154fn opt_self_param(p: &mut Parser) { 156fn opt_self_param(p: &mut Parser, m: Marker) {
155 let m;
156 if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { 157 if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] {
157 m = p.start();
158 p.eat(T![mut]); 158 p.eat(T![mut]);
159 p.eat(T![self]); 159 p.eat(T![self]);
160 // test arb_self_types 160 // test arb_self_types
@@ -174,9 +174,8 @@ fn opt_self_param(p: &mut Parser) {
174 (T![&], T![mut], T![self], _) => 3, 174 (T![&], T![mut], T![self], _) => 3,
175 (T![&], LIFETIME_IDENT, T![self], _) => 3, 175 (T![&], LIFETIME_IDENT, T![self], _) => 3,
176 (T![&], LIFETIME_IDENT, T![mut], T![self]) => 4, 176 (T![&], LIFETIME_IDENT, T![mut], T![self]) => 4,
177 _ => return, 177 _ => return m.abandon(p),
178 }; 178 };
179 m = p.start();
180 p.bump_any(); 179 p.bump_any();
181 if p.at(LIFETIME_IDENT) { 180 if p.at(LIFETIME_IDENT) {
182 lifetime(p); 181 lifetime(p);
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 7e7f73dee..b53d5749f 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -89,6 +89,7 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
89 let m = match p.nth(0) { 89 let m = match p.nth(0) {
90 T![box] => box_pat(p), 90 T![box] => box_pat(p),
91 T![ref] | T![mut] => ident_pat(p, true), 91 T![ref] | T![mut] => ident_pat(p, true),
92 T![const] => const_block_pat(p),
92 IDENT => match p.nth(1) { 93 IDENT => match p.nth(1) {
93 // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro 94 // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro
94 // (T![x]). 95 // (T![x]).
@@ -386,3 +387,16 @@ fn box_pat(p: &mut Parser) -> CompletedMarker {
386 pattern_single(p); 387 pattern_single(p);
387 m.complete(p, BOX_PAT) 388 m.complete(p, BOX_PAT)
388} 389}
390
391// test const_block_pat
392// fn main() {
393// let const { 15 } = ();
394// let const { foo(); bar() } = ();
395// }
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/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs
index 9c3f7c28a..4aeccd193 100644
--- a/crates/parser/src/grammar/type_params.rs
+++ b/crates/parser/src/grammar/type_params.rs
@@ -113,7 +113,7 @@ fn type_bound(p: &mut Parser) -> bool {
113 p.eat(T![?]); 113 p.eat(T![?]);
114 match p.current() { 114 match p.current() {
115 LIFETIME_IDENT => lifetime(p), 115 LIFETIME_IDENT => lifetime(p),
116 T![for] => types::for_type(p), 116 T![for] => types::for_type(p, false),
117 _ if paths::is_use_path_start(p) => types::path_type_(p, false), 117 _ if paths::is_use_path_start(p) => types::path_type_(p, false),
118 _ => { 118 _ => {
119 m.abandon(p); 119 m.abandon(p);
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index 36a15eace..94cbf7d85 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -44,7 +44,7 @@ fn type_with_bounds_cond(p: &mut Parser, allow_bounds: bool) {
44 T![&] => ref_type(p), 44 T![&] => ref_type(p),
45 T![_] => infer_type(p), 45 T![_] => infer_type(p),
46 T![fn] | T![unsafe] | T![extern] => fn_ptr_type(p), 46 T![fn] | T![unsafe] | T![extern] => fn_ptr_type(p),
47 T![for] => for_type(p), 47 T![for] => for_type(p, allow_bounds),
48 T![impl] => impl_trait_type(p), 48 T![impl] => impl_trait_type(p),
49 T![dyn] => dyn_trait_type(p), 49 T![dyn] => dyn_trait_type(p),
50 // Some path types are not allowed to have bounds (no plus) 50 // Some path types are not allowed to have bounds (no plus)
@@ -227,7 +227,7 @@ pub(super) fn for_binder(p: &mut Parser) {
227// type A = for<'a> fn() -> (); 227// type A = for<'a> fn() -> ();
228// type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); 228// type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
229// type Obj = for<'a> PartialEq<&'a i32>; 229// type Obj = for<'a> PartialEq<&'a i32>;
230pub(super) fn for_type(p: &mut Parser) { 230pub(super) fn for_type(p: &mut Parser, allow_bounds: bool) {
231 assert!(p.at(T![for])); 231 assert!(p.at(T![for]));
232 let m = p.start(); 232 let m = p.start();
233 for_binder(p); 233 for_binder(p);
@@ -240,7 +240,13 @@ pub(super) fn for_type(p: &mut Parser) {
240 } 240 }
241 } 241 }
242 type_no_bounds(p); 242 type_no_bounds(p);
243 m.complete(p, FOR_TYPE); 243 let completed = m.complete(p, FOR_TYPE);
244
245 // test no_dyn_trait_leading_for
246 // type A = for<'a> Test<'a> + Send;
247 if allow_bounds {
248 opt_type_bounds_as_dyn_trait_type(p, completed);
249 }
244} 250}
245 251
246// test impl_trait_type 252// test impl_trait_type
@@ -290,7 +296,7 @@ fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) {
290 let path = m.complete(p, kind); 296 let path = m.complete(p, kind);
291 297
292 if allow_bounds { 298 if allow_bounds {
293 opt_path_type_bounds_as_dyn_trait_type(p, path); 299 opt_type_bounds_as_dyn_trait_type(p, path);
294 } 300 }
295} 301}
296 302
@@ -304,19 +310,23 @@ pub(super) fn path_type_(p: &mut Parser, allow_bounds: bool) {
304 // fn foo() -> Box<dyn T + 'f> {} 310 // fn foo() -> Box<dyn T + 'f> {}
305 let path = m.complete(p, PATH_TYPE); 311 let path = m.complete(p, PATH_TYPE);
306 if allow_bounds { 312 if allow_bounds {
307 opt_path_type_bounds_as_dyn_trait_type(p, path); 313 opt_type_bounds_as_dyn_trait_type(p, path);
308 } 314 }
309} 315}
310 316
311/// This turns a parsed PATH_TYPE optionally into a DYN_TRAIT_TYPE 317/// This turns a parsed PATH_TYPE or FOR_TYPE optionally into a DYN_TRAIT_TYPE
312/// with a TYPE_BOUND_LIST 318/// with a TYPE_BOUND_LIST
313fn opt_path_type_bounds_as_dyn_trait_type(p: &mut Parser, path_type_marker: CompletedMarker) { 319fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) {
320 assert!(matches!(
321 type_marker.kind(),
322 SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_CALL
323 ));
314 if !p.at(T![+]) { 324 if !p.at(T![+]) {
315 return; 325 return;
316 } 326 }
317 327
318 // First create a TYPE_BOUND from the completed PATH_TYPE 328 // First create a TYPE_BOUND from the completed PATH_TYPE
319 let m = path_type_marker.precede(p).complete(p, TYPE_BOUND); 329 let m = type_marker.precede(p).complete(p, TYPE_BOUND);
320 330
321 // Next setup a marker for the TYPE_BOUND_LIST 331 // Next setup a marker for the TYPE_BOUND_LIST
322 let m = m.precede(p); 332 let m = m.precede(p);
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index ab8e4c70e..811e740f9 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -112,7 +112,7 @@ pub fn parse_fragment(
112 FragmentKind::Path => grammar::fragments::path, 112 FragmentKind::Path => grammar::fragments::path,
113 FragmentKind::Expr => grammar::fragments::expr, 113 FragmentKind::Expr => grammar::fragments::expr,
114 FragmentKind::Type => grammar::fragments::type_, 114 FragmentKind::Type => grammar::fragments::type_,
115 FragmentKind::Pattern => grammar::fragments::pattern, 115 FragmentKind::Pattern => grammar::fragments::pattern_single,
116 FragmentKind::Item => grammar::fragments::item, 116 FragmentKind::Item => grammar::fragments::item,
117 FragmentKind::Block => grammar::fragments::block_expr, 117 FragmentKind::Block => grammar::fragments::block_expr,
118 FragmentKind::Visibility => grammar::fragments::opt_visibility, 118 FragmentKind::Visibility => grammar::fragments::opt_visibility,
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 980aa5979..f69e71bdb 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -170,6 +170,7 @@ pub enum SyntaxKind {
170 RANGE_PAT, 170 RANGE_PAT,
171 LITERAL_PAT, 171 LITERAL_PAT,
172 MACRO_PAT, 172 MACRO_PAT,
173 CONST_BLOCK_PAT,
173 TUPLE_EXPR, 174 TUPLE_EXPR,
174 ARRAY_EXPR, 175 ARRAY_EXPR,
175 PAREN_EXPR, 176 PAREN_EXPR,
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml
index 1bfa6c3fc..df9a55c10 100644
--- a/crates/proc_macro_srv/Cargo.toml
+++ b/crates/proc_macro_srv/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
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/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 0b4d3f4eb..53e70eaf7 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -28,6 +28,7 @@ oorandom = "11.1.2"
28rustc-hash = "1.1.0" 28rustc-hash = "1.1.0"
29serde = { version = "1.0.106", features = ["derive"] } 29serde = { version = "1.0.106", features = ["derive"] }
30serde_json = { version = "1.0.48", features = ["preserve_order"] } 30serde_json = { version = "1.0.48", features = ["preserve_order"] }
31serde_path_to_error = "0.1"
31threadpool = "1.7.1" 32threadpool = "1.7.1"
32rayon = "1.5" 33rayon = "1.5"
33mimalloc = { version = "0.1.19", default-features = false, optional = true } 34mimalloc = { 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 66f8bee99..374fb5302 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;
@@ -400,6 +402,45 @@ pub(crate) fn handle_workspace_symbol(
400 } 402 }
401} 403}
402 404
405pub(crate) fn handle_will_rename_files(
406 snap: GlobalStateSnapshot,
407 params: lsp_types::RenameFilesParams,
408) -> Result<Option<lsp_types::WorkspaceEdit>> {
409 let _p = profile::span("handle_will_rename_files");
410
411 let source_changes: Vec<SourceChange> = params
412 .files
413 .into_iter()
414 .filter_map(|file_rename| {
415 let from = Url::parse(&file_rename.old_uri).ok()?;
416 let to = Url::parse(&file_rename.new_uri).ok()?;
417
418 let from_path = from.to_file_path().ok()?;
419 let to_path = to.to_file_path().ok()?;
420
421 // Limit to single-level moves for now.
422 match (from_path.parent(), to_path.parent()) {
423 (Some(p1), Some(p2)) if p1 == p2 => {
424 let new_name = to_path.file_stem()?;
425 let new_name = new_name.to_str()?;
426 Some((snap.url_to_file_id(&from).ok()?, new_name.to_string()))
427 }
428 _ => None,
429 }
430 })
431 .filter_map(|(file_id, new_name)| {
432 snap.analysis.will_rename_file(file_id, &new_name).ok()?
433 })
434 .collect();
435
436 // Drop file system edits since we're just renaming things on the same level
437 let edits = source_changes.into_iter().map(|it| it.source_file_edits).flatten().collect();
438 let source_change = SourceChange::from_edits(edits, Vec::new());
439
440 let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
441 Ok(Some(workspace_edit))
442}
443
403pub(crate) fn handle_goto_definition( 444pub(crate) fn handle_goto_definition(
404 snap: GlobalStateSnapshot, 445 snap: GlobalStateSnapshot,
405 params: lsp_types::GotoDefinitionParams, 446 params: lsp_types::GotoDefinitionParams,
@@ -865,58 +906,8 @@ pub(crate) fn handle_formatting(
865 } 906 }
866} 907}
867 908
868fn handle_fixes(
869 snap: &GlobalStateSnapshot,
870 params: &lsp_types::CodeActionParams,
871 res: &mut Vec<lsp_ext::CodeAction>,
872) -> Result<()> {
873 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
874 let line_index = snap.analysis.file_line_index(file_id)?;
875 let range = from_proto::text_range(&line_index, params.range);
876
877 match &params.context.only {
878 Some(v) => {
879 if !v.iter().any(|it| {
880 it == &lsp_types::CodeActionKind::EMPTY
881 || it == &lsp_types::CodeActionKind::QUICKFIX
882 }) {
883 return Ok(());
884 }
885 }
886 None => {}
887 };
888
889 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, file_id)?;
890
891 for fix in diagnostics
892 .into_iter()
893 .filter_map(|d| d.fix)
894 .filter(|fix| fix.fix_trigger_range.intersect(range).is_some())
895 {
896 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
897 let action = lsp_ext::CodeAction {
898 title: fix.label.to_string(),
899 group: None,
900 kind: Some(CodeActionKind::QUICKFIX),
901 edit: Some(edit),
902 is_preferred: Some(false),
903 data: None,
904 };
905 res.push(action);
906 }
907
908 for fix in snap.check_fixes.get(&file_id).into_iter().flatten() {
909 let fix_range = from_proto::text_range(&line_index, fix.range);
910 if fix_range.intersect(range).is_none() {
911 continue;
912 }
913 res.push(fix.action.clone());
914 }
915 Ok(())
916}
917
918pub(crate) fn handle_code_action( 909pub(crate) fn handle_code_action(
919 mut snap: GlobalStateSnapshot, 910 snap: GlobalStateSnapshot,
920 params: lsp_types::CodeActionParams, 911 params: lsp_types::CodeActionParams,
921) -> Result<Option<Vec<lsp_ext::CodeAction>>> { 912) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
922 let _p = profile::span("handle_code_action"); 913 let _p = profile::span("handle_code_action");
@@ -932,24 +923,35 @@ pub(crate) fn handle_code_action(
932 let range = from_proto::text_range(&line_index, params.range); 923 let range = from_proto::text_range(&line_index, params.range);
933 let frange = FileRange { file_id, range }; 924 let frange = FileRange { file_id, range };
934 925
935 snap.config.assist.allowed = params 926 let assists_config = AssistConfig {
936 .clone() 927 allowed: params
937 .context 928 .clone()
938 .only 929 .context
939 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); 930 .only
931 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()),
932 ..snap.config.assist
933 };
940 934
941 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 935 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
942 936
943 handle_fixes(&snap, &params, &mut res)?; 937 let include_quick_fixes = match &params.context.only {
938 Some(v) => v.iter().any(|it| {
939 it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX
940 }),
941 None => true,
942 };
943 if include_quick_fixes {
944 add_quick_fixes(&snap, frange, &line_index, &mut res)?;
945 }
944 946
945 if snap.config.client_caps.code_action_resolve { 947 if snap.config.client_caps.code_action_resolve {
946 for (index, assist) in 948 for (index, assist) in
947 snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate() 949 snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate()
948 { 950 {
949 res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); 951 res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
950 } 952 }
951 } else { 953 } else {
952 for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() { 954 for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() {
953 res.push(to_proto::resolved_code_action(&snap, assist)?); 955 res.push(to_proto::resolved_code_action(&snap, assist)?);
954 } 956 }
955 } 957 }
@@ -957,6 +959,40 @@ pub(crate) fn handle_code_action(
957 Ok(Some(res)) 959 Ok(Some(res))
958} 960}
959 961
962fn add_quick_fixes(
963 snap: &GlobalStateSnapshot,
964 frange: FileRange,
965 line_index: &Arc<LineIndex>,
966 acc: &mut Vec<lsp_ext::CodeAction>,
967) -> Result<()> {
968 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, frange.file_id)?;
969
970 for fix in diagnostics
971 .into_iter()
972 .filter_map(|d| d.fix)
973 .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some())
974 {
975 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
976 let action = lsp_ext::CodeAction {
977 title: fix.label.to_string(),
978 group: None,
979 kind: Some(CodeActionKind::QUICKFIX),
980 edit: Some(edit),
981 is_preferred: Some(false),
982 data: None,
983 };
984 acc.push(action);
985 }
986
987 for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
988 let fix_range = from_proto::text_range(&line_index, fix.range);
989 if fix_range.intersect(frange.range).is_some() {
990 acc.push(fix.action.clone());
991 }
992 }
993 Ok(())
994}
995
960pub(crate) fn handle_code_action_resolve( 996pub(crate) fn handle_code_action_resolve(
961 mut snap: GlobalStateSnapshot, 997 mut snap: GlobalStateSnapshot,
962 mut code_action: lsp_ext::CodeAction, 998 mut code_action: lsp_ext::CodeAction,
@@ -978,11 +1014,11 @@ pub(crate) fn handle_code_action_resolve(
978 .only 1014 .only
979 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); 1015 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
980 1016
981 let assists = snap.analysis.resolved_assists(&snap.config.assist, frange)?; 1017 let assists = snap.analysis.assists(&snap.config.assist, true, frange)?;
982 let (id, index) = split_once(&params.id, ':').unwrap(); 1018 let (id, index) = split_once(&params.id, ':').unwrap();
983 let index = index.parse::<usize>().unwrap(); 1019 let index = index.parse::<usize>().unwrap();
984 let assist = &assists[index]; 1020 let assist = &assists[index];
985 assert!(assist.assist.id.0 == id); 1021 assert!(assist.id.0 == id);
986 let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; 1022 let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit;
987 code_action.edit = edit; 1023 code_action.edit = edit;
988 Ok(code_action) 1024 Ok(code_action)
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 79fe30e53..d538ad69a 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -46,7 +46,7 @@ pub type Error = Box<dyn std::error::Error + Send + Sync>;
46pub type Result<T, E = Error> = std::result::Result<T, E>; 46pub type Result<T, E = Error> = std::result::Result<T, E>;
47 47
48pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { 48pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> {
49 let res = T::deserialize(&json) 49 let res = serde_path_to_error::deserialize(&json)
50 .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?; 50 .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?;
51 Ok(res) 51 Ok(res)
52} 52}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ec3d5e060..5d55dc96e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -485,6 +485,7 @@ impl GlobalState {
485 .on::<lsp_types::request::SemanticTokensRangeRequest>( 485 .on::<lsp_types::request::SemanticTokensRangeRequest>(
486 handlers::handle_semantic_tokens_range, 486 handlers::handle_semantic_tokens_range,
487 ) 487 )
488 .on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
488 .on::<lsp_ext::Ssr>(handlers::handle_ssr) 489 .on::<lsp_ext::Ssr>(handlers::handle_ssr)
489 .finish(); 490 .finish();
490 Ok(()) 491 Ok(())
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 1daad1c98..c2f6a655d 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -45,6 +45,7 @@ define_semantic_token_types![
45 (FORMAT_SPECIFIER, "formatSpecifier"), 45 (FORMAT_SPECIFIER, "formatSpecifier"),
46 (GENERIC, "generic"), 46 (GENERIC, "generic"),
47 (LIFETIME, "lifetime"), 47 (LIFETIME, "lifetime"),
48 (LABEL, "label"),
48 (PUNCTUATION, "punctuation"), 49 (PUNCTUATION, "punctuation"),
49 (SELF_KEYWORD, "selfKeyword"), 50 (SELF_KEYWORD, "selfKeyword"),
50 (TYPE_ALIAS, "typeAlias"), 51 (TYPE_ALIAS, "typeAlias"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index e0561b5a7..1a38e79f0 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -8,8 +8,8 @@ use ide::{
8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, 8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId,
9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, 9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag,
10 HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, 10 HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup,
11 NavigationTarget, ReferenceAccess, ResolvedAssist, Runnable, Severity, SourceChange, 11 NavigationTarget, ReferenceAccess, Runnable, Severity, SourceChange, SourceFileEdit,
12 SourceFileEdit, SymbolKind, TextEdit, TextRange, TextSize, 12 SymbolKind, TextEdit, TextRange, TextSize,
13}; 13};
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/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index c6a6f11e1..21015591c 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.9.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/test_data/parser/inline/ok/0138_self_param_outer_attr.rast b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
index 6403ff8d5..d3219f0b2 100644
--- a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
@@ -6,16 +6,16 @@ [email protected]
6 [email protected] "f" 6 [email protected] "f"
7 [email protected] 7 [email protected]
8 [email protected] "(" 8 [email protected] "("
9 ATT[email protected]6 9 SELF_PARAM@5..21
10 POUND@5..6 "#" 10 ATTR@5..16
11 L_BRACK@6..7 "[" 11 POUND@5..6 "#"
12 PATH@7..15 12 L_BRACK@6..7 "["
13 PATH_SEGMENT@7..15 13 [email protected]
14 NAME_REF@7..15 14 PATH_SEGMENT@7..15
15 IDENT@7..15 "must_use" 15 NAME_REF@7..15
16 R_BRACK@15..16 "]" 16 IDENT@7..15 "must_use"
17 WHITESPACE@16..17 " " 17 R_BRACK@15..16 "]"
18 SELF_PARAM@17..21 18 WHITESPACE@16..17 " "
19 [email protected] "self" 19 [email protected] "self"
20 [email protected] ")" 20 [email protected] ")"
21 [email protected] " " 21 [email protected] " "
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast
new file mode 100644
index 000000000..edfcb288c
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast
@@ -0,0 +1,43 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "A"
7 [email protected] " "
8 [email protected] "="
9 [email protected] " "
10 [email protected]
11 [email protected]
12 [email protected]
13 [email protected]
14 [email protected] "for"
15 [email protected]
16 [email protected] "<"
17 [email protected]
18 [email protected]
19 [email protected] "\'a"
20 [email protected] ">"
21 [email protected] " "
22 [email protected]
23 [email protected]
24 [email protected]
25 [email protected]
26 [email protected] "Test"
27 [email protected]
28 [email protected] "<"
29 [email protected]
30 [email protected]
31 [email protected] "\'a"
32 [email protected] ">"
33 [email protected] " "
34 [email protected] "+"
35 [email protected] " "
36 [email protected]
37 [email protected]
38 [email protected]
39 [email protected]
40 [email protected]
41 [email protected] "Send"
42 [email protected] ";"
43 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs
new file mode 100644
index 000000000..47a71fd19
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs
@@ -0,0 +1 @@
type A = for<'a> Test<'a> + Send;
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast
new file mode 100644
index 000000000..d34b21abe
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast
@@ -0,0 +1,50 @@
1[email protected]
2 [email protected]
3 [email protected] "const"
4 [email protected] " "
5 [email protected]
6 [email protected] "A"
7 [email protected] ":"
8 [email protected] " "
9 [email protected]
10 [email protected] "("
11 [email protected]
12 [email protected]
13 [email protected]
14 [email protected]
15 [email protected] "i64"
16 [email protected] ","
17 [email protected] " "
18 [email protected]
19 [email protected]
20 [email protected]
21 [email protected]
22 [email protected] "i64"
23 [email protected] ")"
24 [email protected] " "
25 [email protected] "="
26 [email protected] " "
27 [email protected]
28 [email protected] "("
29 [email protected]
30 [email protected] "1"
31 [email protected] ","
32 [email protected] " "
33 [email protected]
34 [email protected]
35 [email protected] "#"
36 [email protected] "["
37 [email protected]
38 [email protected]
39 [email protected]
40 [email protected] "cfg"
41 [email protected]
42 [email protected] "("
43 [email protected] "test"
44 [email protected] ")"
45 [email protected] "]"
46 [email protected] " "
47 [email protected] "2"
48 [email protected] ")"
49 [email protected] ";"
50 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs
new file mode 100644
index 000000000..f84b7ab31
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs
@@ -0,0 +1 @@
const A: (i64, i64) = (1, #[cfg(test)] 2);
diff --git a/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rast b/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rast
new file mode 100644
index 000000000..8ff4822c4
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rast
@@ -0,0 +1,76 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "main"
7 [email protected]
8 [email protected] "("
9 [email protected] ")"
10 [email protected] " "
11 [email protected]
12 [email protected] "{"
13 [email protected] "\n "
14 [email protected]
15 [email protected] "let"
16 [email protected] " "
17 [email protected]
18 [email protected] "const"
19 [email protected] " "
20 [email protected]
21 [email protected] "{"
22 [email protected] " "
23 [email protected]
24 [email protected] "15"
25 [email protected] " "
26 [email protected] "}"
27 [email protected] " "
28 [email protected] "="
29 [email protected] " "
30 [email protected]
31 [email protected] "("
32 [email protected] ")"
33 [email protected] ";"
34 [email protected] "\n "
35 [email protected]
36 [email protected] "let"
37 [email protected] " "
38 [email protected]
39 [email protected] "const"
40 [email protected] " "
41 [email protected]
42 [email protected] "{"
43 [email protected] " "
44 [email protected]
45 [email protected]
46 [email protected]
47 [email protected]
48 [email protected]
49 [email protected]
50 [email protected] "foo"
51 [email protected]
52 [email protected] "("
53 [email protected] ")"
54 [email protected] ";"
55 [email protected] " "
56 [email protected]
57 [email protected]
58 [email protected]
59 [email protected]
60 [email protected]
61 [email protected] "bar"
62 [email protected]
63 [email protected] "("
64 [email protected] ")"
65 [email protected] " "
66 [email protected] "}"
67 [email protected] " "
68 [email protected] "="
69 [email protected] " "
70 [email protected]
71 [email protected] "("
72 [email protected] ")"
73 [email protected] ";"
74 [email protected] "\n"
75 [email protected] "}"
76 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rs b/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rs
new file mode 100644
index 000000000..dce9defac
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0156_const_block_pat.rs
@@ -0,0 +1,4 @@
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/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
index 8974f9e40..3fed11838 100644
--- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
@@ -107,16 +107,16 @@ [email protected]
107 [email protected] "i8" 107 [email protected] "i8"
108 [email protected] "," 108 [email protected] ","
109 [email protected] " " 109 [email protected] " "
110 ATT[email protected]3 110 PARAM@106..117
111 POUND@106..107 "#" 111 ATTR@106..113
112 L_BRACK@107..108 "[" 112 POUND@106..107 "#"
113 PATH@108..112 113 L_BRACK@107..108 "["
114 PATH_SEGMENT@108..112 114 [email protected]
115 NAME_REF@108..112 115 PATH_SEGMENT@108..112
116 IDENT@108..112 "attr" 116 NAME_REF@108..112
117 R_BRACK@112..113 "]" 117 IDENT@108..112 "attr"
118 WHITESPACE@113..114 " " 118 R_BRACK@112..113 "]"
119 PARAM@114..117 119 WHITESPACE@113..114 " "
120 [email protected] "..." 120 [email protected] "..."
121 [email protected] ")" 121 [email protected] ")"
122 [email protected] " " 122 [email protected] " "
@@ -153,16 +153,16 @@ [email protected]
153 [email protected] "FnMut" 153 [email protected] "FnMut"
154 [email protected] 154 [email protected]
155 [email protected] "(" 155 [email protected] "("
156 ATT[email protected]53 156 PARAM@146..166
157 POUND@146..147 "#" 157 ATTR@146..153
158 L_BRACK@147..148 "[" 158 POUND@146..147 "#"
159 PATH@148..152 159 L_BRACK@147..148 "["
160 PATH_SEGMENT@148..152 160 [email protected]
161 NAME_REF@148..152 161 PATH_SEGMENT@148..152
162 IDENT@148..152 "attr" 162 NAME_REF@148..152
163 R_BRACK@152..153 "]" 163 IDENT@148..152 "attr"
164 WHITESPACE@153..154 " " 164 R_BRACK@152..153 "]"
165 PARAM@154..166 165 WHITESPACE@153..154 " "
166 [email protected] 166 [email protected]
167 [email protected] "&" 167 [email protected] "&"
168 [email protected] "mut" 168 [email protected] "mut"
@@ -224,17 +224,17 @@ [email protected]
224 [email protected] "u64" 224 [email protected] "u64"
225 [email protected] "," 225 [email protected] ","
226 [email protected] " " 226 [email protected] " "
227 ATT[email protected]1 227 PARAM@213..232
228 POUND@213..214 "#" 228 ATTR@213..221
229 WHITESPACE@214..215 " " 229 POUND@213..214 "#"
230 L_BRACK@215..216 "[" 230 WHITESPACE@214..215 " "
231 PATH@216..220 231 L_BRACK@215..216 "["
232 PATH_SEGMENT@216..220 232 [email protected]
233 NAME_REF@216..220 233 PATH_SEGMENT@216..220
234 IDENT@216..220 "attr" 234 NAME_REF@216..220
235 R_BRACK@220..221 "]" 235 IDENT@216..220 "attr"
236 WHITESPACE@221..222 " " 236 R_BRACK@220..221 "]"
237 PARAM@222..232 237 WHITESPACE@221..222 " "
238 [email protected] 238 [email protected]
239 [email protected] "mut" 239 [email protected] "mut"
240 [email protected] " " 240 [email protected] " "
@@ -271,16 +271,16 @@ [email protected]
271 [email protected] "f" 271 [email protected] "f"
272 [email protected] 272 [email protected]
273 [email protected] "(" 273 [email protected] "("
274 ATT[email protected]68 274 SELF_PARAM@257..273
275 POUND@257..258 "#" 275 ATTR@257..268
276 L_BRACK@258..259 "[" 276 POUND@257..258 "#"
277 PATH@259..267 277 L_BRACK@258..259 "["
278 PATH_SEGMENT@259..267 278 [email protected]
279 NAME_REF@259..267 279 PATH_SEGMENT@259..267
280 IDENT@259..267 "must_use" 280 NAME_REF@259..267
281 R_BRACK@267..268 "]" 281 IDENT@259..267 "must_use"
282 WHITESPACE@268..269 " " 282 R_BRACK@267..268 "]"
283 SELF_PARAM@269..273 283 WHITESPACE@268..269 " "
284 [email protected] "self" 284 [email protected] "self"
285 [email protected] ")" 285 [email protected] ")"
286 [email protected] " " 286 [email protected] " "
@@ -295,16 +295,16 @@ [email protected]
295 [email protected] "g1" 295 [email protected] "g1"
296 [email protected] 296 [email protected]
297 [email protected] "(" 297 [email protected] "("
298 ATTR@289..296 298 SELF_PARAM@289..301
299 POUND@289..290 "#" 299 ATTR@289..296
300 L_BRACK@290..291 "[" 300 POUND@289..290 "#"
301 PATH@291..295 301 L_BRACK@290..291 "["
302 PATH_SEGMENT@291..295 302 [email protected]
303 NAME_REF@291..295 303 PATH_SEGMENT@291..295
304 IDENT@291..295 "attr" 304 NAME_REF@291..295
305 R_BRACK@295..296 "]" 305 IDENT@291..295 "attr"
306 WHITESPACE@296..297 " " 306 R_BRACK@295..296 "]"
307 SELF_PARAM@297..301 307 WHITESPACE@296..297 " "
308 [email protected] "self" 308 [email protected] "self"
309 [email protected] ")" 309 [email protected] ")"
310 [email protected] " " 310 [email protected] " "
@@ -319,16 +319,16 @@ [email protected]
319 [email protected] "g2" 319 [email protected] "g2"
320 [email protected] 320 [email protected]
321 [email protected] "(" 321 [email protected] "("
322 ATT[email protected]24 322 SELF_PARAM@317..330
323 POUND@317..318 "#" 323 ATTR@317..324
324 L_BRACK@318..319 "[" 324 POUND@317..318 "#"
325 PATH@319..323 325 L_BRACK@318..319 "["
326 PATH_SEGMENT@319..323 326 [email protected]
327 NAME_REF@319..323 327 PATH_SEGMENT@319..323
328 IDENT@319..323 "attr" 328 NAME_REF@319..323
329 R_BRACK@323..324 "]" 329 IDENT@319..323 "attr"
330 WHITESPACE@324..325 " " 330 R_BRACK@323..324 "]"
331 SELF_PARAM@325..330 331 WHITESPACE@324..325 " "
332 [email protected] "&" 332 [email protected] "&"
333 [email protected] "self" 333 [email protected] "self"
334 [email protected] ")" 334 [email protected] ")"
@@ -350,16 +350,16 @@ [email protected]
350 [email protected] ">" 350 [email protected] ">"
351 [email protected] 351 [email protected]
352 [email protected] "(" 352 [email protected] "("
353 ATT[email protected]57 353 SELF_PARAM@350..367
354 POUND@350..351 "#" 354 ATTR@350..357
355 L_BRACK@351..352 "[" 355 POUND@350..351 "#"
356 PATH@352..356 356 L_BRACK@351..352 "["
357 PATH_SEGMENT@352..356 357 [email protected]
358 NAME_REF@352..356 358 PATH_SEGMENT@352..356
359 IDENT@352..356 "attr" 359 NAME_REF@352..356
360 R_BRACK@356..357 "]" 360 IDENT@352..356 "attr"
361 WHITESPACE@357..358 " " 361 R_BRACK@356..357 "]"
362 SELF_PARAM@358..367 362 WHITESPACE@357..358 " "
363 [email protected] "&" 363 [email protected] "&"
364 [email protected] "mut" 364 [email protected] "mut"
365 [email protected] " " 365 [email protected] " "
@@ -383,16 +383,16 @@ [email protected]
383 [email protected] ">" 383 [email protected] ">"
384 [email protected] 384 [email protected]
385 [email protected] "(" 385 [email protected] "("
386 ATTR@387..394 386 SELF_PARAM@387..403
387 POUND@387..388 "#" 387 ATTR@387..394
388 L_BRACK@388..389 "[" 388 POUND@387..388 "#"
389 PATH@389..393 389 L_BRACK@388..389 "["
390 PATH_SEGMENT@389..393 390 [email protected]
391 NAME_REF@389..393 391 PATH_SEGMENT@389..393
392 IDENT@389..393 "attr" 392 NAME_REF@389..393
393 R_BRACK@393..394 "]" 393 IDENT@389..393 "attr"
394 WHITESPACE@394..395 " " 394 R_BRACK@393..394 "]"
395 SELF_PARAM@395..403 395 WHITESPACE@394..395 " "
396 [email protected] "&" 396 [email protected] "&"
397 [email protected] 397 [email protected]
398 [email protected] "\'a" 398 [email protected] "\'a"
@@ -417,16 +417,16 @@ [email protected]
417 [email protected] ">" 417 [email protected] ">"
418 [email protected] 418 [email protected]
419 [email protected] "(" 419 [email protected] "("
420 ATT[email protected]0 420 SELF_PARAM@423..443
421 POUND@423..424 "#" 421 ATTR@423..430
422 L_BRACK@424..425 "[" 422 POUND@423..424 "#"
423 PATH@425..429 423 L_BRACK@424..425 "["
424 PATH_SEGMENT@425..429 424 [email protected]
425 NAME_REF@425..429 425 PATH_SEGMENT@425..429
426 IDENT@425..429 "attr" 426 NAME_REF@425..429
427 R_BRACK@429..430 "]" 427 IDENT@425..429 "attr"
428 WHITESPACE@430..431 " " 428 R_BRACK@429..430 "]"
429 SELF_PARAM@431..443 429 WHITESPACE@430..431 " "
430 [email protected] "&" 430 [email protected] "&"
431 [email protected] 431 [email protected]
432 [email protected] "\'a" 432 [email protected] "\'a"
@@ -447,16 +447,16 @@ [email protected]
447 [email protected] "c" 447 [email protected] "c"
448 [email protected] 448 [email protected]
449 [email protected] "(" 449 [email protected] "("
450 ATT[email protected]5 450 SELF_PARAM@458..476
451 POUND@458..459 "#" 451 ATTR@458..465
452 L_BRACK@459..460 "[" 452 POUND@458..459 "#"
453 PATH@460..464 453 L_BRACK@459..460 "["
454 PATH_SEGMENT@460..464 454 [email protected]
455 NAME_REF@460..464 455 PATH_SEGMENT@460..464
456 IDENT@460..464 "attr" 456 NAME_REF@460..464
457 R_BRACK@464..465 "]" 457 IDENT@460..464 "attr"
458 WHITESPACE@465..466 " " 458 R_BRACK@464..465 "]"
459 SELF_PARAM@466..476 459 WHITESPACE@465..466 " "
460 [email protected] "self" 460 [email protected] "self"
461 [email protected] ":" 461 [email protected] ":"
462 [email protected] " " 462 [email protected] " "
@@ -478,16 +478,16 @@ [email protected]
478 [email protected] "d" 478 [email protected] "d"
479 [email protected] 479 [email protected]
480 [email protected] "(" 480 [email protected] "("
481 ATTR@491..498 481 SELF_PARAM@491..513
482 POUND@491..492 "#" 482 ATTR@491..498
483 L_BRACK@492..493 "[" 483 POUND@491..492 "#"
484 PATH@493..497 484 L_BRACK@492..493 "["
485 PATH_SEGMENT@493..497 485 [email protected]
486 NAME_REF@493..497 486 PATH_SEGMENT@493..497
487 IDENT@493..497 "attr" 487 NAME_REF@493..497
488 R_BRACK@497..498 "]" 488 IDENT@493..497 "attr"
489 WHITESPACE@498..499 " " 489 R_BRACK@497..498 "]"
490 SELF_PARAM@499..513 490 WHITESPACE@498..499 " "
491 [email protected] "self" 491 [email protected] "self"
492 [email protected] ":" 492 [email protected] ":"
493 [email protected] " " 493 [email protected] " "
diff --git a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
index 4009b3ff8..f7c094898 100644
--- a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
+++ b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
@@ -92,20 +92,20 @@ [email protected]
92 [email protected] "u8" 92 [email protected] "u8"
93 [email protected] "," 93 [email protected] ","
94 [email protected] " " 94 [email protected] " "
95 ATT[email protected]5 95 PARAM@92..120
96 POUND@92..93 "#" 96 ATTR@92..105
97 L_BRACK@93..94 "[" 97 POUND@92..93 "#"
98 PATH@94..97 98 L_BRACK@93..94 "["
99 PATH_SEGMENT@94..97 99 [email protected]
100 NAME_REF@94..97 100 PATH_SEGMENT@94..97
101 IDENT@94..97 "cfg" 101 NAME_REF@94..97
102 TOKEN_TREE@97..104 102 IDENT@94..97 "cfg"
103 L_PAREN@97..98 "(" 103 TOKEN_TREE@97..104
104 IDENT@98..103 "never" 104 L_PAREN@97..98 "("
105 R_PAREN@103..104 ")" 105 IDENT@98..103 "never"
106 R_BRACK@104..105 "]" 106 R_PAREN@103..104 ")"
107 WHITESPACE@105..106 " " 107 R_BRACK@104..105 "]"
108 PARAM@106..120 108 WHITESPACE@105..106 " "
109 [email protected] 109 [email protected]
110 [email protected] "[" 110 [email protected] "["
111 [email protected] 111 [email protected]