aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-03-21 00:29:15 +0000
committerGitHub <[email protected]>2021-03-21 00:29:15 +0000
commita0ed87ff56e83eb03910b58a9cb80c35c5639338 (patch)
tree9360702f2aaf0c4672a9ed74c51604a8353662a0 /crates
parent787bd3c5516d250245f6070308d689311b638fbe (diff)
parent64957acb5f359763395a54e314d1f5d5cfc6ccf3 (diff)
Merge #8127
8127: Add label completion r=Veykril a=Veykril Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs1
-rw-r--r--crates/hir/src/semantics.rs4
-rw-r--r--crates/hir_def/src/body/scope.rs57
-rw-r--r--crates/hir_def/src/resolver.rs6
-rw-r--r--crates/ide_completion/src/completions/lifetime.rs106
-rw-r--r--crates/ide_completion/src/context.rs26
-rw-r--r--crates/ide_completion/src/lib.rs1
-rw-r--r--crates/ide_completion/src/render.rs1
8 files changed, 184 insertions, 18 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 30e577671..e34be7e42 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2199,6 +2199,7 @@ pub enum ScopeDef {
2199 ImplSelfType(Impl), 2199 ImplSelfType(Impl),
2200 AdtSelfType(Adt), 2200 AdtSelfType(Adt),
2201 Local(Local), 2201 Local(Local),
2202 Label(Label),
2202 Unknown, 2203 Unknown,
2203} 2204}
2204 2205
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 15651bb22..1198e3f0b 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -839,6 +839,10 @@ impl<'a> SemanticsScope<'a> {
839 let parent = resolver.body_owner().unwrap(); 839 let parent = resolver.body_owner().unwrap();
840 ScopeDef::Local(Local { parent, pat_id }) 840 ScopeDef::Local(Local { parent, pat_id })
841 } 841 }
842 resolver::ScopeDef::Label(label_id) => {
843 let parent = resolver.body_owner().unwrap();
844 ScopeDef::Label(Label { parent, label_id })
845 }
842 }; 846 };
843 f(name, def) 847 f(name, def)
844 }) 848 })
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 1bbb54fc6..bd7005ca6 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
8use crate::{ 8use crate::{
9 body::Body, 9 body::Body,
10 db::DefDatabase, 10 db::DefDatabase,
11 expr::{Expr, ExprId, Pat, PatId, Statement}, 11 expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
12 BlockId, DefWithBodyId, 12 BlockId, DefWithBodyId,
13}; 13};
14 14
@@ -40,6 +40,7 @@ impl ScopeEntry {
40pub struct ScopeData { 40pub struct ScopeData {
41 parent: Option<ScopeId>, 41 parent: Option<ScopeId>,
42 block: Option<BlockId>, 42 block: Option<BlockId>,
43 label: Option<(LabelId, Name)>,
43 entries: Vec<ScopeEntry>, 44 entries: Vec<ScopeEntry>,
44} 45}
45 46
@@ -67,6 +68,11 @@ impl ExprScopes {
67 self.scopes[scope].block 68 self.scopes[scope].block
68 } 69 }
69 70
71 /// If `scope` refers to a labeled expression scope, returns the corresponding `Label`.
72 pub fn label(&self, scope: ScopeId) -> Option<(LabelId, Name)> {
73 self.scopes[scope].label.clone()
74 }
75
70 pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ { 76 pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
71 std::iter::successors(scope, move |&scope| self.scopes[scope].parent) 77 std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
72 } 78 }
@@ -85,15 +91,34 @@ impl ExprScopes {
85 } 91 }
86 92
87 fn root_scope(&mut self) -> ScopeId { 93 fn root_scope(&mut self) -> ScopeId {
88 self.scopes.alloc(ScopeData { parent: None, block: None, entries: vec![] }) 94 self.scopes.alloc(ScopeData { parent: None, block: None, label: None, entries: vec![] })
89 } 95 }
90 96
91 fn new_scope(&mut self, parent: ScopeId) -> ScopeId { 97 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
92 self.scopes.alloc(ScopeData { parent: Some(parent), block: None, entries: vec![] }) 98 self.scopes.alloc(ScopeData {
99 parent: Some(parent),
100 block: None,
101 label: None,
102 entries: vec![],
103 })
93 } 104 }
94 105
95 fn new_block_scope(&mut self, parent: ScopeId, block: BlockId) -> ScopeId { 106 fn new_labeled_scope(&mut self, parent: ScopeId, label: Option<(LabelId, Name)>) -> ScopeId {
96 self.scopes.alloc(ScopeData { parent: Some(parent), block: Some(block), entries: vec![] }) 107 self.scopes.alloc(ScopeData { parent: Some(parent), block: None, label, entries: vec![] })
108 }
109
110 fn new_block_scope(
111 &mut self,
112 parent: ScopeId,
113 block: BlockId,
114 label: Option<(LabelId, Name)>,
115 ) -> ScopeId {
116 self.scopes.alloc(ScopeData {
117 parent: Some(parent),
118 block: Some(block),
119 label,
120 entries: vec![],
121 })
97 } 122 }
98 123
99 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { 124 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
@@ -144,21 +169,33 @@ fn compute_block_scopes(
144} 169}
145 170
146fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { 171fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
172 let make_label =
173 |label: &Option<_>| label.map(|label| (label, body.labels[label].name.clone()));
174
147 scopes.set_scope(expr, scope); 175 scopes.set_scope(expr, scope);
148 match &body[expr] { 176 match &body[expr] {
149 Expr::Block { statements, tail, id, .. } => { 177 Expr::Block { statements, tail, id, label } => {
150 let scope = scopes.new_block_scope(scope, *id); 178 let scope = scopes.new_block_scope(scope, *id, make_label(label));
151 // Overwrite the old scope for the block expr, so that every block scope can be found 179 // Overwrite the old scope for the block expr, so that every block scope can be found
152 // via the block itself (important for blocks that only contain items, no expressions). 180 // via the block itself (important for blocks that only contain items, no expressions).
153 scopes.set_scope(expr, scope); 181 scopes.set_scope(expr, scope);
154 compute_block_scopes(&statements, *tail, body, scopes, scope); 182 compute_block_scopes(statements, *tail, body, scopes, scope);
155 } 183 }
156 Expr::For { iterable, pat, body: body_expr, .. } => { 184 Expr::For { iterable, pat, body: body_expr, label } => {
157 compute_expr_scopes(*iterable, body, scopes, scope); 185 compute_expr_scopes(*iterable, body, scopes, scope);
158 let scope = scopes.new_scope(scope); 186 let scope = scopes.new_labeled_scope(scope, make_label(label));
159 scopes.add_bindings(body, scope, *pat); 187 scopes.add_bindings(body, scope, *pat);
160 compute_expr_scopes(*body_expr, body, scopes, scope); 188 compute_expr_scopes(*body_expr, body, scopes, scope);
161 } 189 }
190 Expr::While { condition, body: body_expr, label } => {
191 let scope = scopes.new_labeled_scope(scope, make_label(label));
192 compute_expr_scopes(*condition, body, scopes, scope);
193 compute_expr_scopes(*body_expr, body, scopes, scope);
194 }
195 Expr::Loop { body: body_expr, label } => {
196 let scope = scopes.new_labeled_scope(scope, make_label(label));
197 compute_expr_scopes(*body_expr, body, scopes, scope);
198 }
162 Expr::Lambda { args, body: body_expr, .. } => { 199 Expr::Lambda { args, body: body_expr, .. } => {
163 let scope = scopes.new_scope(scope); 200 let scope = scopes.new_scope(scope);
164 scopes.add_params_bindings(body, scope, &args); 201 scopes.add_params_bindings(body, scope, &args);
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 42736171e..4a2d1c087 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -12,7 +12,7 @@ use crate::{
12 body::scope::{ExprScopes, ScopeId}, 12 body::scope::{ExprScopes, ScopeId},
13 builtin_type::BuiltinType, 13 builtin_type::BuiltinType,
14 db::DefDatabase, 14 db::DefDatabase,
15 expr::{ExprId, PatId}, 15 expr::{ExprId, LabelId, PatId},
16 generics::GenericParams, 16 generics::GenericParams,
17 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, 17 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
18 nameres::DefMap, 18 nameres::DefMap,
@@ -409,6 +409,7 @@ pub enum ScopeDef {
409 AdtSelfType(AdtId), 409 AdtSelfType(AdtId),
410 GenericParam(GenericParamId), 410 GenericParam(GenericParamId),
411 Local(PatId), 411 Local(PatId),
412 Label(LabelId),
412} 413}
413 414
414impl Scope { 415impl Scope {
@@ -470,6 +471,9 @@ impl Scope {
470 f(name![Self], ScopeDef::AdtSelfType(*i)); 471 f(name![Self], ScopeDef::AdtSelfType(*i));
471 } 472 }
472 Scope::ExprScope(scope) => { 473 Scope::ExprScope(scope) => {
474 if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) {
475 f(name.clone(), ScopeDef::Label(label))
476 }
473 scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| { 477 scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| {
474 f(e.name().clone(), ScopeDef::Local(e.pat())); 478 f(e.name().clone(), ScopeDef::Local(e.pat()));
475 }); 479 });
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
index 74eb23360..628c1fb9b 100644
--- a/crates/ide_completion/src/completions/lifetime.rs
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -1,4 +1,4 @@
1//! Completes lifetimes. 1//! Completes lifetimes and labels.
2use hir::ScopeDef; 2use hir::ScopeDef;
3 3
4use crate::{completions::Completions, context::CompletionContext}; 4use crate::{completions::Completions, context::CompletionContext};
@@ -29,6 +29,18 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext)
29 } 29 }
30} 30}
31 31
32/// Completes labels.
33pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
34 if !ctx.is_label_ref {
35 return;
36 }
37 ctx.scope.process_all_names(&mut |name, res| {
38 if let ScopeDef::Label(_) = res {
39 acc.add_resolution(ctx, name.to_string(), &res);
40 }
41 });
42}
43
32#[cfg(test)] 44#[cfg(test)]
33mod tests { 45mod tests {
34 use expect_test::{expect, Expect}; 46 use expect_test::{expect, Expect};
@@ -178,4 +190,96 @@ fn foo<'footime, 'lifetime: 'a$0>() {}
178 "#]], 190 "#]],
179 ); 191 );
180 } 192 }
193
194 #[test]
195 fn complete_label_in_loop() {
196 check(
197 r#"
198fn foo() {
199 'foop: loop {
200 break '$0
201 }
202}
203"#,
204 expect![[r#"
205 lb 'foop
206 "#]],
207 );
208 check(
209 r#"
210fn foo() {
211 'foop: loop {
212 continue '$0
213 }
214}
215"#,
216 expect![[r#"
217 lb 'foop
218 "#]],
219 );
220 }
221
222 #[test]
223 fn complete_label_in_block_nested() {
224 check(
225 r#"
226fn foo() {
227 'foop: {
228 'baap: {
229 break '$0
230 }
231 }
232}
233"#,
234 expect![[r#"
235 lb 'baap
236 lb 'foop
237 "#]],
238 );
239 }
240
241 #[test]
242 fn complete_label_in_loop_with_value() {
243 check(
244 r#"
245fn foo() {
246 'foop: loop {
247 break '$0 i32;
248 }
249}
250"#,
251 expect![[r#"
252 lb 'foop
253 "#]],
254 );
255 }
256
257 #[test]
258 fn complete_label_in_while_cond() {
259 check(
260 r#"
261fn foo() {
262 'outer: while { 'inner: loop { break '$0 } } {}
263}
264"#,
265 expect![[r#"
266 lb 'inner
267 lb 'outer
268 "#]],
269 );
270 }
271
272 #[test]
273 fn complete_label_in_for_iterable() {
274 check(
275 r#"
276fn foo() {
277 'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
278}
279"#,
280 expect![[r#"
281 lb 'inner
282 "#]],
283 );
284 }
181} 285}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 4c2b31084..67e2d6f6c 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -53,6 +53,7 @@ pub(crate) struct CompletionContext<'a> {
53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
54 pub(super) active_parameter: Option<ActiveParameter>, 54 pub(super) active_parameter: Option<ActiveParameter>,
55 pub(super) is_param: bool, 55 pub(super) is_param: bool,
56 pub(super) is_label_ref: bool,
56 /// If a name-binding or reference to a const in a pattern. 57 /// If a name-binding or reference to a const in a pattern.
57 /// Irrefutable patterns (like let) are excluded. 58 /// Irrefutable patterns (like let) are excluded.
58 pub(super) is_pat_binding_or_const: bool, 59 pub(super) is_pat_binding_or_const: bool,
@@ -155,6 +156,7 @@ impl<'a> CompletionContext<'a> {
155 record_field_syntax: None, 156 record_field_syntax: None,
156 impl_def: None, 157 impl_def: None,
157 active_parameter: ActiveParameter::at(db, position), 158 active_parameter: ActiveParameter::at(db, position),
159 is_label_ref: false,
158 is_param: false, 160 is_param: false,
159 is_pat_binding_or_const: false, 161 is_pat_binding_or_const: false,
160 is_irrefutable_pat_binding: false, 162 is_irrefutable_pat_binding: false,
@@ -468,12 +470,24 @@ impl<'a> CompletionContext<'a> {
468 ) { 470 ) {
469 self.lifetime_syntax = 471 self.lifetime_syntax =
470 find_node_at_offset(original_file, lifetime.syntax().text_range().start()); 472 find_node_at_offset(original_file, lifetime.syntax().text_range().start());
471 if lifetime.syntax().parent().map_or(false, |p| p.kind() != syntax::SyntaxKind::ERROR) { 473 if let Some(parent) = lifetime.syntax().parent() {
472 self.lifetime_allowed = true; 474 if parent.kind() == syntax::SyntaxKind::ERROR {
473 } 475 return;
474 if let Some(_) = lifetime.syntax().parent().and_then(ast::LifetimeParam::cast) { 476 }
475 self.lifetime_param_syntax = 477
476 self.sema.find_node_at_offset_with_macros(original_file, offset); 478 match_ast! {
479 match parent {
480 ast::LifetimeParam(_it) => {
481 self.lifetime_allowed = true;
482 self.lifetime_param_syntax =
483 self.sema.find_node_at_offset_with_macros(original_file, offset);
484 },
485 ast::BreakExpr(_it) => self.is_label_ref = true,
486 ast::ContinueExpr(_it) => self.is_label_ref = true,
487 ast::Label(_it) => (),
488 _ => self.lifetime_allowed = true,
489 }
490 }
477 } 491 }
478 } 492 }
479 493
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 7a0eb6a96..995970fca 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -131,6 +131,7 @@ pub fn completions(
131 completions::mod_::complete_mod(&mut acc, &ctx); 131 completions::mod_::complete_mod(&mut acc, &ctx);
132 completions::flyimport::import_on_the_fly(&mut acc, &ctx); 132 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
133 completions::lifetime::complete_lifetime(&mut acc, &ctx); 133 completions::lifetime::complete_lifetime(&mut acc, &ctx);
134 completions::lifetime::complete_label(&mut acc, &ctx);
134 135
135 Some(acc) 136 Some(acc)
136} 137}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 2b6e9ebd1..23e00aa47 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -219,6 +219,7 @@ impl<'a> Render<'a> {
219 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, 219 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
220 }), 220 }),
221 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), 221 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
222 ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
222 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => { 223 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
223 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 224 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
224 } 225 }