Skip to content

Commit 5b8e386

Browse files
committed
Improve macro descension API
1 parent 986577f commit 5b8e386

21 files changed

+177
-137
lines changed

crates/hir-expand/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,8 +629,6 @@ impl ExpansionInfo {
629629
pub fn map_range_down<'a>(
630630
&'a self,
631631
span: SpanData,
632-
// FIXME: use this for range mapping, so that we can resolve inline format args
633-
_relative_token_offset: Option<TextSize>,
634632
) -> Option<impl Iterator<Item = InMacroFile<SyntaxToken>> + 'a> {
635633
let tokens = self
636634
.exp_map

crates/hir/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ pub use crate::{
9292
attrs::{resolve_doc_path_on, HasAttrs},
9393
diagnostics::*,
9494
has_source::HasSource,
95-
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
95+
semantics::{
96+
DescendPreference, PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits,
97+
},
9698
};
9799

98100
// Be careful with these re-exports.

crates/hir/src/semantics.rs

Lines changed: 97 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
33
mod source_to_def;
44

5-
use std::{cell::RefCell, fmt, iter, mem, ops};
5+
use std::{
6+
cell::RefCell,
7+
fmt, iter, mem,
8+
ops::{self, ControlFlow},
9+
};
610

711
use base_db::{FileId, FileRange};
812
use either::Either;
@@ -39,6 +43,12 @@ use crate::{
3943
TypeAlias, TypeParam, VariantDef,
4044
};
4145

46+
pub enum DescendPreference {
47+
SameText,
48+
SameKind,
49+
None,
50+
}
51+
4252
#[derive(Debug, Clone, PartialEq, Eq)]
4353
pub enum PathResolution {
4454
/// An item
@@ -397,6 +407,7 @@ impl<'db> SemanticsImpl<'db> {
397407
// This might not be the correct way to do this, but it works for now
398408
let mut res = smallvec![];
399409
let tokens = (|| {
410+
// FIXME: the trivia skipping should not be necessary
400411
let first = skip_trivia_token(node.syntax().first_token()?, Direction::Next)?;
401412
let last = skip_trivia_token(node.syntax().last_token()?, Direction::Prev)?;
402413
Some((first, last))
@@ -407,18 +418,19 @@ impl<'db> SemanticsImpl<'db> {
407418
};
408419

409420
if first == last {
421+
// node is just the token, so descend the token
410422
self.descend_into_macros_impl(first, 0.into(), &mut |InFile { value, .. }| {
411423
if let Some(node) = value.parent_ancestors().find_map(N::cast) {
412424
res.push(node)
413425
}
414-
false
426+
ControlFlow::Continue(())
415427
});
416428
} else {
417429
// Descend first and last token, then zip them to look for the node they belong to
418430
let mut scratch: SmallVec<[_; 1]> = smallvec![];
419431
self.descend_into_macros_impl(first, 0.into(), &mut |token| {
420432
scratch.push(token);
421-
false
433+
ControlFlow::Continue(())
422434
});
423435

424436
let mut scratch = scratch.into_iter();
@@ -441,7 +453,7 @@ impl<'db> SemanticsImpl<'db> {
441453
}
442454
}
443455
}
444-
false
456+
ControlFlow::Continue(())
445457
},
446458
);
447459
}
@@ -453,78 +465,92 @@ impl<'db> SemanticsImpl<'db> {
453465
/// be considered for the mapping in case of inline format args.
454466
pub fn descend_into_macros(
455467
&self,
468+
mode: DescendPreference,
456469
token: SyntaxToken,
457470
offset: TextSize,
458471
) -> SmallVec<[SyntaxToken; 1]> {
459-
let mut res = smallvec![];
460-
self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
461-
res.push(value);
462-
false
463-
});
464-
res
465-
}
466-
467-
/// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token.
468-
///
469-
/// Returns the original non descended token if none of the mapped counterparts have the same text.
470-
pub fn descend_into_macros_with_same_text(
471-
&self,
472-
token: SyntaxToken,
473-
offset: TextSize,
474-
) -> SmallVec<[SyntaxToken; 1]> {
475-
let text = token.text();
472+
enum Dp<'t> {
473+
SameText(&'t str),
474+
SameKind(SyntaxKind),
475+
None,
476+
}
477+
let fetch_kind = |token: &SyntaxToken| match token.parent() {
478+
Some(node) => match node.kind() {
479+
kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => kind,
480+
_ => token.kind(),
481+
},
482+
None => token.kind(),
483+
};
484+
let mode = match mode {
485+
DescendPreference::SameText => Dp::SameText(token.text()),
486+
DescendPreference::SameKind => Dp::SameKind(fetch_kind(&token)),
487+
DescendPreference::None => Dp::None,
488+
};
476489
let mut res = smallvec![];
477490
self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| {
478-
if value.text() == text {
491+
let is_a_match = match mode {
492+
Dp::SameText(text) => value.text() == text,
493+
Dp::SameKind(preferred_kind) => {
494+
let kind = fetch_kind(&value);
495+
kind == preferred_kind
496+
// special case for derive macros
497+
|| (preferred_kind == SyntaxKind::IDENT && kind == SyntaxKind::NAME_REF)
498+
}
499+
Dp::None => true,
500+
};
501+
if is_a_match {
479502
res.push(value);
480503
}
481-
false
504+
ControlFlow::Continue(())
482505
});
483506
if res.is_empty() {
484507
res.push(token);
485508
}
486509
res
487510
}
488511

489-
pub fn descend_into_macros_with_kind_preference(
512+
pub fn descend_into_macros_single(
490513
&self,
514+
mode: DescendPreference,
491515
token: SyntaxToken,
492516
offset: TextSize,
493517
) -> SyntaxToken {
518+
enum Dp<'t> {
519+
SameText(&'t str),
520+
SameKind(SyntaxKind),
521+
None,
522+
}
494523
let fetch_kind = |token: &SyntaxToken| match token.parent() {
495524
Some(node) => match node.kind() {
496-
kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => {
497-
node.parent().map_or(kind, |it| it.kind())
498-
}
525+
kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => kind,
499526
_ => token.kind(),
500527
},
501528
None => token.kind(),
502529
};
503-
let preferred_kind = fetch_kind(&token);
504-
let mut res = None;
530+
let mode = match mode {
531+
DescendPreference::SameText => Dp::SameText(token.text()),
532+
DescendPreference::SameKind => Dp::SameKind(fetch_kind(&token)),
533+
DescendPreference::None => Dp::None,
534+
};
535+
let mut res = token.clone();
505536
self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| {
506-
if fetch_kind(&value) == preferred_kind {
507-
res = Some(value);
508-
true
509-
} else {
510-
if let None = res {
511-
res = Some(value)
537+
let is_a_match = match mode {
538+
Dp::SameText(text) => value.text() == text,
539+
Dp::SameKind(preferred_kind) => {
540+
let kind = fetch_kind(&value);
541+
kind == preferred_kind
542+
// special case for derive macros
543+
|| (preferred_kind == SyntaxKind::IDENT && kind == SyntaxKind::NAME_REF)
512544
}
513-
false
545+
Dp::None => true,
546+
};
547+
if is_a_match {
548+
res = value;
549+
ControlFlow::Break(())
550+
} else {
551+
ControlFlow::Continue(())
514552
}
515553
});
516-
res.unwrap_or(token)
517-
}
518-
519-
/// Descend the token into its macro call if it is part of one, returning the token in the
520-
/// expansion that it is associated with. If `offset` points into the token's range, it will
521-
/// be considered for the mapping in case of inline format args.
522-
pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
523-
let mut res = token.clone();
524-
self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
525-
res = value;
526-
true
527-
});
528554
res
529555
}
530556

@@ -535,7 +561,7 @@ impl<'db> SemanticsImpl<'db> {
535561
// FIXME: We might want this to be Option<TextSize> to be able to opt out of subrange
536562
// mapping, specifically for node downmapping
537563
_offset: TextSize,
538-
f: &mut dyn FnMut(InFile<SyntaxToken>) -> bool,
564+
f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>,
539565
) {
540566
// FIXME: Clean this up
541567
let _p = profile::span("descend_into_macros");
@@ -560,25 +586,24 @@ impl<'db> SemanticsImpl<'db> {
560586
let def_map = sa.resolver.def_map();
561587
let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)];
562588

563-
let mut process_expansion_for_token =
564-
|stack: &mut SmallVec<_>, macro_file, _token: InFile<&_>| {
565-
let expansion_info = cache
566-
.entry(macro_file)
567-
.or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
589+
let mut process_expansion_for_token = |stack: &mut SmallVec<_>, macro_file| {
590+
let expansion_info = cache
591+
.entry(macro_file)
592+
.or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
568593

569-
{
570-
let InFile { file_id, value } = expansion_info.expanded();
571-
self.cache(value, file_id);
572-
}
594+
{
595+
let InFile { file_id, value } = expansion_info.expanded();
596+
self.cache(value, file_id);
597+
}
573598

574-
let mapped_tokens = expansion_info.map_range_down(span, None)?;
575-
let len = stack.len();
599+
let mapped_tokens = expansion_info.map_range_down(span)?;
600+
let len = stack.len();
576601

577-
// requeue the tokens we got from mapping our current token down
578-
stack.extend(mapped_tokens.map(Into::into));
579-
// if the length changed we have found a mapping for the token
580-
(stack.len() != len).then_some(())
581-
};
602+
// requeue the tokens we got from mapping our current token down
603+
stack.extend(mapped_tokens.map(Into::into));
604+
// if the length changed we have found a mapping for the token
605+
(stack.len() != len).then_some(())
606+
};
582607

583608
// Remap the next token in the queue into a macro call its in, if it is not being remapped
584609
// either due to not being in a macro-call or because its unused push it into the result vec,
@@ -598,7 +623,7 @@ impl<'db> SemanticsImpl<'db> {
598623
});
599624
if let Some(call_id) = containing_attribute_macro_call {
600625
let file_id = call_id.as_macro_file();
601-
return process_expansion_for_token(&mut stack, file_id, token.as_ref());
626+
return process_expansion_for_token(&mut stack, file_id);
602627
}
603628

604629
// Then check for token trees, that means we are either in a function-like macro or
@@ -624,7 +649,7 @@ impl<'db> SemanticsImpl<'db> {
624649
it
625650
}
626651
};
627-
process_expansion_for_token(&mut stack, file_id, token.as_ref())
652+
process_expansion_for_token(&mut stack, file_id)
628653
} else if let Some(meta) = ast::Meta::cast(parent) {
629654
// attribute we failed expansion for earlier, this might be a derive invocation
630655
// or derive helper attribute
@@ -646,11 +671,7 @@ impl<'db> SemanticsImpl<'db> {
646671
Some(call_id) => {
647672
// resolved to a derive
648673
let file_id = call_id.as_macro_file();
649-
return process_expansion_for_token(
650-
&mut stack,
651-
file_id,
652-
token.as_ref(),
653-
);
674+
return process_expansion_for_token(&mut stack, file_id);
654675
}
655676
None => Some(adt),
656677
}
@@ -682,11 +703,8 @@ impl<'db> SemanticsImpl<'db> {
682703
def_map.derive_helpers_in_scope(InFile::new(token.file_id, id))?;
683704
let mut res = None;
684705
for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) {
685-
res = res.or(process_expansion_for_token(
686-
&mut stack,
687-
derive.as_macro_file(),
688-
token.as_ref(),
689-
));
706+
res =
707+
res.or(process_expansion_for_token(&mut stack, derive.as_macro_file()));
690708
}
691709
res
692710
} else {
@@ -695,7 +713,7 @@ impl<'db> SemanticsImpl<'db> {
695713
})()
696714
.is_none();
697715

698-
if was_not_remapped && f(token) {
716+
if was_not_remapped && f(token).is_break() {
699717
break;
700718
}
701719
}
@@ -711,7 +729,7 @@ impl<'db> SemanticsImpl<'db> {
711729
offset: TextSize,
712730
) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ {
713731
node.token_at_offset(offset)
714-
.map(move |token| self.descend_into_macros(token, offset))
732+
.map(move |token| self.descend_into_macros(DescendPreference::None, token, offset))
715733
.map(|descendants| {
716734
descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it))
717735
})

crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{AssistContext, Assists};
2+
use hir::DescendPreference;
23
use ide_db::{
34
assists::{AssistId, AssistKind},
45
syntax_helpers::{
@@ -34,9 +35,11 @@ pub(crate) fn extract_expressions_from_format_string(
3435
let fmt_string = ctx.find_token_at_offset::<ast::String>()?;
3536
let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
3637

37-
let expanded_t = ast::String::cast(
38-
ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone(), 0.into()),
39-
)?;
38+
let expanded_t = ast::String::cast(ctx.sema.descend_into_macros_single(
39+
DescendPreference::SameKind,
40+
fmt_string.syntax().clone(),
41+
0.into(),
42+
))?;
4043
if !is_format_string(&expanded_t) {
4144
return None;
4245
}

crates/ide-assists/src/handlers/extract_function.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::iter;
33
use ast::make;
44
use either::Either;
55
use hir::{
6-
HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics,
7-
TypeInfo, TypeParam,
6+
DescendPreference, HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef,
7+
PathResolution, Semantics, TypeInfo, TypeParam,
88
};
99
use ide_db::{
1010
defs::{Definition, NameRefClass},
@@ -751,7 +751,9 @@ impl FunctionBody {
751751
.descendants_with_tokens()
752752
.filter_map(SyntaxElement::into_token)
753753
.filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self]))
754-
.flat_map(|t| sema.descend_into_macros(t, 0.into()))
754+
.flat_map(|t| {
755+
sema.descend_into_macros(DescendPreference::None, t, 0.into())
756+
})
755757
.for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast)));
756758
}
757759
}

crates/ide-db/src/helpers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::collections::VecDeque;
44

55
use base_db::{FileId, SourceDatabaseExt};
6-
use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
6+
use hir::{Crate, DescendPreference, ItemInNs, ModuleDef, Name, Semantics};
77
use syntax::{
88
ast::{self, make},
99
AstToken, SyntaxKind, SyntaxToken, TokenAtOffset,
@@ -117,7 +117,7 @@ pub fn get_definition(
117117
sema: &Semantics<'_, RootDatabase>,
118118
token: SyntaxToken,
119119
) -> Option<Definition> {
120-
for token in sema.descend_into_macros(token, 0.into()) {
120+
for token in sema.descend_into_macros(DescendPreference::None, token, 0.into()) {
121121
let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
122122
if let Some(&[x]) = def.as_deref() {
123123
return Some(x);

0 commit comments

Comments
 (0)