Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit d2f8eae

Browse files
committed
feat: Support macro calls in eager macros for IDE features
1 parent 9767156 commit d2f8eae

File tree

22 files changed

+420
-226
lines changed

22 files changed

+420
-226
lines changed

crates/hir-def/src/db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
298298
}
299299
};
300300

301+
// FIXME: The def site spans here are wrong, those should point to the name, not the whole ast node
301302
match id {
302303
MacroId::Macro2Id(it) => {
303304
let loc: Macro2Loc = it.lookup(db);
@@ -315,7 +316,6 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
315316
edition: loc.edition,
316317
}
317318
}
318-
319319
MacroId::MacroRulesId(it) => {
320320
let loc: MacroRulesLoc = it.lookup(db);
321321

crates/hir-def/src/item_tree/lower.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -560,17 +560,14 @@ impl<'a> Ctx<'a> {
560560

561561
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
562562
let span_map = self.span_map();
563-
let path = Interned::new(ModPath::from_src(self.db.upcast(), m.path()?, &mut |range| {
563+
let path = m.path()?;
564+
let range = path.syntax().text_range();
565+
let path = Interned::new(ModPath::from_src(self.db.upcast(), path, &mut |range| {
564566
span_map.span_for_range(range).ctx
565567
})?);
566568
let ast_id = self.source_ast_id_map.ast_id(m);
567569
let expand_to = hir_expand::ExpandTo::from_call_site(m);
568-
let res = MacroCall {
569-
path,
570-
ast_id,
571-
expand_to,
572-
call_site: span_map.span_for_range(m.syntax().text_range()),
573-
};
570+
let res = MacroCall { path, ast_id, expand_to, call_site: span_map.span_for_range(range) };
574571
Some(id(self.data().macro_calls.alloc(res)))
575572
}
576573

crates/hir-def/src/item_tree/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ m!();
278278
// AstId: 2
279279
pub macro m2 { ... }
280280
281-
// AstId: 3, Span: 0:3@0..5#0, ExpandTo: Items
281+
// AstId: 3, Span: 0:3@0..1#0, ExpandTo: Items
282282
m!(...);
283283
"#]],
284284
);

crates/hir-def/src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,17 +1342,18 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
13421342
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
13431343
let span_map = db.span_map(self.file_id);
13441344
let path = self.value.path().and_then(|path| {
1345-
path::ModPath::from_src(db, path, &mut |range| {
1345+
let range = path.syntax().text_range();
1346+
let mod_path = path::ModPath::from_src(db, path, &mut |range| {
13461347
span_map.as_ref().span_for_range(range).ctx
1347-
})
1348+
})?;
1349+
let call_site = span_map.span_for_range(range);
1350+
Some((call_site, mod_path))
13481351
});
13491352

1350-
let Some(path) = path else {
1353+
let Some((call_site, path)) = path else {
13511354
return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
13521355
};
13531356

1354-
let call_site = span_map.span_for_range(self.value.syntax().text_range());
1355-
13561357
macro_call_as_call_id_with_eager(
13571358
db,
13581359
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),

crates/hir-def/src/macro_expansion_tests/mbe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ fn main(foo: ()) {
171171
}
172172
173173
fn main(foo: ()) {
174-
/* error: unresolved macro unresolved */"helloworld!"#0:3@207..323#2#;
174+
/* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#0#;
175175
}
176176
}
177177

crates/hir-expand/src/attrs.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,12 @@ impl Attr {
201201
span_map: SpanMapRef<'_>,
202202
id: AttrId,
203203
) -> Option<Attr> {
204-
let path = Interned::new(ModPath::from_src(db, ast.path()?, &mut |range| {
204+
let path = ast.path()?;
205+
let range = path.syntax().text_range();
206+
let path = Interned::new(ModPath::from_src(db, path, &mut |range| {
205207
span_map.span_for_range(range).ctx
206208
})?);
207-
let span = span_map.span_for_range(ast.syntax().text_range());
209+
let span = span_map.span_for_range(range);
208210
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
209211
let value = match lit.kind() {
210212
ast::LiteralKind::String(string) => string.value()?.into(),

crates/hir-expand/src/builtin_fn_macro.rs

Lines changed: 80 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ use crate::{
1919
};
2020

2121
macro_rules! register_builtin {
22-
( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
22+
( $LAZY:ident: $(($name:ident, $kind: ident) => $expand:ident),* , $EAGER:ident: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
2323
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24-
pub enum BuiltinFnLikeExpander {
24+
pub enum $LAZY {
2525
$($kind),*
2626
}
2727

2828
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29-
pub enum EagerExpander {
29+
pub enum $EAGER {
3030
$($e_kind),*
3131
}
3232

@@ -84,6 +84,17 @@ impl EagerExpander {
8484
pub fn is_include(&self) -> bool {
8585
matches!(self, EagerExpander::Include)
8686
}
87+
88+
pub fn is_include_like(&self) -> bool {
89+
matches!(
90+
self,
91+
EagerExpander::Include | EagerExpander::IncludeStr | EagerExpander::IncludeBytes
92+
)
93+
}
94+
95+
pub fn is_env_or_option_env(&self) -> bool {
96+
matches!(self, EagerExpander::Env | EagerExpander::OptionEnv)
97+
}
8798
}
8899

89100
pub fn find_builtin_macro(
@@ -93,7 +104,7 @@ pub fn find_builtin_macro(
93104
}
94105

95106
register_builtin! {
96-
LAZY:
107+
BuiltinFnLikeExpander:
97108
(column, Column) => line_expand,
98109
(file, File) => file_expand,
99110
(line, Line) => line_expand,
@@ -114,7 +125,7 @@ register_builtin! {
114125
(format_args_nl, FormatArgsNl) => format_args_nl_expand,
115126
(quote, Quote) => quote_expand,
116127

117-
EAGER:
128+
EagerExpander:
118129
(compile_error, CompileError) => compile_error_expand,
119130
(concat, Concat) => concat_expand,
120131
(concat_idents, ConcatIdents) => concat_idents_expand,
@@ -426,22 +437,25 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool {
426437
}
427438
}
428439

429-
fn unquote_str(lit: &tt::Literal) -> Option<String> {
440+
fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> {
441+
let span = lit.span;
430442
let lit = ast::make::tokens::literal(&lit.to_string());
431443
let token = ast::String::cast(lit)?;
432-
token.value().map(|it| it.into_owned())
444+
token.value().map(|it| (it.into_owned(), span))
433445
}
434446

435-
fn unquote_char(lit: &tt::Literal) -> Option<char> {
447+
fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> {
448+
let span = lit.span;
436449
let lit = ast::make::tokens::literal(&lit.to_string());
437450
let token = ast::Char::cast(lit)?;
438-
token.value()
451+
token.value().zip(Some(span))
439452
}
440453

441-
fn unquote_byte_string(lit: &tt::Literal) -> Option<Vec<u8>> {
454+
fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec<u8>, Span)> {
455+
let span = lit.span;
442456
let lit = ast::make::tokens::literal(&lit.to_string());
443457
let token = ast::ByteString::cast(lit)?;
444-
token.value().map(|it| it.into_owned())
458+
token.value().map(|it| (it.into_owned(), span))
445459
}
446460

447461
fn compile_error_expand(
@@ -452,7 +466,7 @@ fn compile_error_expand(
452466
) -> ExpandResult<tt::Subtree> {
453467
let err = match &*tt.token_trees {
454468
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
455-
Some(unquoted) => ExpandError::other(unquoted.into_boxed_str()),
469+
Some((unquoted, _)) => ExpandError::other(unquoted.into_boxed_str()),
456470
None => ExpandError::other("`compile_error!` argument must be a string"),
457471
},
458472
_ => ExpandError::other("`compile_error!` argument must be a string"),
@@ -465,10 +479,16 @@ fn concat_expand(
465479
_db: &dyn ExpandDatabase,
466480
_arg_id: MacroCallId,
467481
tt: &tt::Subtree,
468-
span: Span,
482+
_: Span,
469483
) -> ExpandResult<tt::Subtree> {
470484
let mut err = None;
471485
let mut text = String::new();
486+
let mut span: Option<Span> = None;
487+
let mut record_span = |s: Span| match &mut span {
488+
Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range),
489+
Some(_) => (),
490+
None => span = Some(s),
491+
};
472492
for (i, mut t) in tt.token_trees.iter().enumerate() {
473493
// FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses
474494
// to ensure the right parsing order, so skip the parentheses here. Ideally we'd
@@ -486,44 +506,56 @@ fn concat_expand(
486506
// concat works with string and char literals, so remove any quotes.
487507
// It also works with integer, float and boolean literals, so just use the rest
488508
// as-is.
489-
if let Some(c) = unquote_char(it) {
509+
if let Some((c, span)) = unquote_char(it) {
490510
text.push(c);
511+
record_span(span);
491512
} else {
492-
let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
513+
let (component, span) =
514+
unquote_str(it).unwrap_or_else(|| (it.text.to_string(), it.span));
493515
text.push_str(&component);
516+
record_span(span);
494517
}
495518
}
496519
// handle boolean literals
497520
tt::TokenTree::Leaf(tt::Leaf::Ident(id))
498521
if i % 2 == 0 && (id.text == "true" || id.text == "false") =>
499522
{
500523
text.push_str(id.text.as_str());
524+
record_span(id.span);
501525
}
502526
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
503527
_ => {
504528
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
505529
}
506530
}
507531
}
532+
let span = span.unwrap_or(tt.delimiter.open);
508533
ExpandResult { value: quote!(span =>#text), err }
509534
}
510535

511536
fn concat_bytes_expand(
512537
_db: &dyn ExpandDatabase,
513538
_arg_id: MacroCallId,
514539
tt: &tt::Subtree,
515-
span: Span,
540+
call_site: Span,
516541
) -> ExpandResult<tt::Subtree> {
517542
let mut bytes = Vec::new();
518543
let mut err = None;
544+
let mut span: Option<Span> = None;
545+
let mut record_span = |s: Span| match &mut span {
546+
Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range),
547+
Some(_) => (),
548+
None => span = Some(s),
549+
};
519550
for (i, t) in tt.token_trees.iter().enumerate() {
520551
match t {
521552
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
522553
let token = ast::make::tokens::literal(&lit.to_string());
554+
record_span(lit.span);
523555
match token.kind() {
524556
syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()),
525557
syntax::SyntaxKind::BYTE_STRING => {
526-
let components = unquote_byte_string(lit).unwrap_or_default();
558+
let components = unquote_byte_string(lit).map_or(vec![], |(it, _)| it);
527559
components.into_iter().for_each(|it| bytes.push(it.to_string()));
528560
}
529561
_ => {
@@ -534,7 +566,7 @@ fn concat_bytes_expand(
534566
}
535567
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
536568
tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => {
537-
if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes) {
569+
if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span) {
538570
err.get_or_insert(e);
539571
break;
540572
}
@@ -546,17 +578,24 @@ fn concat_bytes_expand(
546578
}
547579
}
548580
let value = tt::Subtree {
549-
delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket },
581+
delimiter: tt::Delimiter {
582+
open: call_site,
583+
close: call_site,
584+
kind: tt::DelimiterKind::Bracket,
585+
},
550586
token_trees: {
551587
Itertools::intersperse_with(
552588
bytes.into_iter().map(|it| {
553-
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: it.into(), span }))
589+
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
590+
text: it.into(),
591+
span: span.unwrap_or(call_site),
592+
}))
554593
}),
555594
|| {
556595
tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
557596
char: ',',
558597
spacing: tt::Spacing::Alone,
559-
span,
598+
span: call_site,
560599
}))
561600
},
562601
)
@@ -569,13 +608,15 @@ fn concat_bytes_expand(
569608
fn concat_bytes_expand_subtree(
570609
tree: &tt::Subtree,
571610
bytes: &mut Vec<String>,
611+
mut record_span: impl FnMut(Span),
572612
) -> Result<(), ExpandError> {
573613
for (ti, tt) in tree.token_trees.iter().enumerate() {
574614
match tt {
575-
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
576-
let lit = ast::make::tokens::literal(&lit.to_string());
615+
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => {
616+
let lit = ast::make::tokens::literal(&it.to_string());
577617
match lit.kind() {
578618
syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => {
619+
record_span(it.span);
579620
bytes.push(lit.text().to_owned())
580621
}
581622
_ => {
@@ -635,7 +676,7 @@ fn relative_file(
635676
}
636677
}
637678

638-
fn parse_string(tt: &tt::Subtree) -> Result<String, ExpandError> {
679+
fn parse_string(tt: &tt::Subtree) -> Result<(String, Span), ExpandError> {
639680
tt.token_trees
640681
.first()
641682
.and_then(|tt| match tt {
@@ -675,7 +716,7 @@ pub fn include_input_to_file_id(
675716
arg_id: MacroCallId,
676717
arg: &tt::Subtree,
677718
) -> Result<FileId, ExpandError> {
678-
relative_file(db, arg_id, &parse_string(arg)?, false)
719+
relative_file(db, arg_id, &parse_string(arg)?.0, false)
679720
}
680721

681722
fn include_bytes_expand(
@@ -701,7 +742,7 @@ fn include_str_expand(
701742
tt: &tt::Subtree,
702743
span: Span,
703744
) -> ExpandResult<tt::Subtree> {
704-
let path = match parse_string(tt) {
745+
let (path, span) = match parse_string(tt) {
705746
Ok(it) => it,
706747
Err(e) => {
707748
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
@@ -736,7 +777,7 @@ fn env_expand(
736777
tt: &tt::Subtree,
737778
span: Span,
738779
) -> ExpandResult<tt::Subtree> {
739-
let key = match parse_string(tt) {
780+
let (key, span) = match parse_string(tt) {
740781
Ok(it) => it,
741782
Err(e) => {
742783
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
@@ -766,18 +807,24 @@ fn option_env_expand(
766807
db: &dyn ExpandDatabase,
767808
arg_id: MacroCallId,
768809
tt: &tt::Subtree,
769-
span: Span,
810+
call_site: Span,
770811
) -> ExpandResult<tt::Subtree> {
771-
let key = match parse_string(tt) {
812+
let (key, span) = match parse_string(tt) {
772813
Ok(it) => it,
773814
Err(e) => {
774-
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
815+
return ExpandResult::new(
816+
tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }),
817+
e,
818+
)
775819
}
776820
};
777-
let dollar_crate = dollar_crate(span);
821+
let dollar_crate = dollar_crate(call_site);
778822
let expanded = match get_env_inner(db, arg_id, &key) {
779-
None => quote! {span => #dollar_crate::option::Option::None::<&str> },
780-
Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) },
823+
None => quote! {call_site => #dollar_crate::option::Option::None::<&str> },
824+
Some(s) => {
825+
let s = quote! (span => #s);
826+
quote! {call_site => #dollar_crate::option::Option::Some(#s) }
827+
}
781828
};
782829

783830
ExpandResult::ok(expanded)

0 commit comments

Comments
 (0)