Unnamed repository; edit this file 'description' to name the repository.
Remove DescendPreference::SameText
Lukas Wirth 2024-08-22
parent 011e3bb · commit f979667
-rw-r--r--Cargo.toml2
-rw-r--r--crates/hir-expand/src/lib.rs7
-rw-r--r--crates/hir/src/semantics.rs353
-rw-r--r--crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs14
-rw-r--r--crates/ide-db/src/syntax_helpers/format_string_exprs.rs1
-rw-r--r--crates/ide/src/doc_links.rs2
-rw-r--r--crates/ide/src/extend_selection.rs10
-rw-r--r--crates/ide/src/goto_implementation.rs93
-rw-r--r--crates/ide/src/hover.rs33
-rw-r--r--crates/ide/src/references.rs4
-rw-r--r--crates/ide/src/signature_help.rs7
-rw-r--r--crates/ide/src/syntax_highlighting.rs3
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_macros.html4
13 files changed, 304 insertions, 229 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 56e80e11e7..d8f9b2b7f6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -184,6 +184,8 @@ style = { level = "warn", priority = -1 }
suspicious = { level = "warn", priority = -1 }
## allow following lints
+# subjective
+single_match = "allow"
# () makes a fine error in most cases
result_unit_err = "allow"
# We don't expose public APIs that matter like this
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 2bea902626..741c2b2ebf 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -279,6 +279,7 @@ pub enum MacroCallKind {
}
pub trait HirFileIdExt {
+ fn edition(self, db: &dyn ExpandDatabase) -> Edition;
/// Returns the original file of this macro call hierarchy.
fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId;
@@ -293,6 +294,12 @@ pub trait HirFileIdExt {
}
impl HirFileIdExt for HirFileId {
+ fn edition(self, db: &dyn ExpandDatabase) -> Edition {
+ match self.repr() {
+ HirFileIdRepr::FileId(file_id) => file_id.edition(),
+ HirFileIdRepr::MacroFile(m) => m.macro_call_id.lookup(db).def.edition,
+ }
+ }
fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId {
let mut file_id = self;
loop {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index d086aee428..a8151e70a1 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -4,6 +4,7 @@ mod source_to_def;
use std::{
cell::RefCell,
+ convert::Infallible,
fmt, iter, mem,
ops::{self, ControlFlow, Not},
};
@@ -48,12 +49,46 @@ use crate::{
Variant, VariantDef,
};
+const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(());
+
pub enum DescendPreference {
- SameText,
SameKind,
None,
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum MacroInputKind {
+ Root,
+ Bang,
+ AttrInput,
+ AttrTarget,
+ Derive,
+ DeriveHelper,
+ // Include,
+}
+impl MacroInputKind {
+ pub fn is_tt(self) -> bool {
+ matches!(
+ self,
+ MacroInputKind::AttrInput
+ | MacroInputKind::Bang
+ | MacroInputKind::DeriveHelper
+ | MacroInputKind::Derive
+ )
+ }
+}
+
+impl ops::BitOr for MacroInputKind {
+ type Output = Self;
+
+ fn bitor(self, rhs: Self) -> Self::Output {
+ match self {
+ Self::Root => rhs,
+ _ => self,
+ }
+ }
+}
+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PathResolution {
/// An item
@@ -545,51 +580,50 @@ impl<'db> SemanticsImpl<'db> {
)
}
+ /// Retrieves all the formatting parts of the format_args! template string.
pub fn as_format_args_parts(
&self,
string: &ast::String,
) -> Option<Vec<(TextRange, Option<PathResolution>)>> {
- if let Some(quote) = string.open_quote_text_range() {
- return self
- .descend_into_macros(DescendPreference::SameText, string.syntax().clone())
- .into_iter()
- .find_map(|token| {
- let string = ast::String::cast(token)?;
- let literal =
- string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
- let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?;
- let source_analyzer = self.analyze_no_infer(format_args.syntax())?;
- let format_args = self.wrap_node_infile(format_args);
- let res = source_analyzer
- .as_format_args_parts(self.db, format_args.as_ref())?
- .map(|(range, res)| (range + quote.end(), res))
- .collect();
- Some(res)
- });
- }
- None
+ let quote = string.open_quote_text_range()?;
+ self.descend_into_macros_ng_b(string.syntax().clone(), |kind, token| {
+ (|| {
+ let token = token.value;
+ let string = ast::String::cast(token)?;
+ let literal =
+ string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
+ let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?;
+ let source_analyzer = self.analyze_no_infer(format_args.syntax())?;
+ let format_args = self.wrap_node_infile(format_args);
+ let res = source_analyzer
+ .as_format_args_parts(self.db, format_args.as_ref())?
+ .map(|(range, res)| (range + quote.end(), res))
+ .collect();
+ Some(res)
+ })()
+ .map_or(ControlFlow::Continue(()), ControlFlow::Break)
+ })
}
+ /// Retrieves the formatting part of the format_args! template string at the given offset.
pub fn check_for_format_args_template(
&self,
original_token: SyntaxToken,
offset: TextSize,
) -> Option<(TextRange, Option<PathResolution>)> {
- if let Some(original_string) = ast::String::cast(original_token.clone()) {
- if let Some(quote) = original_string.open_quote_text_range() {
- return self
- .descend_into_macros(DescendPreference::SameText, original_token)
- .into_iter()
- .find_map(|token| {
- self.resolve_offset_in_format_args(
- ast::String::cast(token)?,
- offset.checked_sub(quote.end())?,
- )
- })
- .map(|(range, res)| (range + quote.end(), res));
- }
- }
- None
+ let original_string = ast::String::cast(original_token.clone())?;
+ let quote = original_string.open_quote_text_range()?;
+ self.descend_into_macros_ng_b(original_token.clone(), |kind, token| {
+ (|| {
+ let token = token.value;
+ self.resolve_offset_in_format_args(
+ ast::String::cast(token)?,
+ offset.checked_sub(quote.end())?,
+ )
+ .map(|(range, res)| (range + quote.end(), res))
+ })()
+ .map_or(ControlFlow::Continue(()), ControlFlow::Break)
+ })
}
fn resolve_offset_in_format_args(
@@ -622,7 +656,7 @@ impl<'db> SemanticsImpl<'db> {
if first == last {
// node is just the token, so descend the token
- self.descend_into_macros_impl(first, &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(first, &mut |_kind, InFile { value, .. }| {
if let Some(node) = value
.parent_ancestors()
.take_while(|it| it.text_range() == value.text_range())
@@ -630,20 +664,20 @@ impl<'db> SemanticsImpl<'db> {
{
res.push(node)
}
- ControlFlow::Continue(())
+ CONTINUE_NO_BREAKS
});
} else {
// Descend first and last token, then zip them to look for the node they belong to
let mut scratch: SmallVec<[_; 1]> = smallvec![];
- self.descend_into_macros_impl(first, &mut |token| {
+ self.descend_into_macros_impl(first, &mut |_kind, token| {
scratch.push(token);
- ControlFlow::Continue(())
+ CONTINUE_NO_BREAKS
});
let mut scratch = scratch.into_iter();
self.descend_into_macros_impl(
last,
- &mut |InFile { value: last, file_id: last_fid }| {
+ &mut |_kind, InFile { value: last, file_id: last_fid }| {
if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
if first_fid == last_fid {
if let Some(p) = first.parent() {
@@ -659,7 +693,7 @@ impl<'db> SemanticsImpl<'db> {
}
}
}
- ControlFlow::Continue(())
+ CONTINUE_NO_BREAKS
},
);
}
@@ -673,8 +707,8 @@ impl<'db> SemanticsImpl<'db> {
mode: DescendPreference,
token: SyntaxToken,
) -> SmallVec<[SyntaxToken; 1]> {
- enum Dp<'t> {
- SameText(&'t str),
+ enum Dp {
+ // SameText(&'t str),
SameKind(SyntaxKind),
None,
}
@@ -686,104 +720,123 @@ impl<'db> SemanticsImpl<'db> {
None => token.kind(),
};
let mode = match mode {
- DescendPreference::SameText => Dp::SameText(token.text()),
+ // DescendPreference::SameText => Dp::SameText(token.text()),
DescendPreference::SameKind => Dp::SameKind(fetch_kind(&token)),
DescendPreference::None => Dp::None,
};
let mut res = smallvec![];
- self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
- let is_a_match = match mode {
- Dp::SameText(text) => value.text() == text,
- Dp::SameKind(preferred_kind) => {
- let kind = fetch_kind(&value);
- kind == preferred_kind
+ self.descend_into_macros_impl::<Infallible>(
+ token.clone(),
+ &mut |kind, InFile { value, .. }| {
+ let is_a_match = match mode {
+ // Dp::SameText(text) => value.text() == text,
+ Dp::SameKind(preferred_kind) => {
+ let kind = fetch_kind(&value);
+ kind == preferred_kind
// special case for derive macros
|| (preferred_kind == SyntaxKind::IDENT && kind == SyntaxKind::NAME_REF)
+ }
+ Dp::None => true,
+ };
+ if is_a_match {
+ res.push(value);
}
- Dp::None => true,
- };
- if is_a_match {
- res.push(value);
- }
- ControlFlow::Continue(())
- });
+ ControlFlow::Continue(())
+ },
+ );
if res.is_empty() {
res.push(token);
}
res
}
- pub fn descend_into_macros_single(
+ pub fn descend_into_macros_ng(
&self,
- mode: DescendPreference,
token: SyntaxToken,
- ) -> SyntaxToken {
- enum Dp<'t> {
- SameText(&'t str),
- SameKind(SyntaxKind),
- None,
+ mut cb: impl FnMut(MacroInputKind, InFile<SyntaxToken>),
+ ) {
+ self.descend_into_macros_impl(token.clone(), &mut |kind, t| {
+ cb(kind, t);
+ CONTINUE_NO_BREAKS
+ });
+ }
+
+ pub fn descend_into_macros_ng_b<T>(
+ &self,
+ token: SyntaxToken,
+ mut cb: impl FnMut(MacroInputKind, InFile<SyntaxToken>) -> ControlFlow<T>,
+ ) -> Option<T> {
+ self.descend_into_macros_impl(token.clone(), &mut |kind, t| cb(kind, t))
+ }
+
+ /// Descends the token into expansions, returning the tokens that matches the input
+ /// token's [`SyntaxKind`] and text.
+ pub fn descend_into_macros_exact(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+ let mut r = smallvec![];
+ let text = token.text();
+ let kind = token.kind();
+
+ self.descend_into_macros_ng(token.clone(), |m_kind, InFile { value, file_id }| {
+ let mapped_kind = value.kind();
+ let any_ident_match = || kind.is_any_identifier() && value.kind().is_any_identifier();
+ let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
+ if matches {
+ r.push(value);
+ }
+ });
+ if r.is_empty() {
+ r.push(token);
}
- let fetch_kind = |token: &SyntaxToken| match token.parent() {
- Some(node) => match node.kind() {
- kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => kind,
- _ => token.kind(),
- },
- None => token.kind(),
- };
- let mode = match mode {
- DescendPreference::SameText => Dp::SameText(token.text()),
- DescendPreference::SameKind => Dp::SameKind(fetch_kind(&token)),
- DescendPreference::None => Dp::None,
- };
- let mut res = token.clone();
- self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
- let is_a_match = match mode {
- Dp::SameText(text) => value.text() == text,
- Dp::SameKind(preferred_kind) => {
- let kind = fetch_kind(&value);
- kind == preferred_kind
- // special case for derive macros
- || (preferred_kind == SyntaxKind::IDENT && kind == SyntaxKind::NAME_REF)
- }
- Dp::None => true,
- };
- res = value;
- if is_a_match {
- ControlFlow::Break(())
+ r
+ }
+
+ /// Descends the token into expansions, returning the first token that matches the input
+ /// token's [`SyntaxKind`] and text.
+ pub fn descend_into_macros_single_exact(&self, token: SyntaxToken) -> SyntaxToken {
+ let text = token.text();
+ let kind = token.kind();
+
+ self.descend_into_macros_ng_b(token.clone(), |m_kind, InFile { value, file_id }| {
+ let mapped_kind = value.kind();
+ let any_ident_match = || kind.is_any_identifier() && value.kind().is_any_identifier();
+ let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
+ if matches {
+ ControlFlow::Break(value)
} else {
ControlFlow::Continue(())
}
- });
- res
+ })
+ .unwrap_or(token)
}
- fn descend_into_macros_impl(
+ fn descend_into_macros_impl<T>(
&self,
token: SyntaxToken,
- f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>,
- ) {
+ f: &mut dyn FnMut(MacroInputKind, InFile<SyntaxToken>) -> ControlFlow<T>,
+ ) -> Option<T> {
let _p = tracing::info_span!("descend_into_macros_impl").entered();
let (sa, span, file_id) =
- match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) {
- Some(sa) => match sa.file_id.file_id() {
- Some(file_id) => (
- sa,
- self.db.real_span_map(file_id).span_for_range(token.text_range()),
- file_id.into(),
- ),
- None => {
- stdx::never!();
- return;
- }
- },
- None => return,
- };
+ token.parent().and_then(|parent| self.analyze_no_infer(&parent)).and_then(|sa| {
+ let file_id = sa.file_id.file_id()?;
+ Some((
+ sa,
+ self.db.real_span_map(file_id).span_for_range(token.text_range()),
+ HirFileId::from(file_id),
+ ))
+ })?;
let mut m_cache = self.macro_call_cache.borrow_mut();
let def_map = sa.resolver.def_map();
- let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
- let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
+ // A stack of tokens to process, along with the file they came from
+ // These are tracked to know which macro calls we still have to look into
+ // the tokens themselves aren't that interesting as the span that is being used to map
+ // things down never changes.
+ let mut stack: Vec<(_, _, SmallVec<[_; 2]>)> =
+ vec![(file_id, MacroInputKind::Root, smallvec![token])];
+
+ // Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
+ let process_expansion_for_token = |stack: &mut Vec<_>, macro_file, remap_kind| {
let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
Some(
ctx.cache
@@ -805,11 +858,17 @@ impl<'db> SemanticsImpl<'db> {
// we have found a mapping for the token if the vec is non-empty
let res = mapped_tokens.is_empty().not().then_some(());
// requeue the tokens we got from mapping our current token down
- stack.push((HirFileId::from(file_id), mapped_tokens));
+ stack.push((HirFileId::from(file_id), remap_kind, mapped_tokens));
res
};
- while let Some((file_id, mut tokens)) = stack.pop() {
+ // Filters out all tokens that contain the given range (usually the macro call), any such
+ // token is redundant as the corresponding macro call has already been processed
+ let filter_duplicates = |tokens: &mut SmallVec<_>, range: TextRange| {
+ tokens.retain(|t: &mut SyntaxToken| !range.contains_range(t.text_range()))
+ };
+
+ while let Some((expansion, remap_kind, ref mut tokens)) = stack.pop() {
while let Some(token) = tokens.pop() {
let was_not_remapped = (|| {
// First expand into attribute invocations
@@ -817,7 +876,7 @@ impl<'db> SemanticsImpl<'db> {
token.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
// Don't force populate the dyn cache for items that don't have an attribute anyways
item.attrs().next()?;
- Some((ctx.item_to_macro_call(InFile::new(file_id, &item))?, item))
+ Some((ctx.item_to_macro_call(InFile::new(expansion, &item))?, item))
})
});
if let Some((call_id, item)) = containing_attribute_macro_call {
@@ -849,10 +908,12 @@ impl<'db> SemanticsImpl<'db> {
})
.unwrap_or_else(|| text_range.start());
let text_range = TextRange::new(start, text_range.end());
- // remove any other token in this macro input, all their mappings are the
- // same as this one
- tokens.retain(|t| !text_range.contains_range(t.text_range()));
- return process_expansion_for_token(&mut stack, file_id);
+ filter_duplicates(tokens, text_range);
+ return process_expansion_for_token(
+ &mut stack,
+ file_id,
+ remap_kind | MacroInputKind::AttrTarget,
+ );
}
// Then check for token trees, that means we are either in a function-like macro or
@@ -862,6 +923,7 @@ impl<'db> SemanticsImpl<'db> {
.map_while(Either::<ast::TokenTree, ast::Meta>::cast)
.last()?;
match tt {
+ // function-like macro call
Either::Left(tt) => {
if tt.left_delimiter_token().map_or(false, |it| it == token) {
return None;
@@ -870,7 +932,7 @@ impl<'db> SemanticsImpl<'db> {
return None;
}
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
- let mcall = InFile::new(file_id, macro_call);
+ let mcall = InFile::new(expansion, macro_call);
let file_id = match m_cache.get(&mcall) {
Some(&it) => it,
None => {
@@ -888,17 +950,25 @@ impl<'db> SemanticsImpl<'db> {
}
};
let text_range = tt.syntax().text_range();
- // remove any other token in this macro input, all their mappings are the
- // same as this one
- tokens.retain(|t| !text_range.contains_range(t.text_range()));
-
- process_expansion_for_token(&mut stack, file_id).or(file_id
+ filter_duplicates(tokens, text_range);
+
+ process_expansion_for_token(
+ &mut stack,
+ file_id,
+ remap_kind | MacroInputKind::Bang,
+ )
+ .or(file_id
.eager_arg(self.db.upcast())
.and_then(|arg| {
// also descend into eager expansions
- process_expansion_for_token(&mut stack, arg.as_macro_file())
+ process_expansion_for_token(
+ &mut stack,
+ arg.as_macro_file(),
+ remap_kind | MacroInputKind::Bang,
+ )
}))
}
+ // derive or derive helper
Either::Right(meta) => {
// attribute we failed expansion for earlier, this might be a derive invocation
// or derive helper attribute
@@ -910,8 +980,8 @@ impl<'db> SemanticsImpl<'db> {
// so try downmapping the token into the pseudo derive expansion
// see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
ctx.attr_to_derive_macro_call(
- InFile::new(file_id, &adt),
- InFile::new(file_id, attr.clone()),
+ InFile::new(expansion, &adt),
+ InFile::new(expansion, attr.clone()),
)
.map(|(_, call_id, _)| call_id)
});
@@ -927,7 +997,9 @@ impl<'db> SemanticsImpl<'db> {
!text_range.contains_range(t.text_range())
});
return process_expansion_for_token(
- &mut stack, file_id,
+ &mut stack,
+ file_id,
+ remap_kind | MacroInputKind::Derive,
);
}
None => Some(adt),
@@ -945,31 +1017,33 @@ impl<'db> SemanticsImpl<'db> {
)
}
}?;
- if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(file_id, &adt))) {
+ if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(expansion, &adt))) {
return None;
}
let attr_name =
attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
- // Not an attribute, nor a derive, so it's either a builtin or a derive helper
+ // Not an attribute, nor a derive, so it's either an intert attribute or a derive helper
// Try to resolve to a derive helper and downmap
- let id = self.db.ast_id_map(file_id).ast_id(&adt);
+ let id = self.db.ast_id_map(expansion).ast_id(&adt);
let helpers =
- def_map.derive_helpers_in_scope(InFile::new(file_id, id))?;
+ def_map.derive_helpers_in_scope(InFile::new(expansion, id))?;
if !helpers.is_empty() {
let text_range = attr.syntax().text_range();
- // remove any other token in this macro input, all their mappings are the
- // same as this
- tokens.retain(|t| !text_range.contains_range(t.text_range()));
+ filter_duplicates(tokens, text_range);
}
let mut res = None;
for (.., derive) in
helpers.iter().filter(|(helper, ..)| *helper == attr_name)
{
+ // as there may be multiple derives registering the same helper
+ // name, we gotta make sure to call this for all of them!
+ // FIXME: We need to call `f` for all of them as well though!
res = res.or(process_expansion_for_token(
&mut stack,
derive.as_macro_file(),
+ remap_kind | MacroInputKind::DeriveHelper,
));
}
res
@@ -978,11 +1052,14 @@ impl<'db> SemanticsImpl<'db> {
})()
.is_none();
- if was_not_remapped && f(InFile::new(file_id, token)).is_break() {
- break;
+ if was_not_remapped {
+ if let ControlFlow::Break(b) = f(remap_kind, InFile::new(expansion, token)) {
+ return Some(b);
+ }
}
}
}
+ None
}
// Note this return type is deliberate as [`find_nodes_at_offset_with_descend`] wants to stop
diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index 9180d8dfcb..e4d347ef16 100644
--- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -1,11 +1,7 @@
use crate::{utils, AssistContext, Assists};
-use hir::DescendPreference;
use ide_db::{
assists::{AssistId, AssistKind},
- syntax_helpers::{
- format_string::is_format_string,
- format_string_exprs::{parse_format_exprs, Arg},
- },
+ syntax_helpers::format_string_exprs::{parse_format_exprs, Arg},
};
use itertools::Itertools;
use syntax::{
@@ -40,13 +36,7 @@ pub(crate) fn extract_expressions_from_format_string(
let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
let tt_delimiter = tt.left_delimiter_token()?.kind();
- let expanded_t = ast::String::cast(
- ctx.sema
- .descend_into_macros_single(DescendPreference::SameKind, fmt_string.syntax().clone()),
- )?;
- if !is_format_string(&expanded_t) {
- return None;
- }
+ let _ = ctx.sema.as_format_args_parts(&fmt_string)?;
let (new_fmt, extracted_args) = parse_format_exprs(fmt_string.text()).ok()?;
if extracted_args.is_empty() {
diff --git a/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
index 8ab5a6ede3..c104aa5718 100644
--- a/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
+++ b/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
@@ -31,6 +31,7 @@ pub fn with_placeholders(args: Vec<Arg>) -> Vec<String> {
.collect()
}
+// FIXME Remove this, we have this information in the HIR now
/// Parser for a format-like string. It is more allowing in terms of string contents,
/// as we expect variable placeholders to be filled with expressions.
///
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index cad0d06eea..1304b05702 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -144,7 +144,7 @@ pub(crate) fn external_docs(
kind if kind.is_trivia() => 0,
_ => 1,
})?;
- let token = sema.descend_into_macros_single(DescendPreference::None, token);
+ let token = sema.descend_into_macros_single_exact(token);
let node = token.parent()?;
let definition = match_ast! {
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 5f6aaeaabb..3d49082f28 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -1,6 +1,6 @@
use std::iter::successors;
-use hir::{DescendPreference, Semantics};
+use hir::Semantics;
use ide_db::RootDatabase;
use syntax::{
algo::{self, skip_trivia_token},
@@ -140,10 +140,8 @@ fn extend_tokens_from_range(
// compute original mapped token range
let extended = {
- let fst_expanded =
- sema.descend_into_macros_single(DescendPreference::None, first_token.clone());
- let lst_expanded =
- sema.descend_into_macros_single(DescendPreference::None, last_token.clone());
+ let fst_expanded = sema.descend_into_macros_single_exact(first_token.clone());
+ let lst_expanded = sema.descend_into_macros_single_exact(last_token.clone());
let mut lca =
algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?;
lca = shallowest_node(&lca);
@@ -157,7 +155,7 @@ fn extend_tokens_from_range(
let validate = || {
let extended = &extended;
move |token: &SyntaxToken| -> bool {
- let expanded = sema.descend_into_macros_single(DescendPreference::None, token.clone());
+ let expanded = sema.descend_into_macros_single_exact(token.clone());
let parent = match expanded.parent() {
Some(it) => it,
None => return false,
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 2eff7796d5..e36c8ee2f3 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -1,4 +1,4 @@
-use hir::{AsAssocItem, DescendPreference, Impl, Semantics};
+use hir::{AsAssocItem, Impl, Semantics};
use ide_db::{
defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
@@ -10,7 +10,7 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
// Feature: Go to Implementation
//
-// Navigates to the impl blocks of types.
+// Navigates to the impl items of types.
//
// |===
// | Editor | Shortcut
@@ -32,48 +32,55 @@ pub(crate) fn goto_implementation(
_ => 0,
})?;
let range = original_token.text_range();
- let navs =
- sema.descend_into_macros_single(DescendPreference::SameText, original_token)
- .parent()
- .and_then(ast::NameLike::cast)
- .and_then(|node| match &node {
- ast::NameLike::Name(name) => {
- NameClass::classify(&sema, name).and_then(|class| match class {
- NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
- NameClass::PatFieldShorthand { .. } => None,
- })
- }
- ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
- .and_then(|class| match class {
- NameRefClass::Definition(def) => Some(def),
- NameRefClass::FieldShorthand { .. }
- | NameRefClass::ExternCrateShorthand { .. } => None,
- }),
- ast::NameLike::Lifetime(_) => None,
- })
- .and_then(|def| {
- let navs = match def {
- Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
- Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
- Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
- Definition::BuiltinType(builtin) => impls_for_ty(&sema, builtin.ty(sema.db)),
- Definition::Function(f) => {
- let assoc = f.as_assoc_item(sema.db)?;
- let name = assoc.name(sema.db)?;
- let trait_ = assoc.container_or_implemented_trait(sema.db)?;
- impls_for_trait_item(&sema, trait_, name)
+ let navs = sema
+ .descend_into_macros_exact(original_token)
+ .iter()
+ .filter_map(|token| {
+ token
+ .parent()
+ .and_then(ast::NameLike::cast)
+ .and_then(|node| match &node {
+ ast::NameLike::Name(name) => {
+ NameClass::classify(&sema, name).and_then(|class| match class {
+ NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
+ NameClass::PatFieldShorthand { .. } => None,
+ })
}
- Definition::Const(c) => {
- let assoc = c.as_assoc_item(sema.db)?;
- let name = assoc.name(sema.db)?;
- let trait_ = assoc.container_or_implemented_trait(sema.db)?;
- impls_for_trait_item(&sema, trait_, name)
- }
- _ => return None,
- };
- Some(navs)
- })
- .unwrap_or_default();
+ ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
+ .and_then(|class| match class {
+ NameRefClass::Definition(def) => Some(def),
+ NameRefClass::FieldShorthand { .. }
+ | NameRefClass::ExternCrateShorthand { .. } => None,
+ }),
+ ast::NameLike::Lifetime(_) => None,
+ })
+ .and_then(|def| {
+ let navs = match def {
+ Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
+ Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
+ Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
+ Definition::BuiltinType(builtin) => {
+ impls_for_ty(&sema, builtin.ty(sema.db))
+ }
+ Definition::Function(f) => {
+ let assoc = f.as_assoc_item(sema.db)?;
+ let name = assoc.name(sema.db)?;
+ let trait_ = assoc.container_or_implemented_trait(sema.db)?;
+ impls_for_trait_item(&sema, trait_, name)
+ }
+ Definition::Const(c) => {
+ let assoc = c.as_assoc_item(sema.db)?;
+ let name = assoc.name(sema.db)?;
+ let trait_ = assoc.container_or_implemented_trait(sema.db)?;
+ impls_for_trait_item(&sema, trait_, name)
+ }
+ _ => return None,
+ };
+ Some(navs)
+ })
+ })
+ .flatten()
+ .collect();
Some(RangeInfo { range, info: navs })
}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 6c646cf4ee..b04f5c8411 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -6,7 +6,7 @@ mod tests;
use std::{iter, ops::Not};
use either::Either;
-use hir::{db::DefDatabase, DescendPreference, HasCrate, HasSource, LangItem, Semantics};
+use hir::{db::DefDatabase, HasCrate, HasSource, LangItem, Semantics};
use ide_db::{
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
famous_defs::FamousDefs,
@@ -178,29 +178,24 @@ fn hover_simple(
return Some(RangeInfo::new(range, res));
}
- let in_attr = original_token
- .parent_ancestors()
- .filter_map(ast::Item::cast)
- .any(|item| sema.is_attr_macro_call(&item))
- && !matches!(
- original_token.parent().and_then(ast::TokenTree::cast),
- Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))
- );
-
// prefer descending the same token kind in attribute expansions, in normal macros text
// equivalency is more important
- let descended = sema.descend_into_macros(
- if in_attr { DescendPreference::SameKind } else { DescendPreference::SameText },
- original_token.clone(),
- );
+ let mut descended = vec![];
+ sema.descend_into_macros_ng(original_token.clone(), |_, token| {
+ descended.push(token.value);
+ });
let descended = || descended.iter();
- let result = descended()
+ // FIXME: WE should not try these step by step, instead to accommodate for macros we should run
+ // all of these in "parallel" and rank their results
+ let result = None
// try lint hover
- .find_map(|token| {
- // FIXME: Definition should include known lints and the like instead of having this special case here
- let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
- render::try_for_lint(&attr, token)
+ .or_else(|| {
+ descended().find_map(|token| {
+ // FIXME: Definition should include known lints and the like instead of having this special case here
+ let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
+ render::try_for_lint(&attr, token)
+ })
})
// try definitions
.or_else(|| {
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 3e3d5ef6e6..55afcb59ba 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -9,7 +9,7 @@
//! at the index that the match starts at and its tree parent is
//! resolved to the search element definition, we get a reference.
-use hir::{DescendPreference, PathResolution, Semantics};
+use hir::{PathResolution, Semantics};
use ide_db::{
defs::{Definition, NameClass, NameRefClass},
search::{ReferenceCategory, SearchScope, UsageSearchResult},
@@ -149,7 +149,7 @@ pub(crate) fn find_defs<'a>(
}
Some(
- sema.descend_into_macros(DescendPreference::SameText, token)
+ sema.descend_into_macros_exact(token)
.into_iter()
.filter_map(|it| ast::NameLike::cast(it.parent()?))
.filter_map(move |name_like| {
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 9d3b8c6ebd..516f64959c 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -4,10 +4,7 @@
use std::collections::BTreeSet;
use either::Either;
-use hir::{
- AssocItem, DescendPreference, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics,
- Trait,
-};
+use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait};
use ide_db::{
active_parameter::{callable_for_node, generic_def_for_node},
documentation::{Documentation, HasDocs},
@@ -82,7 +79,7 @@ pub(crate) fn signature_help(
// if the cursor is sandwiched between two space tokens and the call is unclosed
// this prevents us from leaving the CallExpression
.and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
- let token = sema.descend_into_macros_single(DescendPreference::None, token);
+ let token = sema.descend_into_macros_single_exact(token);
let edition =
sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index e082fcdc76..94df794ccb 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -410,7 +410,8 @@ fn traverse(
})
.unwrap()
} else {
- sema.descend_into_macros_single(DescendPreference::SameKind, token)
+ // FIXME: We should probably rank the tokens and find the most suitable?
+ sema.descend_into_macros_single_exact(token)
};
match token.parent().and_then(ast::NameLike::cast) {
// Remap the token into the wrapping single token nodes
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 17411fefbd..d6b7012a1f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -58,7 +58,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
- <span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="operator macro">-</span><span class="operator macro">&gt;</span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span>
+ <span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="punctuation macro">-</span><span class="angle macro">&gt;</span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span>
<span class="numeric_literal macro">100</span>
<span class="brace macro">}</span>
<span class="brace macro">}</span>
@@ -100,7 +100,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span><span class="semicolon">;</span>
<span class="brace">}</span>
-<span class="macro default_library library">include</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"foo/"</span><span class="string_literal macro">,</span> <span class="string_literal macro">"foo.rs"</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro default_library library">include</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"foo/"</span><span class="comma macro">,</span> <span class="string_literal macro">"foo.rs"</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">struct</span> <span class="struct declaration">S</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>