aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock39
-rw-r--r--crates/assists/src/assist_context.rs51
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs16
-rw-r--r--crates/assists/src/lib.rs41
-rw-r--r--crates/assists/src/tests.rs48
-rw-r--r--crates/assists/src/tests/generated.rs16
-rw-r--r--crates/base_db/src/input.rs19
-rw-r--r--crates/base_db/src/lib.rs2
-rw-r--r--crates/hir/src/code_model.rs36
-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.rs108
-rw-r--r--crates/hir_def/src/expr.rs30
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs4
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/proc_macro.rs9
-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/tests/patterns.rs30
-rw-r--r--crates/hir_ty/src/tests/simple.rs13
-rw-r--r--crates/hir_ty/src/traits/chalk.rs11
-rw-r--r--crates/ide/src/diagnostics.rs118
-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.rs15
-rw-r--r--crates/ide/src/hover.rs2
-rw-r--r--crates/ide/src/lib.rs30
-rw-r--r--crates/ide/src/references.rs24
-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/mbe/src/mbe_expander/matcher.rs40
-rw-r--r--crates/mbe/src/parser.rs11
-rw-r--r--crates/mbe/src/syntax_bridge.rs3
-rw-r--r--crates/mbe/src/tests.rs64
-rw-r--r--crates/parser/src/grammar.rs2
-rw-r--r--crates/parser/src/grammar/items.rs9
-rw-r--r--crates/proc_macro_api/src/lib.rs8
-rw-r--r--crates/proc_macro_api/src/rpc.rs4
-rw-r--r--crates/proc_macro_srv/src/dylib.rs3
-rw-r--r--crates/proc_macro_srv/src/lib.rs20
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/client.rs12
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/mod.rs3
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/server.rs34
-rw-r--r--crates/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/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.rs23
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/expr_ext.rs4
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast57
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs2
-rw-r--r--crates/tt/src/lib.rs7
-rw-r--r--docs/dev/style.md12
-rw-r--r--docs/user/manual.adoc2
75 files changed, 1019 insertions, 453 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 891cff55e..fd04ec3c5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
168 168
169[[package]] 169[[package]]
170name = "chalk-derive" 170name = "chalk-derive"
171version = "0.43.0" 171version = "0.45.0"
172source = "registry+https://github.com/rust-lang/crates.io-index" 172source = "registry+https://github.com/rust-lang/crates.io-index"
173checksum = "e2d9e0c8adcced1ab0fea5cb8a38647922893d5b495e363e1814299fd380469b" 173checksum = "ec7dacf94958d1a930b95d049d9443860859af59eadc77849392093eb577bcee"
174dependencies = [ 174dependencies = [
175 "proc-macro2", 175 "proc-macro2",
176 "quote", 176 "quote",
@@ -180,19 +180,20 @@ dependencies = [
180 180
181[[package]] 181[[package]]
182name = "chalk-ir" 182name = "chalk-ir"
183version = "0.43.0" 183version = "0.45.0"
184source = "registry+https://github.com/rust-lang/crates.io-index" 184source = "registry+https://github.com/rust-lang/crates.io-index"
185checksum = "c5218266a5709bc4943de997e64d3fab41c9e9f68efd54a898de53135e987bd3" 185checksum = "a1a5b38ede247def17da87f4badb62396a5753db6048e2011d3089d8b3796c67"
186dependencies = [ 186dependencies = [
187 "bitflags",
187 "chalk-derive", 188 "chalk-derive",
188 "lazy_static", 189 "lazy_static",
189] 190]
190 191
191[[package]] 192[[package]]
192name = "chalk-recursive" 193name = "chalk-recursive"
193version = "0.43.0" 194version = "0.45.0"
194source = "registry+https://github.com/rust-lang/crates.io-index" 195source = "registry+https://github.com/rust-lang/crates.io-index"
195checksum = "ed8f34f13fd4f30251f9f6f1dc56f80363201390ecbcac2fdfc8e33036cd9c4a" 196checksum = "7a18db146d7a023edc20ad094e8c2284451f7888719645004979617d1f17c041"
196dependencies = [ 197dependencies = [
197 "chalk-derive", 198 "chalk-derive",
198 "chalk-ir", 199 "chalk-ir",
@@ -203,9 +204,9 @@ dependencies = [
203 204
204[[package]] 205[[package]]
205name = "chalk-solve" 206name = "chalk-solve"
206version = "0.43.0" 207version = "0.45.0"
207source = "registry+https://github.com/rust-lang/crates.io-index" 208source = "registry+https://github.com/rust-lang/crates.io-index"
208checksum = "379c9f584488346044709d4c638c38d61a06fe593d4de2ac5f15fd2b0ba4cd9d" 209checksum = "7f73e0de04a0f394e47ed8118e00541bcf681d7c3c2ef500fa743eb4cf3a4850"
209dependencies = [ 210dependencies = [
210 "chalk-derive", 211 "chalk-derive",
211 "chalk-ir", 212 "chalk-ir",
@@ -507,9 +508,9 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
507 508
508[[package]] 509[[package]]
509name = "heck" 510name = "heck"
510version = "0.3.1" 511version = "0.3.2"
511source = "registry+https://github.com/rust-lang/crates.io-index" 512source = "registry+https://github.com/rust-lang/crates.io-index"
512checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 513checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
513dependencies = [ 514dependencies = [
514 "unicode-segmentation", 515 "unicode-segmentation",
515] 516]
@@ -1074,9 +1075,9 @@ dependencies = [
1074 1075
1075[[package]] 1076[[package]]
1076name = "parking_lot_core" 1077name = "parking_lot_core"
1077version = "0.8.1" 1078version = "0.8.2"
1078source = "registry+https://github.com/rust-lang/crates.io-index" 1079source = "registry+https://github.com/rust-lang/crates.io-index"
1079checksum = "d7c6d9b8427445284a09c55be860a15855ab580a417ccad9da88f5a06787ced0" 1080checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
1080dependencies = [ 1081dependencies = [
1081 "cfg-if 1.0.0", 1082 "cfg-if 1.0.0",
1082 "instant", 1083 "instant",
@@ -1358,6 +1359,7 @@ dependencies = [
1358 "rustc-hash", 1359 "rustc-hash",
1359 "serde", 1360 "serde",
1360 "serde_json", 1361 "serde_json",
1362 "serde_path_to_error",
1361 "ssr", 1363 "ssr",
1362 "stdx", 1364 "stdx",
1363 "syntax", 1365 "syntax",
@@ -1376,9 +1378,9 @@ dependencies = [
1376 1378
1377[[package]] 1379[[package]]
1378name = "rustc-ap-rustc_lexer" 1380name = "rustc-ap-rustc_lexer"
1379version = "691.0.0" 1381version = "695.0.0"
1380source = "registry+https://github.com/rust-lang/crates.io-index" 1382source = "registry+https://github.com/rust-lang/crates.io-index"
1381checksum = "44bc89d9ca7a78fb82e103b389362c55f03800745f8ba14e068b805cfaf783ec" 1383checksum = "390bad134705b0bff02cd9541ac66df751a91c3cc734c3369cd6151ca269caed"
1382dependencies = [ 1384dependencies = [
1383 "unicode-xid", 1385 "unicode-xid",
1384] 1386]
@@ -1527,6 +1529,15 @@ dependencies = [
1527] 1529]
1528 1530
1529[[package]] 1531[[package]]
1532name = "serde_path_to_error"
1533version = "0.1.4"
1534source = "registry+https://github.com/rust-lang/crates.io-index"
1535checksum = "42f6109f0506e20f7e0f910e51a0079acf41da8e0694e6442527c4ddf5a2b158"
1536dependencies = [
1537 "serde",
1538]
1539
1540[[package]]
1530name = "serde_repr" 1541name = "serde_repr"
1531version = "0.1.6" 1542version = "0.1.6"
1532source = "registry+https://github.com/rust-lang/crates.io-index" 1543source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 80cf9aba1..4f59d39a9 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -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)),
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/lib.rs b/crates/assists/src/lib.rs
index 6b89b2d04..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
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index b41f4874a..21e448fb8 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -48,24 +48,25 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
48 let before = db.file_text(file_id).to_string(); 48 let before = db.file_text(file_id).to_string();
49 let frange = FileRange { file_id, range: selection.into() }; 49 let frange = FileRange { file_id, range: selection.into() };
50 50
51 let assist = Assist::resolved(&db, &AssistConfig::default(), frange) 51 let assist = Assist::get(&db, &AssistConfig::default(), true, frange)
52 .into_iter() 52 .into_iter()
53 .find(|assist| assist.assist.id.0 == assist_id) 53 .find(|assist| assist.id.0 == assist_id)
54 .unwrap_or_else(|| { 54 .unwrap_or_else(|| {
55 panic!( 55 panic!(
56 "\n\nAssist is not applicable: {}\nAvailable assists: {}", 56 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
57 assist_id, 57 assist_id,
58 Assist::resolved(&db, &AssistConfig::default(), frange) 58 Assist::get(&db, &AssistConfig::default(), false, frange)
59 .into_iter() 59 .into_iter()
60 .map(|assist| assist.assist.id.0) 60 .map(|assist| assist.id.0)
61 .collect::<Vec<_>>() 61 .collect::<Vec<_>>()
62 .join(", ") 62 .join(", ")
63 ) 63 )
64 }); 64 });
65 65
66 let actual = { 66 let actual = {
67 let source_change = assist.source_change.unwrap();
67 let mut actual = before; 68 let mut actual = before;
68 for source_file_edit in assist.source_change.source_file_edits { 69 for source_file_edit in source_change.source_file_edits {
69 if source_file_edit.file_id == file_id { 70 if source_file_edit.file_id == file_id {
70 source_file_edit.edit.apply(&mut actual) 71 source_file_edit.edit.apply(&mut actual)
71 } 72 }
@@ -90,18 +91,18 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
90 let sema = Semantics::new(&db); 91 let sema = Semantics::new(&db);
91 let config = AssistConfig::default(); 92 let config = AssistConfig::default();
92 let ctx = AssistContext::new(sema, &config, frange); 93 let ctx = AssistContext::new(sema, &config, frange);
93 let mut acc = Assists::new_resolved(&ctx); 94 let mut acc = Assists::new(&ctx, true);
94 handler(&mut acc, &ctx); 95 handler(&mut acc, &ctx);
95 let mut res = acc.finish_resolved(); 96 let mut res = acc.finish();
96 97
97 let assist = match assist_label { 98 let assist = match assist_label {
98 Some(label) => res.into_iter().find(|resolved| resolved.assist.label == label), 99 Some(label) => res.into_iter().find(|resolved| resolved.label == label),
99 None => res.pop(), 100 None => res.pop(),
100 }; 101 };
101 102
102 match (assist, expected) { 103 match (assist, expected) {
103 (Some(assist), ExpectedResult::After(after)) => { 104 (Some(assist), ExpectedResult::After(after)) => {
104 let mut source_change = assist.source_change; 105 let mut source_change = assist.source_change.unwrap();
105 assert!(!source_change.source_file_edits.is_empty()); 106 assert!(!source_change.source_file_edits.is_empty());
106 let skip_header = source_change.source_file_edits.len() == 1 107 let skip_header = source_change.source_file_edits.len() == 1
107 && source_change.file_system_edits.len() == 0; 108 && source_change.file_system_edits.len() == 0;
@@ -138,7 +139,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
138 assert_eq_text!(after, &buf); 139 assert_eq_text!(after, &buf);
139 } 140 }
140 (Some(assist), ExpectedResult::Target(target)) => { 141 (Some(assist), ExpectedResult::Target(target)) => {
141 let range = assist.assist.target; 142 let range = assist.target;
142 assert_eq_text!(&text_without_caret[range], target); 143 assert_eq_text!(&text_without_caret[range], target);
143 } 144 }
144 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), 145 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
@@ -155,14 +156,11 @@ fn assist_order_field_struct() {
155 let (before_cursor_pos, before) = extract_offset(before); 156 let (before_cursor_pos, before) = extract_offset(before);
156 let (db, file_id) = with_single_file(&before); 157 let (db, file_id) = with_single_file(&before);
157 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; 158 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
158 let assists = Assist::resolved(&db, &AssistConfig::default(), frange); 159 let assists = Assist::get(&db, &AssistConfig::default(), false, frange);
159 let mut assists = assists.iter(); 160 let mut assists = assists.iter();
160 161
161 assert_eq!( 162 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
162 assists.next().expect("expected assist").assist.label, 163 assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
163 "Change visibility to pub(crate)"
164 );
165 assert_eq!(assists.next().expect("expected assist").assist.label, "Add `#[derive]`");
166} 164}
167 165
168#[test] 166#[test]
@@ -178,11 +176,11 @@ fn assist_order_if_expr() {
178 let (range, before) = extract_range(before); 176 let (range, before) = extract_range(before);
179 let (db, file_id) = with_single_file(&before); 177 let (db, file_id) = with_single_file(&before);
180 let frange = FileRange { file_id, range }; 178 let frange = FileRange { file_id, range };
181 let assists = Assist::resolved(&db, &AssistConfig::default(), frange); 179 let assists = Assist::get(&db, &AssistConfig::default(), false, frange);
182 let mut assists = assists.iter(); 180 let mut assists = assists.iter();
183 181
184 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); 182 assert_eq!(assists.next().expect("expected assist").label, "Extract into variable");
185 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); 183 assert_eq!(assists.next().expect("expected assist").label, "Replace with match");
186} 184}
187 185
188#[test] 186#[test]
@@ -203,27 +201,27 @@ fn assist_filter_works() {
203 let mut cfg = AssistConfig::default(); 201 let mut cfg = AssistConfig::default();
204 cfg.allowed = Some(vec![AssistKind::Refactor]); 202 cfg.allowed = Some(vec![AssistKind::Refactor]);
205 203
206 let assists = Assist::resolved(&db, &cfg, frange); 204 let assists = Assist::get(&db, &cfg, false, frange);
207 let mut assists = assists.iter(); 205 let mut assists = assists.iter();
208 206
209 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); 207 assert_eq!(assists.next().expect("expected assist").label, "Extract into variable");
210 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); 208 assert_eq!(assists.next().expect("expected assist").label, "Replace with match");
211 } 209 }
212 210
213 { 211 {
214 let mut cfg = AssistConfig::default(); 212 let mut cfg = AssistConfig::default();
215 cfg.allowed = Some(vec![AssistKind::RefactorExtract]); 213 cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
216 let assists = Assist::resolved(&db, &cfg, frange); 214 let assists = Assist::get(&db, &cfg, false, frange);
217 assert_eq!(assists.len(), 1); 215 assert_eq!(assists.len(), 1);
218 216
219 let mut assists = assists.iter(); 217 let mut assists = assists.iter();
220 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); 218 assert_eq!(assists.next().expect("expected assist").label, "Extract into variable");
221 } 219 }
222 220
223 { 221 {
224 let mut cfg = AssistConfig::default(); 222 let mut cfg = AssistConfig::default();
225 cfg.allowed = Some(vec![AssistKind::QuickFix]); 223 cfg.allowed = Some(vec![AssistKind::QuickFix]);
226 let assists = Assist::resolved(&db, &cfg, frange); 224 let assists = Assist::get(&db, &cfg, false, frange);
227 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");
228 } 226 }
229} 227}
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index e9093ec53..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"#####,
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index cda5e57dc..9567bcc42 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -6,12 +6,12 @@
6//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how 6//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
7//! actual IO is done and lowered to input. 7//! actual IO is done and lowered to input.
8 8
9use std::{fmt, iter::FromIterator, ops, str::FromStr, sync::Arc}; 9use std::{fmt, iter::FromIterator, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
10 10
11use cfg::CfgOptions; 11use cfg::CfgOptions;
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
13use syntax::SmolStr; 13use syntax::SmolStr;
14use tt::TokenExpander; 14use tt::{ExpansionError, Subtree};
15use vfs::{file_set::FileSet, FileId, VfsPath}; 15use vfs::{file_set::FileSet, FileId, VfsPath};
16 16
17/// Files are grouped into source roots. A source root is a directory on the 17/// Files are grouped into source roots. A source root is a directory on the
@@ -150,11 +150,20 @@ pub enum ProcMacroKind {
150 Attr, 150 Attr,
151} 151}
152 152
153pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
154 fn expand(
155 &self,
156 subtree: &Subtree,
157 attrs: Option<&Subtree>,
158 env: &Env,
159 ) -> Result<Subtree, ExpansionError>;
160}
161
153#[derive(Debug, Clone)] 162#[derive(Debug, Clone)]
154pub struct ProcMacro { 163pub struct ProcMacro {
155 pub name: SmolStr, 164 pub name: SmolStr,
156 pub kind: ProcMacroKind, 165 pub kind: ProcMacroKind,
157 pub expander: Arc<dyn TokenExpander>, 166 pub expander: Arc<dyn ProcMacroExpander>,
158} 167}
159 168
160impl Eq for ProcMacro {} 169impl Eq for ProcMacro {}
@@ -413,6 +422,10 @@ impl Env {
413 pub fn get(&self, env: &str) -> Option<String> { 422 pub fn get(&self, env: &str) -> Option<String> {
414 self.entries.get(env).cloned() 423 self.entries.get(env).cloned()
415 } 424 }
425
426 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
427 self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str()))
428 }
416} 429}
417 430
418#[derive(Debug)] 431#[derive(Debug)]
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs
index 595f28ada..5f77a0b1f 100644
--- a/crates/base_db/src/lib.rs
+++ b/crates/base_db/src/lib.rs
@@ -14,7 +14,7 @@ pub use crate::{
14 change::Change, 14 change::Change,
15 input::{ 15 input::{
16 CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, 16 CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env,
17 ProcMacro, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId, 17 ProcMacro, ProcMacroExpander, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId,
18 }, 18 },
19}; 19};
20pub use salsa; 20pub use salsa;
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 1d7e5ddd7..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
@@ -1206,6 +1206,34 @@ impl Local {
1206} 1206}
1207 1207
1208#[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)]
1209pub enum GenericParam { 1237pub enum GenericParam {
1210 TypeParam(TypeParam), 1238 TypeParam(TypeParam),
1211 LifetimeParam(LifetimeParam), 1239 LifetimeParam(LifetimeParam),
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index 8e0c571b8..a0792b9a6 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -4,12 +4,15 @@
4//! are splitting the hir. 4//! are splitting the hir.
5 5
6use hir_def::{ 6use hir_def::{
7 expr::PatId, item_scope::ItemInNs, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, 7 expr::{LabelId, PatId},
8 GenericDefId, ModuleDefId, VariantId, 8 item_scope::ItemInNs,
9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, ModuleDefId,
10 VariantId,
9}; 11};
10 12
11use crate::{ 13use crate::{
12 Adt, AssocItem, DefWithBody, Field, GenericDef, Local, MacroDef, ModuleDef, Variant, VariantDef, 14 Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant,
15 VariantDef,
13}; 16};
14 17
15macro_rules! from_id { 18macro_rules! from_id {
@@ -228,6 +231,12 @@ impl From<(DefWithBodyId, PatId)> for Local {
228 } 231 }
229} 232}
230 233
234impl From<(DefWithBodyId, LabelId)> for Label {
235 fn from((parent, label_id): (DefWithBodyId, LabelId)) -> Self {
236 Label { parent, label_id }
237 }
238}
239
231impl From<MacroDef> for ItemInNs { 240impl From<MacroDef> for ItemInNs {
232 fn from(macro_def: MacroDef) -> Self { 241 fn from(macro_def: MacroDef) -> Self {
233 ItemInNs::Macros(macro_def.into()) 242 ItemInNs::Macros(macro_def.into())
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index bdd270c58..7ac9fd507 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -35,8 +35,8 @@ pub use crate::{
35 code_model::{ 35 code_model::{
36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, 36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const,
37 Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef, 37 Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef,
38 HasVisibility, Impl, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, 38 HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef,
39 Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, 39 Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
40 }, 40 },
41 has_source::HasSource, 41 has_source::HasSource,
42 semantics::{PathResolution, Semantics, SemanticsScope}, 42 semantics::{PathResolution, Semantics, SemanticsScope},
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 25ebf73d8..67cd16e31 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -15,7 +15,7 @@ use itertools::Itertools;
15use rustc_hash::{FxHashMap, FxHashSet}; 15use rustc_hash::{FxHashMap, FxHashSet};
16use syntax::{ 16use syntax::{
17 algo::find_node_at_offset, 17 algo::find_node_at_offset,
18 ast::{self, GenericParamsOwner}, 18 ast::{self, GenericParamsOwner, LoopBodyOwner},
19 match_ast, AstNode, SyntaxNode, SyntaxToken, TextSize, 19 match_ast, AstNode, SyntaxNode, SyntaxToken, TextSize,
20}; 20};
21 21
@@ -25,8 +25,8 @@ use crate::{
25 diagnostics::Diagnostic, 25 diagnostics::Diagnostic,
26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
27 source_analyzer::{resolve_hir_path, SourceAnalyzer}, 27 source_analyzer::{resolve_hir_path, SourceAnalyzer},
28 AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, LifetimeParam, Local, 28 AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, Label, LifetimeParam,
29 MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, 29 Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam,
30 VariantDef, 30 VariantDef,
31}; 31};
32 32
@@ -182,6 +182,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
182 self.imp.resolve_lifetime_param(lifetime) 182 self.imp.resolve_lifetime_param(lifetime)
183 } 183 }
184 184
185 pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
186 self.imp.resolve_label(lifetime)
187 }
188
185 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { 189 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
186 self.imp.type_of_expr(expr) 190 self.imp.type_of_expr(expr)
187 } 191 }
@@ -425,6 +429,28 @@ impl<'db> SemanticsImpl<'db> {
425 ToDef::to_def(self, src) 429 ToDef::to_def(self, src)
426 } 430 }
427 431
432 fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
433 let text = lifetime.text();
434 let label = lifetime.syntax().ancestors().find_map(|syn| {
435 let label = match_ast! {
436 match syn {
437 ast::ForExpr(it) => it.label(),
438 ast::WhileExpr(it) => it.label(),
439 ast::LoopExpr(it) => it.label(),
440 ast::EffectExpr(it) => it.label(),
441 _ => None,
442 }
443 };
444 label.filter(|l| {
445 l.lifetime()
446 .and_then(|lt| lt.lifetime_ident_token())
447 .map_or(false, |lt| lt.text() == text)
448 })
449 })?;
450 let src = self.find_file(label.syntax().clone()).with_value(label);
451 ToDef::to_def(self, src)
452 }
453
428 fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { 454 fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
429 self.analyze(expr.syntax()).type_of_expr(self.db, expr) 455 self.analyze(expr.syntax()).type_of_expr(self.db, expr)
430 } 456 }
@@ -720,6 +746,7 @@ to_def_impls![
720 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), 746 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
721 (crate::MacroDef, ast::MacroRules, macro_rules_to_def), 747 (crate::MacroDef, ast::MacroRules, macro_rules_to_def),
722 (crate::Local, ast::IdentPat, bind_pat_to_def), 748 (crate::Local, ast::IdentPat, bind_pat_to_def),
749 (crate::Label, ast::Label, label_to_def),
723]; 750];
724 751
725fn find_root(node: &SyntaxNode) -> SyntaxNode { 752fn find_root(node: &SyntaxNode) -> SyntaxNode {
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 3efca5baa..424e6e8a9 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -4,7 +4,7 @@ use base_db::FileId;
4use hir_def::{ 4use hir_def::{
5 child_by_source::ChildBySource, 5 child_by_source::ChildBySource,
6 dyn_map::DynMap, 6 dyn_map::DynMap,
7 expr::PatId, 7 expr::{LabelId, PatId},
8 keys::{self, Key}, 8 keys::{self, Key},
9 ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, 9 ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId,
10 LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, 10 LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
@@ -108,12 +108,21 @@ impl SourceToDefCtx<'_, '_> {
108 &mut self, 108 &mut self,
109 src: InFile<ast::IdentPat>, 109 src: InFile<ast::IdentPat>,
110 ) -> Option<(DefWithBodyId, PatId)> { 110 ) -> Option<(DefWithBodyId, PatId)> {
111 let container = self.find_pat_container(src.as_ref().map(|it| it.syntax()))?; 111 let container = self.find_pat_or_label_container(src.as_ref().map(|it| it.syntax()))?;
112 let (_body, source_map) = self.db.body_with_source_map(container); 112 let (_body, source_map) = self.db.body_with_source_map(container);
113 let src = src.map(ast::Pat::from); 113 let src = src.map(ast::Pat::from);
114 let pat_id = source_map.node_pat(src.as_ref())?; 114 let pat_id = source_map.node_pat(src.as_ref())?;
115 Some((container, pat_id)) 115 Some((container, pat_id))
116 } 116 }
117 pub(super) fn label_to_def(
118 &mut self,
119 src: InFile<ast::Label>,
120 ) -> Option<(DefWithBodyId, LabelId)> {
121 let container = self.find_pat_or_label_container(src.as_ref().map(|it| it.syntax()))?;
122 let (_body, source_map) = self.db.body_with_source_map(container);
123 let label_id = source_map.node_label(src.as_ref())?;
124 Some((container, label_id))
125 }
117 126
118 fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( 127 fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
119 &mut self, 128 &mut self,
@@ -237,7 +246,7 @@ impl SourceToDefCtx<'_, '_> {
237 None 246 None
238 } 247 }
239 248
240 fn find_pat_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> { 249 fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
241 for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { 250 for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
242 let res: DefWithBodyId = match_ast! { 251 let res: DefWithBodyId = match_ast! {
243 match (container.value) { 252 match (container.value) {
diff --git a/crates/hir_def/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 978c3a324..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,10 +934,16 @@ 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 }
935 // FIXME: implement 937 ast::Pat::ConstBlockPat(const_block_pat) => {
936 ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) | ast::Pat::ConstBlockPat(_) => { 938 if let Some(expr) = const_block_pat.block_expr() {
937 Pat::Missing 939 let expr_id = self.collect_block(expr);
940 Pat::ConstBlock(expr_id)
941 } else {
942 Pat::Missing
943 }
938 } 944 }
945 // FIXME: implement
946 ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
939 }; 947 };
940 let ptr = AstPtr::new(&pat); 948 let ptr = AstPtr::new(&pat);
941 self.alloc_pat(pattern, Either::Left(ptr)) 949 self.alloc_pat(pattern, Either::Left(ptr))
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/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index f9bf5bc72..e5e9e8ca1 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -677,7 +677,7 @@ fn macro_expansion_overflow() {
677 r#" 677 r#"
678macro_rules! a { 678macro_rules! a {
679 ($e:expr; $($t:tt)*) => { 679 ($e:expr; $($t:tt)*) => {
680 b!($($t)*); 680 b!(static = (); $($t)*);
681 }; 681 };
682 () => {}; 682 () => {};
683} 683}
@@ -689,7 +689,7 @@ macro_rules! b {
689 () => {}; 689 () => {};
690} 690}
691 691
692b! { static = #[] (); } 692b! { static = #[] ();}
693"#, 693"#,
694 expect![[r#" 694 expect![[r#"
695 crate 695 crate
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 077de3727..06f0a3ed9 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -271,7 +271,7 @@ fn expand_proc_macro(
271 _ => unreachable!(), 271 _ => unreachable!(),
272 }; 272 };
273 273
274 expander.expand(db, lazy_id, &macro_arg.0) 274 expander.expand(db, loc.krate, &macro_arg.0)
275} 275}
276 276
277fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { 277fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 38882d2b6..7c77f6ce0 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -1,6 +1,6 @@
1//! Proc Macro Expander stub 1//! Proc Macro Expander stub
2 2
3use crate::{db::AstDatabase, LazyMacroId}; 3use crate::db::AstDatabase;
4use base_db::{CrateId, ProcMacroId}; 4use base_db::{CrateId, ProcMacroId};
5use tt::buffer::{Cursor, TokenBuffer}; 5use tt::buffer::{Cursor, TokenBuffer};
6 6
@@ -32,7 +32,7 @@ impl ProcMacroExpander {
32 pub fn expand( 32 pub fn expand(
33 self, 33 self,
34 db: &dyn AstDatabase, 34 db: &dyn AstDatabase,
35 _id: LazyMacroId, 35 calling_crate: CrateId,
36 tt: &tt::Subtree, 36 tt: &tt::Subtree,
37 ) -> Result<tt::Subtree, mbe::ExpandError> { 37 ) -> Result<tt::Subtree, mbe::ExpandError> {
38 match self.proc_macro_id { 38 match self.proc_macro_id {
@@ -47,7 +47,10 @@ impl ProcMacroExpander {
47 let tt = remove_derive_attrs(tt) 47 let tt = remove_derive_attrs(tt)
48 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; 48 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
49 49
50 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 50 // Proc macros have access to the environment variables of the invoking crate.
51 let env = &krate_graph[calling_crate].env;
52
53 proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from)
51 } 54 }
52 None => Err(mbe::ExpandError::UnresolvedProcMacro), 55 None => Err(mbe::ExpandError::UnresolvedProcMacro),
53 } 56 }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 289e812fe..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/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 5a5f48fd0..2053d8f56 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -774,3 +774,33 @@ fn foo(tuple: Tuple) {
774 "#]], 774 "#]],
775 ); 775 );
776} 776}
777
778#[test]
779fn const_block_pattern() {
780 check_infer(
781 r#"
782struct Foo(usize);
783fn foo(foo: Foo) {
784 match foo {
785 const { Foo(15 + 32) } => {},
786 _ => {}
787 }
788}"#,
789 expect![[r#"
790 26..29 'foo': Foo
791 36..115 '{ ... } }': ()
792 42..113 'match ... }': ()
793 48..51 'foo': Foo
794 62..84 'const ... 32) }': Foo
795 68..84 '{ Foo(... 32) }': Foo
796 70..73 'Foo': Foo(usize) -> Foo
797 70..82 'Foo(15 + 32)': Foo
798 74..76 '15': usize
799 74..81 '15 + 32': usize
800 79..81 '32': usize
801 88..90 '{}': ()
802 100..101 '_': Foo
803 105..107 '{}': ()
804 "#]],
805 );
806}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index a569223b4..a61282d5a 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1894,6 +1894,7 @@ fn effects_smoke_test() {
1894 let x = unsafe { 92 }; 1894 let x = unsafe { 92 };
1895 let y = async { async { () }.await }; 1895 let y = async { async { () }.await };
1896 let z = try { () }; 1896 let z = try { () };
1897 let w = const { 92 };
1897 let t = 'a: { 92 }; 1898 let t = 'a: { 92 };
1898 } 1899 }
1899 1900
@@ -1905,7 +1906,7 @@ fn effects_smoke_test() {
1905 } 1906 }
1906 "#, 1907 "#,
1907 expect![[r#" 1908 expect![[r#"
1908 16..136 '{ ...2 }; }': () 1909 16..162 '{ ...2 }; }': ()
1909 26..27 'x': i32 1910 26..27 'x': i32
1910 30..43 'unsafe { 92 }': i32 1911 30..43 'unsafe { 92 }': i32
1911 37..43 '{ 92 }': i32 1912 37..43 '{ 92 }': i32
@@ -1921,9 +1922,13 @@ fn effects_smoke_test() {
1921 99..109 'try { () }': {unknown} 1922 99..109 'try { () }': {unknown}
1922 103..109 '{ () }': () 1923 103..109 '{ () }': ()
1923 105..107 '()': () 1924 105..107 '()': ()
1924 119..120 't': i32 1925 119..120 'w': i32
1925 127..133 '{ 92 }': i32 1926 123..135 'const { 92 }': i32
1926 129..131 '92': i32 1927 129..135 '{ 92 }': i32
1928 131..133 '92': i32
1929 145..146 't': i32
1930 153..159 '{ 92 }': i32
1931 155..157 '92': i32
1927 "#]], 1932 "#]],
1928 ) 1933 )
1929} 1934}
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 69eae6f79..2196af677 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -56,8 +56,13 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
56 fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> { 56 fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> {
57 self.db.struct_datum(self.krate, struct_id) 57 self.db.struct_datum(self.krate, struct_id)
58 } 58 }
59 fn adt_repr(&self, _struct_id: AdtId) -> rust_ir::AdtRepr { 59 fn adt_repr(&self, _struct_id: AdtId) -> Arc<rust_ir::AdtRepr<Interner>> {
60 rust_ir::AdtRepr { repr_c: false, repr_packed: false } 60 // FIXME: keep track of these
61 Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None })
62 }
63 fn discriminant_type(&self, _ty: chalk_ir::Ty<Interner>) -> chalk_ir::Ty<Interner> {
64 // FIXME: keep track of this
65 chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(&Interner)
61 } 66 }
62 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> { 67 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
63 self.db.impl_datum(self.krate, impl_id) 68 self.db.impl_datum(self.krate, impl_id)
@@ -457,6 +462,7 @@ fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
457 "fn" => WellKnownTrait::Fn, 462 "fn" => WellKnownTrait::Fn,
458 "unsize" => WellKnownTrait::Unsize, 463 "unsize" => WellKnownTrait::Unsize,
459 "coerce_unsized" => WellKnownTrait::CoerceUnsized, 464 "coerce_unsized" => WellKnownTrait::CoerceUnsized,
465 "discriminant_kind" => WellKnownTrait::DiscriminantKind,
460 _ => return None, 466 _ => return None,
461 }) 467 })
462} 468}
@@ -473,6 +479,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
473 WellKnownTrait::Unsize => "unsize", 479 WellKnownTrait::Unsize => "unsize",
474 WellKnownTrait::Unpin => "unpin", 480 WellKnownTrait::Unpin => "unpin",
475 WellKnownTrait::CoerceUnsized => "coerce_unsized", 481 WellKnownTrait::CoerceUnsized => "coerce_unsized",
482 WellKnownTrait::DiscriminantKind => "discriminant_kind",
476 } 483 }
477} 484}
478 485
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3ad30f0c9..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) {
@@ -764,25 +715,25 @@ struct Foo {
764 715
765 #[test] 716 #[test]
766 fn test_add_field_in_other_file_from_usage() { 717 fn test_add_field_in_other_file_from_usage() {
767 check_apply_diagnostic_fix_in_other_file( 718 check_fix(
768 r" 719 r#"
769 //- /main.rs 720//- /main.rs
770 mod foo; 721mod foo;
771 722
772 fn main() { 723fn main() {
773 <|>foo::Foo { bar: 3, baz: false}; 724 foo::Foo { bar: 3, <|>baz: false};
774 } 725}
775 //- /foo.rs 726//- /foo.rs
776 struct Foo { 727struct Foo {
777 bar: i32 728 bar: i32
778 } 729}
779 ", 730"#,
780 r" 731 r#"
781 struct Foo { 732struct Foo {
782 bar: i32, 733 bar: i32,
783 pub(crate) baz: bool 734 pub(crate) baz: bool
784 } 735}
785 ", 736"#,
786 ) 737 )
787 } 738 }
788 739
@@ -802,7 +753,7 @@ struct Foo {
802 753
803 #[test] 754 #[test]
804 fn test_rename_incorrect_case() { 755 fn test_rename_incorrect_case() {
805 check_fixes( 756 check_fix(
806 r#" 757 r#"
807pub struct test_struct<|> { one: i32 } 758pub struct test_struct<|> { one: i32 }
808 759
@@ -819,7 +770,7 @@ pub fn some_fn(val: TestStruct) -> TestStruct {
819"#, 770"#,
820 ); 771 );
821 772
822 check_fixes( 773 check_fix(
823 r#" 774 r#"
824pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { 775pub fn some_fn(NonSnakeCase<|>: u8) -> u8 {
825 NonSnakeCase 776 NonSnakeCase
@@ -832,7 +783,7 @@ pub fn some_fn(non_snake_case: u8) -> u8 {
832"#, 783"#,
833 ); 784 );
834 785
835 check_fixes( 786 check_fix(
836 r#" 787 r#"
837pub fn SomeFn<|>(val: u8) -> u8 { 788pub fn SomeFn<|>(val: u8) -> u8 {
838 if val != 0 { SomeFn(val - 1) } else { val } 789 if val != 0 { SomeFn(val - 1) } else { val }
@@ -845,7 +796,7 @@ pub fn some_fn(val: u8) -> u8 {
845"#, 796"#,
846 ); 797 );
847 798
848 check_fixes( 799 check_fix(
849 r#" 800 r#"
850fn some_fn() { 801fn some_fn() {
851 let whatAWeird_Formatting<|> = 10; 802 let whatAWeird_Formatting<|> = 10;
@@ -874,7 +825,7 @@ fn foo() {
874 825
875 #[test] 826 #[test]
876 fn test_rename_incorrect_case_struct_method() { 827 fn test_rename_incorrect_case_struct_method() {
877 check_fixes( 828 check_fix(
878 r#" 829 r#"
879pub struct TestStruct; 830pub struct TestStruct;
880 831
@@ -895,4 +846,17 @@ impl TestStruct {
895"#, 846"#,
896 ); 847 );
897 } 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 }
898} 862}
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 47dd85ceb..912144f8b 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1130,4 +1130,19 @@ fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {}
1130"#, 1130"#,
1131 ); 1131 );
1132 } 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 }
1133} 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 33b7358f7..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
@@ -1122,4 +1122,26 @@ fn main() {
1122 "#]], 1122 "#]],
1123 ); 1123 );
1124 } 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 }
1125} 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/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index 7aeef7be5..44722c0f1 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -240,26 +240,26 @@ impl<'a> TtIter<'a> {
240 let tt3 = self.next().unwrap().clone(); 240 let tt3 = self.next().unwrap().clone();
241 Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2, tt3] }.into()) 241 Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2, tt3] }.into())
242 } 242 }
243 ('-', '=', None) 243 ('-', '=', _)
244 | ('-', '>', None) 244 | ('-', '>', _)
245 | (':', ':', None) 245 | (':', ':', _)
246 | ('!', '=', None) 246 | ('!', '=', _)
247 | ('.', '.', None) 247 | ('.', '.', _)
248 | ('*', '=', None) 248 | ('*', '=', _)
249 | ('/', '=', None) 249 | ('/', '=', _)
250 | ('&', '&', None) 250 | ('&', '&', _)
251 | ('&', '=', None) 251 | ('&', '=', _)
252 | ('%', '=', None) 252 | ('%', '=', _)
253 | ('^', '=', None) 253 | ('^', '=', _)
254 | ('+', '=', None) 254 | ('+', '=', _)
255 | ('<', '<', None) 255 | ('<', '<', _)
256 | ('<', '=', None) 256 | ('<', '=', _)
257 | ('=', '=', None) 257 | ('=', '=', _)
258 | ('=', '>', None) 258 | ('=', '>', _)
259 | ('>', '=', None) 259 | ('>', '=', _)
260 | ('>', '>', None) 260 | ('>', '>', _)
261 | ('|', '=', None) 261 | ('|', '=', _)
262 | ('|', '|', None) => { 262 | ('|', '|', _) => {
263 let tt2 = self.next().unwrap().clone(); 263 let tt2 = self.next().unwrap().clone();
264 Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2] }.into()) 264 Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2] }.into())
265 } 265 }
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index c3fdd4040..d681905f5 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -101,8 +101,15 @@ fn next_op<'a>(
101 Op::Repeat { subtree, separator, kind } 101 Op::Repeat { subtree, separator, kind }
102 } 102 }
103 tt::TokenTree::Leaf(leaf) => match leaf { 103 tt::TokenTree::Leaf(leaf) => match leaf {
104 tt::Leaf::Punct(_) => { 104 tt::Leaf::Punct(punct) => {
105 return Err(ExpandError::UnexpectedToken); 105 static UNDERSCORE: SmolStr = SmolStr::new_inline("_");
106
107 if punct.char != '_' {
108 return Err(ExpandError::UnexpectedToken);
109 }
110 let name = &UNDERSCORE;
111 let kind = eat_fragment_kind(src, mode)?;
112 Op::Var { name, kind }
106 } 113 }
107 tt::Leaf::Ident(ident) => { 114 tt::Leaf::Ident(ident) => {
108 let name = &ident.text; 115 let name = &ident.text;
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 2bec7fd49..265c0d63d 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -313,7 +313,7 @@ trait TokenConvertor {
313 return; 313 return;
314 } 314 }
315 315
316 result.push(if k.is_punct() && k != UNDERSCORE { 316 result.push(if k.is_punct() {
317 assert_eq!(range.len(), TextSize::of('.')); 317 assert_eq!(range.len(), TextSize::of('.'));
318 let delim = match k { 318 let delim = match k {
319 T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), 319 T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])),
@@ -378,7 +378,6 @@ trait TokenConvertor {
378 let leaf: tt::Leaf = match k { 378 let leaf: tt::Leaf = match k {
379 T![true] | T![false] => make_leaf!(Ident), 379 T![true] | T![false] => make_leaf!(Ident),
380 IDENT => make_leaf!(Ident), 380 IDENT => make_leaf!(Ident),
381 UNDERSCORE => make_leaf!(Ident),
382 k if k.is_keyword() => make_leaf!(Ident), 381 k if k.is_keyword() => make_leaf!(Ident),
383 k if k.is_literal() => make_leaf!(Literal), 382 k if k.is_literal() => make_leaf!(Literal),
384 LIFETIME_IDENT => { 383 LIFETIME_IDENT => {
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 451fa1456..1d9afb4fb 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -761,6 +761,18 @@ fn test_last_expr() {
761} 761}
762 762
763#[test] 763#[test]
764fn test_expr_with_attr() {
765 parse_macro(
766 r#"
767macro_rules! m {
768 ($a:expr) => {0}
769}
770"#,
771 )
772 .assert_expand_items("m!(#[allow(a)]())", "0");
773}
774
775#[test]
764fn test_ty() { 776fn test_ty() {
765 parse_macro( 777 parse_macro(
766 r#" 778 r#"
@@ -992,6 +1004,22 @@ fn test_tt_composite2() {
992} 1004}
993 1005
994#[test] 1006#[test]
1007fn test_tt_with_composite_without_space() {
1008 parse_macro(
1009 r#"
1010 macro_rules! foo {
1011 ($ op:tt, $j:path) => (
1012 0
1013 )
1014 }
1015"#,
1016 )
1017 // Test macro input without any spaces
1018 // See https://github.com/rust-analyzer/rust-analyzer/issues/6692
1019 .assert_expand_items("foo!(==,Foo::Bool)", "0");
1020}
1021
1022#[test]
995fn test_underscore() { 1023fn test_underscore() {
996 parse_macro( 1024 parse_macro(
997 r#" 1025 r#"
@@ -1004,6 +1032,42 @@ fn test_underscore() {
1004} 1032}
1005 1033
1006#[test] 1034#[test]
1035fn test_underscore_not_greedily() {
1036 parse_macro(
1037 r#"
1038macro_rules! q {
1039 ($($a:ident)* _) => {0};
1040}
1041"#,
1042 )
1043 // `_` overlaps with `$a:ident` but rustc matches it under the `_` token
1044 .assert_expand_items(r#"q![a b c d _]"#, r#"0"#);
1045
1046 parse_macro(
1047 r#"
1048macro_rules! q {
1049 ($($a:expr => $b:ident)* _ => $c:expr) => {0};
1050}
1051"#,
1052 )
1053 // `_ => ou` overlaps with `$a:expr => $b:ident` but rustc matches it under `_ => $c:expr`
1054 .assert_expand_items(r#"q![a => b c => d _ => ou]"#, r#"0"#);
1055}
1056
1057#[test]
1058fn test_underscore_as_type() {
1059 parse_macro(
1060 r#"
1061macro_rules! q {
1062 ($a:ty) => {0};
1063}
1064"#,
1065 )
1066 // Underscore is a type
1067 .assert_expand_items(r#"q![_]"#, r#"0"#);
1068}
1069
1070#[test]
1007fn test_vertical_bar_with_pat() { 1071fn test_vertical_bar_with_pat() {
1008 parse_macro( 1072 parse_macro(
1009 r#" 1073 r#"
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index f08c8bab7..63cc90027 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -59,7 +59,7 @@ pub(crate) mod fragments {
59 }; 59 };
60 60
61 pub(crate) fn expr(p: &mut Parser) { 61 pub(crate) fn expr(p: &mut Parser) {
62 let _ = expressions::expr(p); 62 let _ = expressions::expr_with_attrs(p);
63 } 63 }
64 64
65 pub(crate) fn stmt(p: &mut Parser) { 65 pub(crate) fn stmt(p: &mut Parser) {
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 72b73f891..cf4168d32 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -389,10 +389,15 @@ fn macro_rules(p: &mut Parser, m: Marker) {
389 } 389 }
390 390
391 match p.current() { 391 match p.current() {
392 T!['{'] => { 392 // test macro_rules_non_brace
393 // macro_rules! m ( ($i:ident) => {} );
394 // macro_rules! m [ ($i:ident) => {} ];
395 T!['['] | T!['('] => {
393 token_tree(p); 396 token_tree(p);
397 p.expect(T![;]);
394 } 398 }
395 _ => p.error("expected `{`"), 399 T!['{'] => token_tree(p),
400 _ => p.error("expected `{`, `[`, `(`"),
396 } 401 }
397 m.complete(p, MACRO_RULES); 402 m.complete(p, MACRO_RULES);
398} 403}
diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs
index 0d061fd53..2ea456fb0 100644
--- a/crates/proc_macro_api/src/lib.rs
+++ b/crates/proc_macro_api/src/lib.rs
@@ -16,7 +16,7 @@ use std::{
16 sync::Arc, 16 sync::Arc,
17}; 17};
18 18
19use base_db::ProcMacro; 19use base_db::{Env, ProcMacro};
20use tt::{SmolStr, Subtree}; 20use tt::{SmolStr, Subtree};
21 21
22use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; 22use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
@@ -39,17 +39,19 @@ impl PartialEq for ProcMacroProcessExpander {
39 } 39 }
40} 40}
41 41
42impl tt::TokenExpander for ProcMacroProcessExpander { 42impl base_db::ProcMacroExpander for ProcMacroProcessExpander {
43 fn expand( 43 fn expand(
44 &self, 44 &self,
45 subtree: &Subtree, 45 subtree: &Subtree,
46 attr: Option<&Subtree>, 46 attr: Option<&Subtree>,
47 env: &Env,
47 ) -> Result<Subtree, tt::ExpansionError> { 48 ) -> Result<Subtree, tt::ExpansionError> {
48 let task = ExpansionTask { 49 let task = ExpansionTask {
49 macro_body: subtree.clone(), 50 macro_body: subtree.clone(),
50 macro_name: self.name.to_string(), 51 macro_name: self.name.to_string(),
51 attributes: attr.cloned(), 52 attributes: attr.cloned(),
52 lib: self.dylib_path.to_path_buf(), 53 lib: self.dylib_path.to_path_buf(),
54 env: env.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(),
53 }; 55 };
54 56
55 let result: ExpansionResult = self.process.send_task(msg::Request::ExpansionMacro(task))?; 57 let result: ExpansionResult = self.process.send_task(msg::Request::ExpansionMacro(task))?;
@@ -90,7 +92,7 @@ impl ProcMacroClient {
90 ProcMacroKind::FuncLike => base_db::ProcMacroKind::FuncLike, 92 ProcMacroKind::FuncLike => base_db::ProcMacroKind::FuncLike,
91 ProcMacroKind::Attr => base_db::ProcMacroKind::Attr, 93 ProcMacroKind::Attr => base_db::ProcMacroKind::Attr,
92 }; 94 };
93 let expander: Arc<dyn tt::TokenExpander> = Arc::new(ProcMacroProcessExpander { 95 let expander = Arc::new(ProcMacroProcessExpander {
94 process: self.process.clone(), 96 process: self.process.clone(),
95 name: name.clone(), 97 name: name.clone(),
96 dylib_path: dylib_path.into(), 98 dylib_path: dylib_path.into(),
diff --git a/crates/proc_macro_api/src/rpc.rs b/crates/proc_macro_api/src/rpc.rs
index b85f92eea..cf830b59f 100644
--- a/crates/proc_macro_api/src/rpc.rs
+++ b/crates/proc_macro_api/src/rpc.rs
@@ -51,6 +51,9 @@ pub struct ExpansionTask {
51 pub attributes: Option<Subtree>, 51 pub attributes: Option<Subtree>,
52 52
53 pub lib: PathBuf, 53 pub lib: PathBuf,
54
55 /// Environment variables to set during macro expansion.
56 pub env: Vec<(String, String)>,
54} 57}
55 58
56#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] 59#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
@@ -251,6 +254,7 @@ mod tests {
251 macro_name: Default::default(), 254 macro_name: Default::default(),
252 attributes: None, 255 attributes: None,
253 lib: Default::default(), 256 lib: Default::default(),
257 env: Default::default(),
254 }; 258 };
255 259
256 let json = serde_json::to_string(&task).unwrap(); 260 let json = serde_json::to_string(&task).unwrap();
diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs
index 2afb973cc..4e719f3aa 100644
--- a/crates/proc_macro_srv/src/dylib.rs
+++ b/crates/proc_macro_srv/src/dylib.rs
@@ -136,6 +136,7 @@ impl Expander {
136 &crate::proc_macro::bridge::server::SameThread, 136 &crate::proc_macro::bridge::server::SameThread,
137 crate::rustc_server::Rustc::default(), 137 crate::rustc_server::Rustc::default(),
138 parsed_body, 138 parsed_body,
139 false,
139 ); 140 );
140 return res.map(|it| it.subtree); 141 return res.map(|it| it.subtree);
141 } 142 }
@@ -144,6 +145,7 @@ impl Expander {
144 &crate::proc_macro::bridge::server::SameThread, 145 &crate::proc_macro::bridge::server::SameThread,
145 crate::rustc_server::Rustc::default(), 146 crate::rustc_server::Rustc::default(),
146 parsed_body, 147 parsed_body,
148 false,
147 ); 149 );
148 return res.map(|it| it.subtree); 150 return res.map(|it| it.subtree);
149 } 151 }
@@ -153,6 +155,7 @@ impl Expander {
153 crate::rustc_server::Rustc::default(), 155 crate::rustc_server::Rustc::default(),
154 parsed_attributes, 156 parsed_attributes,
155 parsed_body, 157 parsed_body,
158 false,
156 ); 159 );
157 return res.map(|it| it.subtree); 160 return res.map(|it| it.subtree);
158 } 161 }
diff --git a/crates/proc_macro_srv/src/lib.rs b/crates/proc_macro_srv/src/lib.rs
index 9cca96994..d4f04ee06 100644
--- a/crates/proc_macro_srv/src/lib.rs
+++ b/crates/proc_macro_srv/src/lib.rs
@@ -24,7 +24,7 @@ use proc_macro::bridge::client::TokenStream;
24use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; 24use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
25use std::{ 25use std::{
26 collections::{hash_map::Entry, HashMap}, 26 collections::{hash_map::Entry, HashMap},
27 fs, 27 env, fs,
28 path::{Path, PathBuf}, 28 path::{Path, PathBuf},
29 time::SystemTime, 29 time::SystemTime,
30}; 30};
@@ -37,7 +37,23 @@ pub(crate) struct ProcMacroSrv {
37impl ProcMacroSrv { 37impl ProcMacroSrv {
38 pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> { 38 pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> {
39 let expander = self.expander(&task.lib)?; 39 let expander = self.expander(&task.lib)?;
40 match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) { 40
41 let mut prev_env = HashMap::new();
42 for (k, v) in &task.env {
43 prev_env.insert(k.as_str(), env::var_os(k));
44 env::set_var(k, v);
45 }
46
47 let result = expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref());
48
49 for (k, _) in &task.env {
50 match &prev_env[k.as_str()] {
51 Some(v) => env::set_var(k, v),
52 None => env::remove_var(k),
53 }
54 }
55
56 match result {
41 Ok(expansion) => Ok(ExpansionResult { expansion }), 57 Ok(expansion) => Ok(ExpansionResult { expansion }),
42 Err(msg) => { 58 Err(msg) => {
43 let msg = msg.as_str().unwrap_or("<unknown error>"); 59 let msg = msg.as_str().unwrap_or("<unknown error>");
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
index 55d6330cc..ca6749b9b 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
@@ -303,17 +303,21 @@ impl BridgeState<'_> {
303 303
304impl Bridge<'_> { 304impl Bridge<'_> {
305 fn enter<R>(self, f: impl FnOnce() -> R) -> R { 305 fn enter<R>(self, f: impl FnOnce() -> R) -> R {
306 let force_show_panics = self.force_show_panics;
307
306 // Hide the default panic output within `proc_macro` expansions. 308 // Hide the default panic output within `proc_macro` expansions.
307 // NB. the server can't do this because it may use a different libstd. 309 // NB. the server can't do this because it may use a different libstd.
308 static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); 310 static HIDE_PANICS_DURING_EXPANSION: Once = Once::new();
309 HIDE_PANICS_DURING_EXPANSION.call_once(|| { 311 HIDE_PANICS_DURING_EXPANSION.call_once(|| {
310 let prev = panic::take_hook(); 312 let prev = panic::take_hook();
311 panic::set_hook(Box::new(move |info| { 313 panic::set_hook(Box::new(move |info| {
312 let hide = BridgeState::with(|state| match state { 314 let show = BridgeState::with(|state| match state {
313 BridgeState::NotConnected => false, 315 BridgeState::NotConnected => true,
314 BridgeState::Connected(_) | BridgeState::InUse => true, 316 // Something weird is going on, so don't suppress any backtraces
317 BridgeState::InUse => true,
318 BridgeState::Connected(bridge) => force_show_panics,
315 }); 319 });
316 if !hide { 320 if show {
317 prev(info) 321 prev(info)
318 } 322 }
319 })); 323 }));
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
index b97886eb9..e4006a5ab 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
@@ -225,6 +225,9 @@ pub struct Bridge<'a> {
225 225
226 /// Server-side function that the client uses to make requests. 226 /// Server-side function that the client uses to make requests.
227 dispatch: closure::Closure<'a, Buffer<u8>, Buffer<u8>>, 227 dispatch: closure::Closure<'a, Buffer<u8>, Buffer<u8>>,
228
229 /// If 'true', always invoke the default panic hook
230 force_show_panics: bool,
228} 231}
229 232
230// impl<'a> !Sync for Bridge<'a> {} 233// impl<'a> !Sync for Bridge<'a> {}
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
index 3acb239af..88fbdc078 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
@@ -138,6 +138,7 @@ pub trait ExecutionStrategy {
138 input: Buffer<u8>, 138 input: Buffer<u8>,
139 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, 139 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
140 client_data: D, 140 client_data: D,
141 force_show_panics: bool,
141 ) -> Buffer<u8>; 142 ) -> Buffer<u8>;
142} 143}
143 144
@@ -150,10 +151,14 @@ impl ExecutionStrategy for SameThread {
150 input: Buffer<u8>, 151 input: Buffer<u8>,
151 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, 152 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
152 client_data: D, 153 client_data: D,
154 force_show_panics: bool,
153 ) -> Buffer<u8> { 155 ) -> Buffer<u8> {
154 let mut dispatch = |b| dispatcher.dispatch(b); 156 let mut dispatch = |b| dispatcher.dispatch(b);
155 157
156 run_client(Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, client_data) 158 run_client(
159 Bridge { cached_buffer: input, dispatch: (&mut dispatch).into(), force_show_panics },
160 client_data,
161 )
157 } 162 }
158} 163}
159 164
@@ -169,6 +174,7 @@ impl ExecutionStrategy for CrossThread1 {
169 input: Buffer<u8>, 174 input: Buffer<u8>,
170 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, 175 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
171 client_data: D, 176 client_data: D,
177 force_show_panics: bool,
172 ) -> Buffer<u8> { 178 ) -> Buffer<u8> {
173 use std::sync::mpsc::channel; 179 use std::sync::mpsc::channel;
174 180
@@ -182,7 +188,11 @@ impl ExecutionStrategy for CrossThread1 {
182 }; 188 };
183 189
184 run_client( 190 run_client(
185 Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, 191 Bridge {
192 cached_buffer: input,
193 dispatch: (&mut dispatch).into(),
194 force_show_panics,
195 },
186 client_data, 196 client_data,
187 ) 197 )
188 }); 198 });
@@ -204,6 +214,7 @@ impl ExecutionStrategy for CrossThread2 {
204 input: Buffer<u8>, 214 input: Buffer<u8>,
205 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, 215 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
206 client_data: D, 216 client_data: D,
217 force_show_panics: bool,
207 ) -> Buffer<u8> { 218 ) -> Buffer<u8> {
208 use std::sync::{Arc, Mutex}; 219 use std::sync::{Arc, Mutex};
209 220
@@ -229,7 +240,11 @@ impl ExecutionStrategy for CrossThread2 {
229 }; 240 };
230 241
231 let r = run_client( 242 let r = run_client(
232 Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, 243 Bridge {
244 cached_buffer: input,
245 dispatch: (&mut dispatch).into(),
246 force_show_panics,
247 },
233 client_data, 248 client_data,
234 ); 249 );
235 250
@@ -268,6 +283,7 @@ fn run_server<
268 input: I, 283 input: I,
269 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, 284 run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
270 client_data: D, 285 client_data: D,
286 force_show_panics: bool,
271) -> Result<O, PanicMessage> { 287) -> Result<O, PanicMessage> {
272 let mut dispatcher = 288 let mut dispatcher =
273 Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; 289 Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) };
@@ -275,7 +291,13 @@ fn run_server<
275 let mut b = Buffer::new(); 291 let mut b = Buffer::new();
276 input.encode(&mut b, &mut dispatcher.handle_store); 292 input.encode(&mut b, &mut dispatcher.handle_store);
277 293
278 b = strategy.run_bridge_and_client(&mut dispatcher, b, run_client, client_data); 294 b = strategy.run_bridge_and_client(
295 &mut dispatcher,
296 b,
297 run_client,
298 client_data,
299 force_show_panics,
300 );
279 301
280 Result::decode(&mut &b[..], &mut dispatcher.handle_store) 302 Result::decode(&mut &b[..], &mut dispatcher.handle_store)
281} 303}
@@ -286,6 +308,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
286 strategy: &impl ExecutionStrategy, 308 strategy: &impl ExecutionStrategy,
287 server: S, 309 server: S,
288 input: S::TokenStream, 310 input: S::TokenStream,
311 force_show_panics: bool,
289 ) -> Result<S::TokenStream, PanicMessage> { 312 ) -> Result<S::TokenStream, PanicMessage> {
290 let client::Client { get_handle_counters, run, f } = *self; 313 let client::Client { get_handle_counters, run, f } = *self;
291 run_server( 314 run_server(
@@ -295,6 +318,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
295 <MarkedTypes<S> as Types>::TokenStream::mark(input), 318 <MarkedTypes<S> as Types>::TokenStream::mark(input),
296 run, 319 run,
297 f, 320 f,
321 force_show_panics,
298 ) 322 )
299 .map(<MarkedTypes<S> as Types>::TokenStream::unmark) 323 .map(<MarkedTypes<S> as Types>::TokenStream::unmark)
300 } 324 }
@@ -307,6 +331,7 @@ impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenSt
307 server: S, 331 server: S,
308 input: S::TokenStream, 332 input: S::TokenStream,
309 input2: S::TokenStream, 333 input2: S::TokenStream,
334 force_show_panics: bool,
310 ) -> Result<S::TokenStream, PanicMessage> { 335 ) -> Result<S::TokenStream, PanicMessage> {
311 let client::Client { get_handle_counters, run, f } = *self; 336 let client::Client { get_handle_counters, run, f } = *self;
312 run_server( 337 run_server(
@@ -319,6 +344,7 @@ impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenSt
319 ), 344 ),
320 run, 345 run,
321 f, 346 f,
347 force_show_panics,
322 ) 348 )
323 .map(<MarkedTypes<S> as Types>::TokenStream::unmark) 349 .map(<MarkedTypes<S> as Types>::TokenStream::unmark)
324 } 350 }
diff --git a/crates/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/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 5a1ae96aa..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,
@@ -778,6 +780,7 @@ pub(crate) fn unresolved_code_action(
778 assist: Assist, 780 assist: Assist,
779 index: usize, 781 index: usize,
780) -> Result<lsp_ext::CodeAction> { 782) -> Result<lsp_ext::CodeAction> {
783 assert!(assist.source_change.is_none());
781 let res = lsp_ext::CodeAction { 784 let res = lsp_ext::CodeAction {
782 title: assist.label.to_string(), 785 title: assist.label.to_string(),
783 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),
@@ -794,18 +797,14 @@ pub(crate) fn unresolved_code_action(
794 797
795pub(crate) fn resolved_code_action( 798pub(crate) fn resolved_code_action(
796 snap: &GlobalStateSnapshot, 799 snap: &GlobalStateSnapshot,
797 assist: ResolvedAssist, 800 assist: Assist,
798) -> Result<lsp_ext::CodeAction> { 801) -> Result<lsp_ext::CodeAction> {
799 let change = assist.source_change; 802 let change = assist.source_change.unwrap();
800 let res = lsp_ext::CodeAction { 803 let res = lsp_ext::CodeAction {
801 edit: Some(snippet_workspace_edit(snap, change)?), 804 edit: Some(snippet_workspace_edit(snap, change)?),
802 title: assist.assist.label.to_string(), 805 title: assist.label.to_string(),
803 group: assist 806 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
804 .assist 807 kind: Some(code_action_kind(assist.id.1)),
805 .group
806 .filter(|_| snap.config.client_caps.code_action_group)
807 .map(|gr| gr.0),
808 kind: Some(code_action_kind(assist.assist.id.1)),
809 is_preferred: None, 808 is_preferred: None,
810 data: None, 809 data: None,
811 }; 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/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast b/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast
new file mode 100644
index 000000000..4a1f712aa
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast
@@ -0,0 +1,57 @@
1[email protected]
2 [email protected]
3 [email protected] "macro_rules"
4 [email protected] "!"
5 [email protected] " "
6 [email protected]
7 [email protected] "m"
8 [email protected] " "
9 [email protected]
10 [email protected] "("
11 [email protected] " "
12 [email protected]
13 [email protected] "("
14 [email protected] "$"
15 [email protected] "i"
16 [email protected] ":"
17 [email protected] "ident"
18 [email protected] ")"
19 [email protected] " "
20 [email protected] "="
21 [email protected] ">"
22 [email protected] " "
23 [email protected]
24 [email protected] "{"
25 [email protected] "}"
26 [email protected] " "
27 [email protected] ")"
28 [email protected] ";"
29 [email protected] "\n"
30 [email protected]
31 [email protected] "macro_rules"
32 [email protected] "!"
33 [email protected] " "
34 [email protected]
35 [email protected] "m"
36 [email protected] " "
37 [email protected]
38 [email protected] "["
39 [email protected] " "
40 [email protected]
41 [email protected] "("
42 [email protected] "$"
43 [email protected] "i"
44 [email protected] ":"
45 [email protected] "ident"
46 [email protected] ")"
47 [email protected] " "
48 [email protected] "="
49 [email protected] ">"
50 [email protected] " "
51 [email protected]
52 [email protected] "{"
53 [email protected] "}"
54 [email protected] " "
55 [email protected] "]"
56 [email protected] ";"
57 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs b/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs
new file mode 100644
index 000000000..6033a28cd
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs
@@ -0,0 +1,2 @@
1macro_rules! m ( ($i:ident) => {} );
2macro_rules! m [ ($i:ident) => {} ];
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index 6c1bf8d09..8301dc28a 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -1,7 +1,7 @@
1//! `tt` crate defines a `TokenTree` data structure: this is the interface (both 1//! `tt` crate defines a `TokenTree` data structure: this is the interface (both
2//! input and output) of macros. It closely mirrors `proc_macro` crate's 2//! input and output) of macros. It closely mirrors `proc_macro` crate's
3//! `TokenTree`. 3//! `TokenTree`.
4use std::{fmt, panic::RefUnwindSafe}; 4use std::fmt;
5 5
6use stdx::impl_from; 6use stdx::impl_from;
7 7
@@ -247,8 +247,3 @@ impl fmt::Display for ExpansionError {
247 } 247 }
248 } 248 }
249} 249}
250
251pub trait TokenExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
252 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
253 -> Result<Subtree, ExpansionError>;
254}
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 13c6a2a16..58b309379 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -63,6 +63,18 @@ After you are happy with the state of the code, please use [interactive rebase](
63Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors). 63Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors).
64Such messages create a lot of duplicate notification traffic during rebases. 64Such messages create a lot of duplicate notification traffic during rebases.
65 65
66If possible, write commit messages from user's perspective:
67
68```
69# Good
70Goto definition works inside macros
71
72# Not as good
73Use original span for FileId
74```
75
76This makes it easier to prepare a changelog.
77
66## Clippy 78## Clippy
67 79
68We don't enforce Clippy. 80We don't enforce Clippy.
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index e3082d584..d4121b401 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -218,7 +218,7 @@ The are several LSP client implementations for vim or neovim:
218 * automatically install and upgrade stable/nightly releases 218 * automatically install and upgrade stable/nightly releases
219 * same configurations as VSCode extension, `rust-analyzer.serverPath`, `rust-analyzer.cargo.features` etc. 219 * same configurations as VSCode extension, `rust-analyzer.serverPath`, `rust-analyzer.cargo.features` etc.
220 * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc. 220 * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc.
221 * inlay hints for method chaining support, _Neovim Only_ 221 * inlay hints for variables and method chaining, _Neovim Only_
222 * semantic highlighting is not implemented yet 222 * semantic highlighting is not implemented yet
223 223
224==== LanguageClient-neovim 224==== LanguageClient-neovim