about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs353
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/extend_selection.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs93
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs33
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html4
13 files changed, 304 insertions, 229 deletions
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 56e80e11e77..d8f9b2b7f6a 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 2bea9026265..741c2b2ebfe 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index d086aee4285..a8151e70a10 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index 9180d8dfcbb..e4d347ef16b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
index 8ab5a6ede3b..c104aa57189 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index cad0d06eeab..1304b057021 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
index 5f6aaeaabb6..3d49082f285 100644
--- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
index 2eff7796d54..e36c8ee2f3f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 6c646cf4ee1..b04f5c8411e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 3e3d5ef6e65..55afcb59bae 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index 9d3b8c6ebd1..516f64959ce 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index e082fcdc765..94df794ccbb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 17411fefbd9..d6b7012a1f9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/src/tools/rust-analyzer/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>