Skip to content

Commit 52e8eaa

Browse files
committed
Auto merge of #18210 - ChayimFriedman2:label-macro, r=Veykril
fix: Fix resolution of label inside macro When working on Something Else (TM) (I left a hint in the commits :P), I noticed to my surprise that labels inside macros are not resolved. This led to a discovery of *two* unrelated bugs, which are hereby fixed in two commits.
2 parents 4800a0e + d073a85 commit 52e8eaa

5 files changed

Lines changed: 71 additions & 27 deletions

File tree

‎src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ macro_rules! f {
3636
}
3737
3838
struct#0:1@58..64#1# MyTraitMap2#0:2@31..42#0# {#0:1@72..73#1#
39-
map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..92#1#std#0:1@93..96#1#::#0:1@96..97#1#collections#0:1@98..109#1#::#0:1@109..110#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1#
39+
map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..93#1#std#0:1@93..96#1#::#0:1@96..98#1#collections#0:1@98..109#1#::#0:1@109..111#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1#
4040
}#0:1@132..133#1#
4141
"#]],
4242
);

‎src/tools/rust-analyzer/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,HasAttrsas _,HasGenericParams,HasLoopBody,IsStringas _},
40-
match_ast,AstNode,AstToken,Direction,SyntaxKind,SyntaxNode,SyntaxNodePtr,SyntaxToken,
41-
TextRange,TextSize,
39+
ast::{self,HasAttrsas _,HasGenericParams,IsStringas _},
40+
AstNode,AstToken,Direction,SyntaxKind,SyntaxNode,SyntaxNodePtr,SyntaxToken,TextRange,
41+
TextSize,
4242
};
4343

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

1224-
pubfnresolve_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+
pubfnresolve_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
pubfnresolve_type(&self,ty:&ast::Type) -> Option<Type>{

‎src/tools/rust-analyzer/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)fnlabel_ref_to_def(
347+
&mutself,
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+
returnNone;
356+
};
357+
Some((container, label?))
358+
}
359+
346360
pub(super)fnitem_to_macro_call(&mutself,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()

‎src/tools/rust-analyzer/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+
fnlabel_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
fngoto_def_on_return_in_try(){
26652683
check(

‎src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub fn token_tree_to_syntax_node<Ctx>(
148148
) -> (Parse<SyntaxNode>,SpanMap<Ctx>)
149149
where
150150
SpanData<Ctx>:Copy + fmt::Debug,
151+
Ctx:PartialEq,
151152
{
152153
let buffer = match tt {
153154
tt::Subtree{
@@ -892,6 +893,7 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> {
892893
impl<Ctx>TtTreeSink<'_,Ctx>
893894
where
894895
SpanData<Ctx>:Copy + fmt::Debug,
896+
Ctx:PartialEq,
895897
{
896898
/// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween.
897899
/// This occurs when a float literal is used as a field access.
@@ -949,6 +951,7 @@ where
949951
}
950952

951953
letmut last = self.cursor;
954+
letmut combined_span = None;
952955
'tokens:for _ in0..n_tokens {
953956
let tmp:u8;
954957
ifself.cursor.eof(){
@@ -982,7 +985,10 @@ where
982985
format_to!(self.buf,"{lit}");
983986
debug_assert_ne!(self.buf.len() - buf_l,0);
984987
self.text_pos += TextSize::new((self.buf.len() - buf_l)asu32);
985-
self.token_map.push(self.text_pos, lit.span);
988+
combined_span = match combined_span {
989+
None => Some(lit.span),
990+
Some(prev_span) => Some(Self::merge_spans(prev_span, lit.span)),
991+
};
986992
self.cursor = self.cursor.bump();
987993
continue'tokens;
988994
}
@@ -1006,9 +1012,13 @@ where
10061012
};
10071013
self.buf += text;
10081014
self.text_pos += TextSize::of(text);
1009-
self.token_map.push(self.text_pos, span);
1015+
combined_span = match combined_span {
1016+
None => Some(span),
1017+
Some(prev_span) => Some(Self::merge_spans(prev_span, span)),
1018+
}
10101019
}
10111020

1021+
self.token_map.push(self.text_pos, combined_span.expect("expected at least one token"));
10121022
self.inner.token(kind,self.buf.as_str());
10131023
self.buf.clear();
10141024
// FIXME: Emitting whitespace for this is really just a hack, we should get rid of it.
@@ -1043,4 +1053,22 @@ where
10431053
fnerror(&mutself,error:String){
10441054
self.inner.error(error,self.text_pos)
10451055
}
1056+
1057+
fnmerge_spans(a:SpanData<Ctx>,b:SpanData<Ctx>) -> SpanData<Ctx>{
1058+
// We don't do what rustc does exactly, rustc does something clever when the spans have different syntax contexts
1059+
// but this runs afoul of our separation between `span` and `hir-expand`.
1060+
SpanData{
1061+
range:if a.ctx == b.ctx{
1062+
TextRange::new(
1063+
std::cmp::min(a.range.start(), b.range.start()),
1064+
std::cmp::max(a.range.end(), b.range.end()),
1065+
)
1066+
}else{
1067+
// Combining ranges make no sense when they come from different syntax contexts.
1068+
a.range
1069+
},
1070+
anchor: a.anchor,
1071+
ctx: a.ctx,
1072+
}
1073+
}
10461074
}

0 commit comments

Comments
 (0)