Skip to content

Commit 6bfdd38

Browse files
committed
Render matched macro arm on hover of macro calls
1 parent 062e1b9 commit 6bfdd38

File tree

11 files changed

+133
-65
lines changed

11 files changed

+133
-65
lines changed

crates/hir-expand/src/db.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -313,16 +313,18 @@ fn parse_macro_expansion(
313313
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
314314
let edition = loc.def.edition;
315315
let expand_to = loc.expand_to();
316-
let mbe::ValueResult { value: tt, err } = macro_expand(db, macro_file.macro_call_id, loc);
316+
let mbe::ValueResult { value: (tt, matched_arm), err } =
317+
macro_expand(db, macro_file.macro_call_id, loc);
317318

318-
let (parse, rev_token_map) = token_tree_to_syntax_node(
319+
let (parse, mut rev_token_map) = token_tree_to_syntax_node(
319320
match &tt {
320321
CowArc::Arc(it) => it,
321322
CowArc::Owned(it) => it,
322323
},
323324
expand_to,
324325
edition,
325326
);
327+
rev_token_map.matched_arm = matched_arm;
326328

327329
ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
328330
}
@@ -544,11 +546,13 @@ fn macro_expand(
544546
db: &dyn ExpandDatabase,
545547
macro_call_id: MacroCallId,
546548
loc: MacroCallLoc,
547-
) -> ExpandResult<CowArc<tt::Subtree>> {
549+
) -> ExpandResult<(CowArc<tt::Subtree>, Option<u32>)> {
548550
let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered();
549551

550-
let (ExpandResult { value: tt, err }, span) = match loc.def.kind {
551-
MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
552+
let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
553+
MacroDefKind::ProcMacro(..) => {
554+
return db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None)
555+
}
552556
_ => {
553557
let (macro_arg, undo_info, span) =
554558
db.macro_arg_considering_derives(macro_call_id, &loc.kind);
@@ -560,10 +564,10 @@ fn macro_expand(
560564
.decl_macro_expander(loc.def.krate, id)
561565
.expand(db, arg.clone(), macro_call_id, span),
562566
MacroDefKind::BuiltIn(it, _) => {
563-
it.expand(db, macro_call_id, arg, span).map_err(Into::into)
567+
it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
564568
}
565569
MacroDefKind::BuiltInDerive(it, _) => {
566-
it.expand(db, macro_call_id, arg, span).map_err(Into::into)
570+
it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
567571
}
568572
MacroDefKind::BuiltInEager(it, _) => {
569573
// This might look a bit odd, but we do not expand the inputs to eager macros here.
@@ -574,7 +578,8 @@ fn macro_expand(
574578
// As such we just return the input subtree here.
575579
let eager = match &loc.kind {
576580
MacroCallKind::FnLike { eager: None, .. } => {
577-
return ExpandResult::ok(CowArc::Arc(macro_arg.clone()));
581+
return ExpandResult::ok(CowArc::Arc(macro_arg.clone()))
582+
.zip_val(None);
578583
}
579584
MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
580585
_ => None,
@@ -586,12 +591,12 @@ fn macro_expand(
586591
// FIXME: We should report both errors!
587592
res.err = error.clone().or(res.err);
588593
}
589-
res
594+
res.zip_val(None)
590595
}
591596
MacroDefKind::BuiltInAttr(it, _) => {
592597
let mut res = it.expand(db, macro_call_id, arg, span);
593598
fixup::reverse_fixups(&mut res.value, &undo_info);
594-
res
599+
res.zip_val(None)
595600
}
596601
_ => unreachable!(),
597602
};
@@ -603,16 +608,18 @@ fn macro_expand(
603608
if !loc.def.is_include() {
604609
// Set a hard limit for the expanded tt
605610
if let Err(value) = check_tt_count(&tt) {
606-
return value.map(|()| {
607-
CowArc::Owned(tt::Subtree {
608-
delimiter: tt::Delimiter::invisible_spanned(span),
609-
token_trees: Box::new([]),
611+
return value
612+
.map(|()| {
613+
CowArc::Owned(tt::Subtree {
614+
delimiter: tt::Delimiter::invisible_spanned(span),
615+
token_trees: Box::new([]),
616+
})
610617
})
611-
});
618+
.zip_val(matched_arm);
612619
}
613620
}
614621

615-
ExpandResult { value: CowArc::Owned(tt), err }
622+
ExpandResult { value: (CowArc::Owned(tt), matched_arm), err }
616623
}
617624

618625
fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {

crates/hir-expand/src/declarative.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::sync::OnceLock;
33

44
use base_db::{CrateId, VersionReq};
55
use span::{Edition, MacroCallId, Span, SyntaxContextId};
6+
use stdx::TupleExt;
67
use syntax::{ast, AstNode};
78
use triomphe::Arc;
89

@@ -30,7 +31,7 @@ impl DeclarativeMacroExpander {
3031
tt: tt::Subtree,
3132
call_id: MacroCallId,
3233
span: Span,
33-
) -> ExpandResult<tt::Subtree> {
34+
) -> ExpandResult<(tt::Subtree, Option<u32>)> {
3435
let loc = db.lookup_intern_macro_call(call_id);
3536
let toolchain = db.toolchain(loc.def.krate);
3637
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
@@ -46,7 +47,7 @@ impl DeclarativeMacroExpander {
4647
});
4748
match self.mac.err() {
4849
Some(_) => ExpandResult::new(
49-
tt::Subtree::empty(tt::DelimSpan { open: span, close: span }),
50+
(tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), None),
5051
ExpandError::MacroDefinition,
5152
),
5253
None => self
@@ -90,6 +91,7 @@ impl DeclarativeMacroExpander {
9091
None => self
9192
.mac
9293
.expand(&tt, |_| (), new_meta_vars, call_site, def_site_edition)
94+
.map(TupleExt::head)
9395
.map_err(Into::into),
9496
}
9597
}

crates/hir/src/semantics.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,17 @@ impl<'db> SemanticsImpl<'db> {
12461246
.map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..)))
12471247
}
12481248

1249+
pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option<u32> {
1250+
let sa = self.analyze(macro_call.syntax())?;
1251+
self.db
1252+
.parse_macro_expansion(
1253+
sa.expand(self.db, self.wrap_node_infile(macro_call.clone()).as_ref())?,
1254+
)
1255+
.value
1256+
.1
1257+
.matched_arm
1258+
}
1259+
12491260
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
12501261
let sa = match self.analyze(macro_call.syntax()) {
12511262
Some(it) => it,

crates/ide/src/hover.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use ide_db::{
1414
helpers::pick_best_token,
1515
FxIndexSet, RootDatabase,
1616
};
17-
use itertools::Itertools;
17+
use itertools::{multizip, Itertools};
1818
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, T};
1919

2020
use crate::{
@@ -149,7 +149,7 @@ fn hover_simple(
149149
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
150150
cov_mark::hit!(no_highlight_on_comment_hover);
151151
return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
152-
let res = hover_for_definition(sema, file_id, def, &node, config);
152+
let res = hover_for_definition(sema, file_id, def, &node, None, config);
153153
Some(RangeInfo::new(range, res))
154154
});
155155
}
@@ -162,6 +162,7 @@ fn hover_simple(
162162
file_id,
163163
Definition::from(resolution?),
164164
&original_token.parent()?,
165+
None,
165166
config,
166167
);
167168
return Some(RangeInfo::new(range, res));
@@ -196,6 +197,29 @@ fn hover_simple(
196197
descended()
197198
.filter_map(|token| {
198199
let node = token.parent()?;
200+
201+
// special case macro calls, we wanna render the invoked arm index
202+
if let Some(name) = ast::NameRef::cast(node.clone()) {
203+
if let Some(path_seg) =
204+
name.syntax().parent().and_then(ast::PathSegment::cast)
205+
{
206+
if let Some(macro_call) = path_seg
207+
.parent_path()
208+
.syntax()
209+
.parent()
210+
.and_then(ast::MacroCall::cast)
211+
{
212+
if let Some(macro_) = sema.resolve_macro_call(&macro_call) {
213+
return Some(vec![(
214+
Definition::Macro(macro_),
215+
sema.resolve_macro_call_arm(&macro_call),
216+
node,
217+
)]);
218+
}
219+
}
220+
}
221+
}
222+
199223
match IdentClass::classify_node(sema, &node)? {
200224
// It's better for us to fall back to the keyword hover here,
201225
// rendering poll is very confusing
@@ -204,20 +228,19 @@ fn hover_simple(
204228
IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand {
205229
decl,
206230
..
207-
}) => Some(vec![(Definition::ExternCrateDecl(decl), node)]),
231+
}) => Some(vec![(Definition::ExternCrateDecl(decl), None, node)]),
208232

209233
class => Some(
210-
class
211-
.definitions()
212-
.into_iter()
213-
.zip(iter::repeat(node))
234+
multizip((class.definitions(), iter::repeat(None), iter::repeat(node)))
214235
.collect::<Vec<_>>(),
215236
),
216237
}
217238
})
218239
.flatten()
219-
.unique_by(|&(def, _)| def)
220-
.map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
240+
.unique_by(|&(def, _, _)| def)
241+
.map(|(def, macro_arm, node)| {
242+
hover_for_definition(sema, file_id, def, &node, macro_arm, config)
243+
})
221244
.reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
222245
acc.actions.extend(actions);
223246
acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup));
@@ -374,6 +397,7 @@ pub(crate) fn hover_for_definition(
374397
file_id: FileId,
375398
def: Definition,
376399
scope_node: &SyntaxNode,
400+
macro_arm: Option<u32>,
377401
config: &HoverConfig,
378402
) -> HoverResult {
379403
let famous_defs = match &def {
@@ -398,7 +422,8 @@ pub(crate) fn hover_for_definition(
398422
};
399423
let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
400424

401-
let markup = render::definition(sema.db, def, famous_defs.as_ref(), &notable_traits, config);
425+
let markup =
426+
render::definition(sema.db, def, famous_defs.as_ref(), &notable_traits, macro_arm, config);
402427
HoverResult {
403428
markup: render::process_markup(sema.db, def, &markup, config),
404429
actions: [

crates/ide/src/hover/render.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ pub(super) fn definition(
403403
def: Definition,
404404
famous_defs: Option<&FamousDefs<'_, '_>>,
405405
notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
406+
macro_arm: Option<u32>,
406407
config: &HoverConfig,
407408
) -> Markup {
408409
let mod_path = definition_mod_path(db, &def);
@@ -413,6 +414,13 @@ pub(super) fn definition(
413414
Definition::Adt(Adt::Struct(struct_)) => {
414415
struct_.display_limited(db, config.max_struct_field_count).to_string()
415416
}
417+
Definition::Macro(it) => {
418+
let mut label = it.display(db).to_string();
419+
if let Some(macro_arm) = macro_arm {
420+
format_to!(label, " // matched arm #{}", macro_arm);
421+
}
422+
label
423+
}
416424
_ => def.label(db),
417425
};
418426
let docs = def.docs(db, famous_defs);

crates/ide/src/hover/tests.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,21 +1560,21 @@ fn y() {
15601560
fn test_hover_macro_invocation() {
15611561
check(
15621562
r#"
1563-
macro_rules! foo { () => {} }
1563+
macro_rules! foo { (a) => {}; () => {} }
15641564
15651565
fn f() { fo$0o!(); }
15661566
"#,
15671567
expect![[r#"
1568-
*foo*
1568+
*foo*
15691569
1570-
```rust
1571-
test
1572-
```
1570+
```rust
1571+
test
1572+
```
15731573
1574-
```rust
1575-
macro_rules! foo
1576-
```
1577-
"#]],
1574+
```rust
1575+
macro_rules! foo // matched arm #1
1576+
```
1577+
"#]],
15781578
)
15791579
}
15801580

@@ -1590,22 +1590,22 @@ macro foo() {}
15901590
fn f() { fo$0o!(); }
15911591
"#,
15921592
expect![[r#"
1593-
*foo*
1593+
*foo*
15941594
1595-
```rust
1596-
test
1597-
```
1595+
```rust
1596+
test
1597+
```
15981598
1599-
```rust
1600-
macro foo
1601-
```
1599+
```rust
1600+
macro foo // matched arm #0
1601+
```
16021602
1603-
---
1603+
---
16041604
1605-
foo bar
1605+
foo bar
16061606
1607-
foo bar baz
1608-
"#]],
1607+
foo bar baz
1608+
"#]],
16091609
)
16101610
}
16111611

crates/ide/src/static_index.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,14 @@ impl StaticIndex<'_> {
188188
} else {
189189
let it = self.tokens.insert(TokenStaticData {
190190
documentation: documentation_for_definition(&sema, def, &node),
191-
hover: Some(hover_for_definition(&sema, file_id, def, &node, &hover_config)),
191+
hover: Some(hover_for_definition(
192+
&sema,
193+
file_id,
194+
def,
195+
&node,
196+
None,
197+
&hover_config,
198+
)),
192199
definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| {
193200
FileRange { file_id: it.file_id, range: it.focus_or_full_range() }
194201
}),

crates/mbe/src/benchmark.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fn benchmark_expand_macro_rules() {
4848
.map(|(id, tt)| {
4949
let res = rules[&id].expand(&tt, |_| (), true, DUMMY, Edition::CURRENT);
5050
assert!(res.err.is_none());
51-
res.value.token_trees.len()
51+
res.value.0.token_trees.len()
5252
})
5353
.sum()
5454
};

0 commit comments

Comments
 (0)