Skip to content

Commit

Permalink
Auto merge of rust-lang#16137 - unexge:complete-macros-in-macro-use, …
Browse files Browse the repository at this point in the history
…r=Veykril

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

Closes rust-lang/rust-analyzer#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.
  • Loading branch information
bors committed Dec 22, 2023
2 parents 20e09c6 + 3b8801c commit 23a1280
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 1 deletion.
5 changes: 5 additions & 0 deletions crates/ide-completion/src/completions/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod cfg;
mod derive;
mod lint;
mod repr;
mod macro_use;

pub(crate) use self::derive::complete_derive_path;

Expand All @@ -35,6 +36,7 @@ pub(crate) fn complete_known_attribute_input(
ctx: &CompletionContext<'_>,
&colon_prefix: &bool,
fake_attribute_under_caret: &ast::Attr,
extern_crate: Option<&ast::ExternCrate>,
) -> Option<()> {
let attribute = fake_attribute_under_caret;
let name_ref = match attribute.path() {
Expand Down Expand Up @@ -66,6 +68,9 @@ pub(crate) fn complete_known_attribute_input(
lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints);
}
"cfg" => cfg::complete_cfg(acc, ctx),
"macro_use" => {
macro_use::complete_macro_use(acc, ctx, extern_crate, &parse_tt_as_comma_sep_paths(tt)?)
}
_ => (),
}
Some(())
Expand Down
35 changes: 35 additions & 0 deletions crates/ide-completion/src/completions/attribute/macro_use.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Completion for macros in `#[macro_use(...)]`
use hir::ModuleDef;
use ide_db::SymbolKind;
use syntax::ast;

use crate::{context::CompletionContext, item::CompletionItem, Completions};

pub(super) fn complete_macro_use(
acc: &mut Completions,
ctx: &CompletionContext<'_>,
extern_crate: Option<&ast::ExternCrate>,
existing_imports: &[ast::Path],
) {
let Some(extern_crate) = extern_crate else { return };
let Some(extern_crate) = ctx.sema.to_def(extern_crate) else { return };
let Some(krate) = extern_crate.resolved_crate(ctx.db) else { return };

for mod_def in krate.root_module().declarations(ctx.db) {
if let ModuleDef::Macro(mac) = mod_def {
let mac_name = mac.name(ctx.db);
let Some(mac_name) = mac_name.as_str() else { continue };

let existing_import = existing_imports
.iter()
.filter_map(|p| p.as_single_name_ref())
.find(|n| n.text() == mac_name);
if existing_import.is_some() {
continue;
}

let item = CompletionItem::new(SymbolKind::Macro, ctx.source_range(), mac_name);
item.add_to(acc, ctx.db);
}
}
}
1 change: 1 addition & 0 deletions crates/ide-completion/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ pub(super) enum CompletionAnalysis {
UnexpandedAttrTT {
colon_prefix: bool,
fake_attribute_under_caret: Option<ast::Attr>,
extern_crate: Option<ast::ExternCrate>,
},
}

Expand Down
2 changes: 2 additions & 0 deletions crates/ide-completion/src/context/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,13 @@ fn analyze(
{
let colon_prefix = previous_non_trivia_token(self_token.clone())
.map_or(false, |it| T![:] == it.kind());

CompletionAnalysis::UnexpandedAttrTT {
fake_attribute_under_caret: fake_ident_token
.parent_ancestors()
.find_map(ast::Attr::cast),
colon_prefix,
extern_crate: p.ancestors().find_map(ast::ExternCrate::cast),
}
} else {
return None;
Expand Down
2 changes: 2 additions & 0 deletions crates/ide-completion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,14 @@ pub fn completions(
CompletionAnalysis::UnexpandedAttrTT {
colon_prefix,
fake_attribute_under_caret: Some(attr),
extern_crate,
} => {
completions::attribute::complete_known_attribute_input(
acc,
ctx,
colon_prefix,
attr,
extern_crate.as_ref(),
);
}
CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (),
Expand Down
79 changes: 79 additions & 0 deletions crates/ide-completion/src/tests/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1067,3 +1067,82 @@ mod repr {
);
}
}

mod macro_use {
use super::*;

#[test]
fn completes_macros() {
check(
r#"
//- /dep.rs crate:dep
#[macro_export]
macro_rules! foo {
() => {};
}
#[macro_export]
macro_rules! bar {
() => {};
}
//- /main.rs crate:main deps:dep
#[macro_use($0)]
extern crate dep;
"#,
expect![[r#"
ma bar
ma foo
"#]],
)
}

#[test]
fn only_completes_exported_macros() {
check(
r#"
//- /dep.rs crate:dep
#[macro_export]
macro_rules! foo {
() => {};
}
macro_rules! bar {
() => {};
}
//- /main.rs crate:main deps:dep
#[macro_use($0)]
extern crate dep;
"#,
expect![[r#"
ma foo
"#]],
)
}

#[test]
fn does_not_completes_already_imported_macros() {
check(
r#"
//- /dep.rs crate:dep
#[macro_export]
macro_rules! foo {
() => {};
}
#[macro_export]
macro_rules! bar {
() => {};
}
//- /main.rs crate:main deps:dep
#[macro_use(foo, $0)]
extern crate dep;
"#,
expect![[r#"
ma bar
"#]],
)
}
}
71 changes: 70 additions & 1 deletion crates/ide/src/goto_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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)?
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
() => {};
}
"#,
);
}
}

0 comments on commit 23a1280

Please sign in to comment.