Enable auto-import and qualify-path in derive attributes
This commit is contained in:
parent
3018ffd85e
commit
7fdbdc4ab2
@ -299,8 +299,14 @@ impl SourceToDefCtx<'_, '_> {
|
||||
dyn_map[keys::CONST_PARAM].get(&src).copied()
|
||||
}
|
||||
|
||||
// FIXME: use DynMap as well?
|
||||
pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDefId> {
|
||||
let makro = self.dyn_map(src.as_ref()).and_then(|it| it[keys::MACRO].get(&src).copied());
|
||||
if let res @ Some(_) = makro {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Not all macros are recorded in the dyn map, only the ones behaving like items, so fall back
|
||||
// for the non-item like definitions.
|
||||
let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
|
||||
let ast_id = AstId::new(src.file_id, file_ast_id.upcast());
|
||||
let kind = MacroDefKind::Declarative(ast_id);
|
||||
|
@ -101,6 +101,17 @@ impl ChildBySource for ModuleId {
|
||||
impl ChildBySource for ItemScope {
|
||||
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
||||
self.declarations().for_each(|item| add_module_def(db, file_id, res, item));
|
||||
self.macros().for_each(|(_, makro)| {
|
||||
let ast_id = makro.ast_id();
|
||||
if ast_id.either(|it| it.file_id, |it| it.file_id) == file_id {
|
||||
let src = match ast_id {
|
||||
Either::Left(ast_id) => ast_id.with_value(ast_id.to_node(db.upcast())),
|
||||
// FIXME: Do we need to add proc-macros into a PROCMACRO dynmap here?
|
||||
Either::Right(_fn) => return,
|
||||
};
|
||||
res[keys::MACRO].insert(src, makro);
|
||||
}
|
||||
});
|
||||
self.unnamed_consts().for_each(|konst| {
|
||||
let src = konst.lookup(db).source(db);
|
||||
res[keys::CONST].insert(src, konst);
|
||||
|
@ -31,7 +31,7 @@ pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
|
||||
pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
|
||||
pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
|
||||
|
||||
pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
|
||||
pub const MACRO: Key<ast::Macro, MacroDefId> = Key::new();
|
||||
pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
|
||||
pub const DERIVE_MACRO: Key<ast::Attr, Box<[MacroCallId]>> = Key::new();
|
||||
|
||||
|
@ -1907,7 +1907,7 @@ impl ModCollector<'_, '_> {
|
||||
let mac = &self.item_tree[id];
|
||||
let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
|
||||
|
||||
// Case 1: bulitin macros
|
||||
// Case 1: builtin macros
|
||||
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
|
||||
if attrs.by_key("rustc_builtin_macro").exists() {
|
||||
let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
|
||||
|
@ -128,6 +128,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||
<span class="brace">}</span>
|
||||
|
||||
<span class="comment documentation">/// ```</span>
|
||||
<span class="comment documentation">/// </span><span class="keyword injected">macro_rules</span><span class="punctuation injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="angle injected">></span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
|
||||
<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
|
||||
<span class="comment documentation">/// ```</span>
|
||||
<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
|
||||
|
@ -707,6 +707,7 @@ pub mod module {
|
||||
}
|
||||
|
||||
/// ```
|
||||
/// macro_rules! noop { ($expr:expr) => { $expr }}
|
||||
/// noop!(1);
|
||||
/// ```
|
||||
macro_rules! noop {
|
||||
|
@ -3,7 +3,7 @@ use ide_db::helpers::{
|
||||
insert_use::{insert_use, ImportScope},
|
||||
mod_path_to_ast,
|
||||
};
|
||||
use syntax::{ast, AstNode, AstToken, SyntaxNode};
|
||||
use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxElement};
|
||||
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
|
||||
|
||||
@ -90,9 +90,18 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
return None;
|
||||
}
|
||||
|
||||
let range = ctx.sema.original_range(&syntax_under_caret).range;
|
||||
let range = match &syntax_under_caret {
|
||||
NodeOrToken::Node(node) => ctx.sema.original_range(node).range,
|
||||
NodeOrToken::Token(token) => token.text_range(),
|
||||
};
|
||||
let group_label = group_label(import_assets.import_candidate());
|
||||
let scope = ImportScope::find_insert_use_container_with_macros(&syntax_under_caret, &ctx.sema)?;
|
||||
let scope = ImportScope::find_insert_use_container_with_macros(
|
||||
&match syntax_under_caret {
|
||||
NodeOrToken::Node(it) => it,
|
||||
NodeOrToken::Token(it) => it.parent()?,
|
||||
},
|
||||
&ctx.sema,
|
||||
)?;
|
||||
|
||||
// we aren't interested in different namespaces
|
||||
proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
|
||||
@ -115,23 +124,24 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> {
|
||||
pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxElement)> {
|
||||
if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
|
||||
ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
|
||||
.zip(Some(path_under_caret.syntax().clone()))
|
||||
.zip(Some(path_under_caret.syntax().clone().into()))
|
||||
} else if let Some(method_under_caret) =
|
||||
ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
|
||||
{
|
||||
ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
|
||||
.zip(Some(method_under_caret.syntax().clone()))
|
||||
.zip(Some(method_under_caret.syntax().clone().into()))
|
||||
} else if let Some(pat) = ctx
|
||||
.find_node_at_offset_with_descend::<ast::IdentPat>()
|
||||
.filter(ast::IdentPat::is_simple_ident)
|
||||
{
|
||||
ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone()))
|
||||
ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone().into()))
|
||||
} else {
|
||||
// FIXME: Descend?
|
||||
let ident = ctx.find_token_at_offset()?;
|
||||
ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(ident.syntax().parent())
|
||||
ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(Some(ident.syntax().clone().into()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1028,6 +1038,32 @@ mod foo {
|
||||
fn foo() {
|
||||
let Foo;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_in_derives() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
r#"
|
||||
//- minicore:derive
|
||||
mod foo {
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
}
|
||||
#[derive(Copy$0)]
|
||||
struct Foo;
|
||||
"#,
|
||||
r#"
|
||||
use foo::Copy;
|
||||
|
||||
mod foo {
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
}
|
||||
#[derive(Copy)]
|
||||
struct Foo;
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use ide_db::RootDatabase;
|
||||
use syntax::{
|
||||
ast,
|
||||
ast::{make, HasArgList},
|
||||
AstNode,
|
||||
AstNode, NodeOrToken,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -42,32 +42,39 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
return None;
|
||||
}
|
||||
|
||||
let range = ctx.sema.original_range(&syntax_under_caret).range;
|
||||
let range = match &syntax_under_caret {
|
||||
NodeOrToken::Node(node) => ctx.sema.original_range(node).range,
|
||||
NodeOrToken::Token(token) => token.text_range(),
|
||||
};
|
||||
let candidate = import_assets.import_candidate();
|
||||
let qualify_candidate = match candidate {
|
||||
ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
|
||||
cov_mark::hit!(qualify_path_qualifier_start);
|
||||
let path = ast::Path::cast(syntax_under_caret)?;
|
||||
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
|
||||
QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
|
||||
}
|
||||
ImportCandidate::Path(_) => {
|
||||
cov_mark::hit!(qualify_path_unqualified_name);
|
||||
let path = ast::Path::cast(syntax_under_caret)?;
|
||||
let generics = path.segment()?.generic_arg_list();
|
||||
QualifyCandidate::UnqualifiedName(generics)
|
||||
}
|
||||
ImportCandidate::TraitAssocItem(_) => {
|
||||
cov_mark::hit!(qualify_path_trait_assoc_item);
|
||||
let path = ast::Path::cast(syntax_under_caret)?;
|
||||
let (qualifier, segment) = (path.qualifier()?, path.segment()?);
|
||||
QualifyCandidate::TraitAssocItem(qualifier, segment)
|
||||
}
|
||||
ImportCandidate::TraitMethod(_) => {
|
||||
cov_mark::hit!(qualify_path_trait_method);
|
||||
let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
|
||||
QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
|
||||
}
|
||||
let qualify_candidate = match syntax_under_caret {
|
||||
NodeOrToken::Node(syntax_under_caret) => match candidate {
|
||||
ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
|
||||
cov_mark::hit!(qualify_path_qualifier_start);
|
||||
let path = ast::Path::cast(syntax_under_caret)?;
|
||||
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
|
||||
QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
|
||||
}
|
||||
ImportCandidate::Path(_) => {
|
||||
cov_mark::hit!(qualify_path_unqualified_name);
|
||||
let path = ast::Path::cast(syntax_under_caret)?;
|
||||
let generics = path.segment()?.generic_arg_list();
|
||||
QualifyCandidate::UnqualifiedName(generics)
|
||||
}
|
||||
ImportCandidate::TraitAssocItem(_) => {
|
||||
cov_mark::hit!(qualify_path_trait_assoc_item);
|
||||
let path = ast::Path::cast(syntax_under_caret)?;
|
||||
let (qualifier, segment) = (path.qualifier()?, path.segment()?);
|
||||
QualifyCandidate::TraitAssocItem(qualifier, segment)
|
||||
}
|
||||
ImportCandidate::TraitMethod(_) => {
|
||||
cov_mark::hit!(qualify_path_trait_method);
|
||||
let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
|
||||
QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
|
||||
}
|
||||
},
|
||||
// derive attribute path
|
||||
NodeOrToken::Token(_) => QualifyCandidate::UnqualifiedName(None),
|
||||
};
|
||||
|
||||
// we aren't interested in different namespaces
|
||||
@ -1236,6 +1243,30 @@ fn main() {
|
||||
let test_struct = test_mod::TestStruct {};
|
||||
test_mod::TestTrait::test_method::<()>(&test_struct)
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_in_derives() {
|
||||
check_assist(
|
||||
qualify_path,
|
||||
r#"
|
||||
//- minicore:derive
|
||||
mod foo {
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
}
|
||||
#[derive(Copy$0)]
|
||||
struct Foo;
|
||||
"#,
|
||||
r#"
|
||||
mod foo {
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
}
|
||||
#[derive(foo::Copy)]
|
||||
struct Foo;
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user