Auto merge of #16137 - unexge:complete-macros-in-macro-use, r=Veykril

Complete exported macros in `#[macro_use($0)]`

Closes https://github.com/rust-lang/rust-analyzer/issues/15657.

Originally added a test case for incomplete input:
```rust
#[test]
fn completes_incomplete_syntax() {
    check(
        r#"
//- /dep.rs crate:dep
#[macro_export]
macro_rules! foo {
    () => {};
}

//- /main.rs crate:main deps:dep
#[macro_use($0
extern crate dep;
"#,
        expect![[r#"
                ma foo
            "#]],
    )
}
```

but couldn't make it pass and removed it 😅 Our current recovering logic doesn't work for token trees and for this code:
```rust
#[macro_use(
extern crate lazy_static;

fn main() {}
```

we ended up with this syntax tree:
```
SOURCE_FILE@0..53
  ATTR@0..52
    POUND@0..1 "#"
    L_BRACK@1..2 "["
    META@2..52
      PATH@2..11
        PATH_SEGMENT@2..11
          NAME_REF@2..11
            IDENT@2..11 "macro_use"
      TOKEN_TREE@11..52
        L_PAREN@11..12 "("
        WHITESPACE@12..13 "\n"
        EXTERN_KW@13..19 "extern"
        WHITESPACE@19..20 " "
        CRATE_KW@20..25 "crate"
        WHITESPACE@25..26 " "
        IDENT@26..37 "lazy_static"
        SEMICOLON@37..38 ";"
        WHITESPACE@38..40 "\n\n"
        FN_KW@40..42 "fn"
        WHITESPACE@42..43 " "
        IDENT@43..47 "main"
        TOKEN_TREE@47..49
          L_PAREN@47..48 "("
          R_PAREN@48..49 ")"
        WHITESPACE@49..50 " "
        TOKEN_TREE@50..52
          L_CURLY@50..51 "{"
          R_CURLY@51..52 "}"
  WHITESPACE@52..53 "\n"
```

Maybe we can try to parse the token tree in `crates/ide-completion/src/context/analysis.rs` but I'm not sure what's the best way forward.
This commit is contained in:
bors 2023-12-22 10:42:38 +00:00
commit 23a1280106
7 changed files with 194 additions and 1 deletions

View file

@ -4,7 +4,7 @@ use crate::{
doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget,
RangeInfo, TryToNav,
};
use hir::{AsAssocItem, AssocItem, DescendPreference, Semantics};
use hir::{AsAssocItem, AssocItem, DescendPreference, ModuleDef, Semantics};
use ide_db::{
base_db::{AnchoredPath, FileId, FileLoader},
defs::{Definition, IdentClass},
@ -73,10 +73,15 @@ pub(crate) fn goto_definition(
.into_iter()
.filter_map(|token| {
let parent = token.parent()?;
if let Some(tt) = ast::TokenTree::cast(parent.clone()) {
if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) {
return Some(vec![x]);
}
if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.clone()) {
return Some(vec![x]);
}
}
Some(
IdentClass::classify_node(sema, &parent)?
@ -140,6 +145,27 @@ fn try_lookup_include_path(
})
}
fn try_lookup_macro_def_in_macro_use(
sema: &Semantics<'_, RootDatabase>,
token: SyntaxToken,
) -> Option<NavigationTarget> {
let extern_crate = token.parent()?.ancestors().find_map(ast::ExternCrate::cast)?;
let extern_crate = sema.to_def(&extern_crate)?;
let krate = extern_crate.resolved_crate(sema.db)?;
for mod_def in krate.root_module().declarations(sema.db) {
if let ModuleDef::Macro(mac) = mod_def {
if mac.name(sema.db).as_str() == Some(token.text()) {
if let Some(nav) = mac.try_to_nav(sema.db) {
return Some(nav.call_site);
}
}
}
}
None
}
/// finds the trait definition of an impl'd item, except function
/// e.g.
/// ```rust
@ -2081,4 +2107,47 @@ fn test() {
"#,
);
}
#[test]
fn goto_macro_def_from_macro_use() {
check(
r#"
//- /main.rs crate:main deps:mac
#[macro_use(foo$0)]
extern crate mac;
//- /mac.rs crate:mac
#[macro_export]
macro_rules! foo {
//^^^
() => {};
}
"#,
);
check(
r#"
//- /main.rs crate:main deps:mac
#[macro_use(foo, bar$0, baz)]
extern crate mac;
//- /mac.rs crate:mac
#[macro_export]
macro_rules! foo {
() => {};
}
#[macro_export]
macro_rules! bar {
//^^^
() => {};
}
#[macro_export]
macro_rules! baz {
() => {};
}
"#,
);
}
}