Skip to content

Commit cd7cbdd

Browse files
When resolving labels in break and continue for the IDE, do not resolve them textually, instead reuse the results of HIR lowering
This fixes a bug where labels inside macros were not resolved, but more importantly this prepares us to a future where we have hygiene, and textual equivalence isn't enough to resolve identifiers.
1 parent 9798cf8 commit cd7cbdd

File tree

3 files changed

+40
-24
lines changed

3 files changed

+40
-24
lines changed

crates/hir/src/semantics.rs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ use span::{EditionedFileId, FileId, HirFileIdRepr};
3636
use stdx::TupleExt;
3737
use syntax::{
3838
algo::skip_trivia_token,
39-
ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody, IsString as _},
40-
match_ast, AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
41-
TextRange, TextSize,
39+
ast::{self, HasAttrs as _, HasGenericParams, IsString as _},
40+
AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
41+
TextSize,
4242
};
4343

4444
use crate::{
@@ -1221,26 +1221,10 @@ impl<'db> SemanticsImpl<'db> {
12211221
ToDef::to_def(self, src.as_ref())
12221222
}
12231223

1224-
pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
1225-
let text = lifetime.text();
1226-
let label = lifetime.syntax().ancestors().find_map(|syn| {
1227-
let label = match_ast! {
1228-
match syn {
1229-
ast::ForExpr(it) => it.label(),
1230-
ast::WhileExpr(it) => it.label(),
1231-
ast::LoopExpr(it) => it.label(),
1232-
ast::BlockExpr(it) => it.label(),
1233-
_ => None,
1234-
}
1235-
};
1236-
label.filter(|l| {
1237-
l.lifetime()
1238-
.and_then(|lt| lt.lifetime_ident_token())
1239-
.map_or(false, |lt| lt.text() == text)
1240-
})
1241-
})?;
1242-
let src = self.wrap_node_infile(label);
1243-
ToDef::to_def(self, src.as_ref())
1224+
pub fn resolve_label(&self, label: &ast::Lifetime) -> Option<Label> {
1225+
let (parent, label_id) = self
1226+
.with_ctx(|ctx| ctx.label_ref_to_def(self.wrap_node_infile(label.clone()).as_ref()))?;
1227+
Some(Label { parent, label_id })
12441228
}
12451229

12461230
pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {

crates/hir/src/semantics/source_to_def.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ use hir_def::{
9292
keys::{self, Key},
9393
DynMap,
9494
},
95-
hir::{BindingId, LabelId},
95+
hir::{BindingId, Expr, LabelId},
9696
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId,
9797
FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, Lookup, MacroId,
9898
ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId,
@@ -343,6 +343,20 @@ impl SourceToDefCtx<'_, '_> {
343343
Some((container, label_id))
344344
}
345345

346+
pub(super) fn label_ref_to_def(
347+
&mut self,
348+
src: InFile<&ast::Lifetime>,
349+
) -> Option<(DefWithBodyId, LabelId)> {
350+
let break_or_continue = ast::Expr::cast(src.value.syntax().parent()?)?;
351+
let container = self.find_pat_or_label_container(src.syntax_ref())?;
352+
let (body, source_map) = self.db.body_with_source_map(container);
353+
let break_or_continue = source_map.node_expr(src.with_value(&break_or_continue))?;
354+
let (Expr::Break { label, .. } | Expr::Continue { label }) = body[break_or_continue] else {
355+
return None;
356+
};
357+
Some((container, label?))
358+
}
359+
346360
pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
347361
let map = self.dyn_map(src)?;
348362
map[keys::ATTR_MACRO_CALL].get(&AstPtr::new(src.value)).copied()

crates/ide/src/goto_definition.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2660,6 +2660,24 @@ fn foo() {
26602660
);
26612661
}
26622662

2663+
#[test]
2664+
fn label_inside_macro() {
2665+
check(
2666+
r#"
2667+
macro_rules! m {
2668+
($s:stmt) => { $s };
2669+
}
2670+
2671+
fn foo() {
2672+
'label: loop {
2673+
// ^^^^^^
2674+
m!(continue 'label$0);
2675+
}
2676+
}
2677+
"#,
2678+
);
2679+
}
2680+
26632681
#[test]
26642682
fn goto_def_on_return_in_try() {
26652683
check(

0 commit comments

Comments
 (0)