mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-26 03:45:04 +00:00
feat: Make unqualified derive attributes flyimportable
This commit is contained in:
parent
7fdbdc4ab2
commit
ebd63ec1cf
11 changed files with 124 additions and 31 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -99,6 +99,7 @@ dependencies = [
|
||||||
"profile",
|
"profile",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"salsa",
|
"salsa",
|
||||||
|
"stdx",
|
||||||
"syntax",
|
"syntax",
|
||||||
"test_utils",
|
"test_utils",
|
||||||
"tt",
|
"tt",
|
||||||
|
|
|
@ -14,6 +14,7 @@ salsa = "0.17.0-pre.2"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
|
|
||||||
syntax = { path = "../syntax", version = "0.0.0" }
|
syntax = { path = "../syntax", version = "0.0.0" }
|
||||||
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
cfg = { path = "../cfg", version = "0.0.0" }
|
cfg = { path = "../cfg", version = "0.0.0" }
|
||||||
profile = { path = "../profile", version = "0.0.0" }
|
profile = { path = "../profile", version = "0.0.0" }
|
||||||
tt = { path = "../tt", version = "0.0.0" }
|
tt = { path = "../tt", version = "0.0.0" }
|
||||||
|
|
|
@ -270,7 +270,7 @@ fn test_proc_macros(proc_macros: &[String]) -> (Vec<ProcMacro>, String) {
|
||||||
pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
#[proc_macro_derive(derive_identity)]
|
#[proc_macro_derive(DeriveIdentity)]
|
||||||
pub fn derive_identity(item: TokenStream) -> TokenStream {
|
pub fn derive_identity(item: TokenStream) -> TokenStream {
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
|
||||||
expander: Arc::new(IdentityProcMacroExpander),
|
expander: Arc::new(IdentityProcMacroExpander),
|
||||||
},
|
},
|
||||||
ProcMacro {
|
ProcMacro {
|
||||||
name: "derive_identity".into(),
|
name: "DeriveIdentity".into(),
|
||||||
kind: crate::ProcMacroKind::CustomDerive,
|
kind: crate::ProcMacroKind::CustomDerive,
|
||||||
expander: Arc::new(IdentityProcMacroExpander),
|
expander: Arc::new(IdentityProcMacroExpander),
|
||||||
},
|
},
|
||||||
|
@ -306,7 +306,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|pm| proc_macros.iter().any(|name| name == pm.name))
|
.filter(|pm| proc_macros.iter().any(|name| name == &stdx::to_lower_snake_case(&pm.name)))
|
||||||
.collect();
|
.collect();
|
||||||
(proc_macros, source.into())
|
(proc_macros, source.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1623,7 +1623,12 @@ impl MacroDef {
|
||||||
pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
|
pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
|
||||||
match self.source(db)?.value {
|
match self.source(db)?.value {
|
||||||
Either::Left(it) => it.name().map(|it| it.as_name()),
|
Either::Left(it) => it.name().map(|it| it.as_name()),
|
||||||
Either::Right(it) => it.name().map(|it| it.as_name()),
|
Either::Right(_) => {
|
||||||
|
let krate = self.id.krate;
|
||||||
|
let def_map = db.crate_def_map(krate);
|
||||||
|
let (_, name) = def_map.exported_proc_macros().find(|&(id, _)| id == self.id)?;
|
||||||
|
Some(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ fn derive_censoring() {
|
||||||
//- proc_macros: derive_identity
|
//- proc_macros: derive_identity
|
||||||
#[attr1]
|
#[attr1]
|
||||||
#[derive(Foo)]
|
#[derive(Foo)]
|
||||||
#[derive(proc_macros::derive_identity)]
|
#[derive(proc_macros::DeriveIdentity)]
|
||||||
#[derive(Bar)]
|
#[derive(Bar)]
|
||||||
#[attr2]
|
#[attr2]
|
||||||
struct S;
|
struct S;
|
||||||
|
@ -41,7 +41,7 @@ struct S;
|
||||||
expect![[r##"
|
expect![[r##"
|
||||||
#[attr1]
|
#[attr1]
|
||||||
#[derive(Foo)]
|
#[derive(Foo)]
|
||||||
#[derive(proc_macros::derive_identity)]
|
#[derive(proc_macros::DeriveIdentity)]
|
||||||
#[derive(Bar)]
|
#[derive(Bar)]
|
||||||
#[attr2]
|
#[attr2]
|
||||||
struct S;
|
struct S;
|
||||||
|
|
|
@ -103,7 +103,7 @@ pub struct DefMap {
|
||||||
/// Side table with additional proc. macro info, for use by name resolution in downstream
|
/// Side table with additional proc. macro info, for use by name resolution in downstream
|
||||||
/// crates.
|
/// crates.
|
||||||
///
|
///
|
||||||
/// (the primary purpose is to resolve derive helpers)
|
/// (the primary purpose is to resolve derive helpers and fetch a proc-macros name)
|
||||||
exported_proc_macros: FxHashMap<MacroDefId, ProcMacroDef>,
|
exported_proc_macros: FxHashMap<MacroDefId, ProcMacroDef>,
|
||||||
|
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
|
@ -279,7 +279,9 @@ impl DefMap {
|
||||||
pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
|
pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
|
||||||
self.modules.iter()
|
self.modules.iter()
|
||||||
}
|
}
|
||||||
|
pub fn exported_proc_macros(&self) -> impl Iterator<Item = (MacroDefId, Name)> + '_ {
|
||||||
|
self.exported_proc_macros.iter().map(|(id, def)| (*id, def.name.clone()))
|
||||||
|
}
|
||||||
pub fn root(&self) -> LocalModuleId {
|
pub fn root(&self) -> LocalModuleId {
|
||||||
self.root
|
self.root
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,8 @@ impl Name {
|
||||||
/// Ideally, we want a `gensym` semantics for missing names -- each missing
|
/// Ideally, we want a `gensym` semantics for missing names -- each missing
|
||||||
/// name is equal only to itself. It's not clear how to implement this in
|
/// name is equal only to itself. It's not clear how to implement this in
|
||||||
/// salsa though, so we punt on that bit for a moment.
|
/// salsa though, so we punt on that bit for a moment.
|
||||||
pub fn missing() -> Name {
|
pub const fn missing() -> Name {
|
||||||
Name::new_text("[missing name]".into())
|
Name::new_inline("[missing name]")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the tuple index this name represents if it is a tuple field.
|
/// Returns the tuple index this name represents if it is a tuple field.
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
//! Completion for derives
|
//! Completion for derives
|
||||||
use hir::{HasAttrs, MacroDef, MacroKind};
|
use hir::{HasAttrs, MacroDef, MacroKind};
|
||||||
use ide_db::helpers::FamousDefs;
|
use ide_db::helpers::{import_assets::ImportAssets, insert_use::ImportScope, FamousDefs};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use syntax::ast;
|
use syntax::{ast, SyntaxKind};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
completions::flyimport::compute_fuzzy_completion_order_key,
|
||||||
context::CompletionContext,
|
context::CompletionContext,
|
||||||
item::{CompletionItem, CompletionItemKind},
|
item::{CompletionItem, CompletionItemKind},
|
||||||
Completions,
|
Completions, ImportEdit,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) fn complete_derive(
|
pub(super) fn complete_derive(
|
||||||
|
@ -66,6 +67,8 @@ pub(super) fn complete_derive(
|
||||||
}
|
}
|
||||||
item.add_to(acc);
|
item.add_to(acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flyimport_attribute(ctx, acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, MacroDef)> {
|
fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, MacroDef)> {
|
||||||
|
@ -80,6 +83,50 @@ fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, MacroDef)> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn flyimport_attribute(ctx: &CompletionContext, acc: &mut Completions) -> Option<()> {
|
||||||
|
if ctx.token.kind() != SyntaxKind::IDENT {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let potential_import_name = ctx.token.to_string();
|
||||||
|
let module = ctx.scope.module()?;
|
||||||
|
let parent = ctx.token.parent()?;
|
||||||
|
let user_input_lowercased = potential_import_name.to_lowercase();
|
||||||
|
let import_assets = ImportAssets::for_fuzzy_path(
|
||||||
|
module,
|
||||||
|
None,
|
||||||
|
potential_import_name,
|
||||||
|
&ctx.sema,
|
||||||
|
parent.clone(),
|
||||||
|
)?;
|
||||||
|
let import_scope = ImportScope::find_insert_use_container_with_macros(&parent, &ctx.sema)?;
|
||||||
|
acc.add_all(
|
||||||
|
import_assets
|
||||||
|
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|import| match import.original_item {
|
||||||
|
hir::ItemInNs::Macros(mac) => Some((import, mac)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter(|&(_, mac)| !ctx.is_item_hidden(&hir::ItemInNs::Macros(mac)))
|
||||||
|
.sorted_by_key(|(import, _)| {
|
||||||
|
compute_fuzzy_completion_order_key(&import.import_path, &user_input_lowercased)
|
||||||
|
})
|
||||||
|
.filter_map(|(import, mac)| {
|
||||||
|
let mut item = CompletionItem::new(
|
||||||
|
CompletionItemKind::Attribute,
|
||||||
|
ctx.source_range(),
|
||||||
|
mac.name(ctx.db)?.to_string(),
|
||||||
|
);
|
||||||
|
item.add_import(ImportEdit { import, scope: import_scope.clone() });
|
||||||
|
if let Some(docs) = mac.docs(ctx.db) {
|
||||||
|
item.documentation(docs);
|
||||||
|
}
|
||||||
|
Some(item.build())
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
struct DeriveDependencies {
|
struct DeriveDependencies {
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
dependencies: &'static [&'static str],
|
dependencies: &'static [&'static str],
|
||||||
|
|
|
@ -125,12 +125,12 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
|
let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.clone());
|
||||||
|
|
||||||
let user_input_lowercased = potential_import_name.to_lowercase();
|
let user_input_lowercased = potential_import_name.to_lowercase();
|
||||||
let import_assets = import_assets(ctx, potential_import_name)?;
|
let import_assets = import_assets(ctx, potential_import_name)?;
|
||||||
let import_scope = ImportScope::find_insert_use_container_with_macros(
|
let import_scope = ImportScope::find_insert_use_container_with_macros(
|
||||||
position_for_import(ctx, Some(import_assets.import_candidate()))?,
|
&position_for_import(ctx, Some(import_assets.import_candidate()))?,
|
||||||
&ctx.sema,
|
&ctx.sema,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -158,21 +158,19 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn position_for_import<'a>(
|
pub(crate) fn position_for_import(
|
||||||
ctx: &'a CompletionContext,
|
ctx: &CompletionContext,
|
||||||
import_candidate: Option<&ImportCandidate>,
|
import_candidate: Option<&ImportCandidate>,
|
||||||
) -> Option<&'a SyntaxNode> {
|
) -> Option<SyntaxNode> {
|
||||||
Some(match import_candidate {
|
Some(
|
||||||
Some(ImportCandidate::Path(_)) => ctx.name_syntax.as_ref()?.syntax(),
|
match import_candidate {
|
||||||
Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
|
Some(ImportCandidate::Path(_)) => ctx.name_syntax.as_ref()?.syntax(),
|
||||||
Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
|
Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
|
||||||
None => ctx
|
Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
|
||||||
.name_syntax
|
None => return ctx.original_token.parent(),
|
||||||
.as_ref()
|
}
|
||||||
.map(|name_ref| name_ref.syntax())
|
.clone(),
|
||||||
.or_else(|| ctx.path_qual().map(|path| path.syntax()))
|
)
|
||||||
.or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
|
fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
|
||||||
|
@ -205,7 +203,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_fuzzy_completion_order_key(
|
pub(crate) fn compute_fuzzy_completion_order_key(
|
||||||
proposed_mod_path: &hir::ModPath,
|
proposed_mod_path: &hir::ModPath,
|
||||||
user_input_lowercased: &str,
|
user_input_lowercased: &str,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
|
|
@ -182,7 +182,7 @@ pub fn resolve_completion_edits(
|
||||||
) -> Option<Vec<TextEdit>> {
|
) -> Option<Vec<TextEdit>> {
|
||||||
let _p = profile::span("resolve_completion_edits");
|
let _p = profile::span("resolve_completion_edits");
|
||||||
let ctx = CompletionContext::new(db, position, config)?;
|
let ctx = CompletionContext::new(db, position, config)?;
|
||||||
let position_for_import = position_for_import(&ctx, None)?;
|
let position_for_import = &position_for_import(&ctx, None)?;
|
||||||
let scope = ImportScope::find_insert_use_container_with_macros(position_for_import, &ctx.sema)?;
|
let scope = ImportScope::find_insert_use_container_with_macros(position_for_import, &ctx.sema)?;
|
||||||
|
|
||||||
let current_module = ctx.sema.scope(position_for_import).module()?;
|
let current_module = ctx.sema.scope(position_for_import).module()?;
|
||||||
|
|
|
@ -640,6 +640,45 @@ mod derive {
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derive_flyimport() {
|
||||||
|
check_derive(
|
||||||
|
r#"
|
||||||
|
//- proc_macros: derive_identity
|
||||||
|
#[derive(der$0)] struct Test;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
at DeriveIdentity (use proc_macros::DeriveIdentity)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check_derive(
|
||||||
|
r#"
|
||||||
|
//- proc_macros: derive_identity
|
||||||
|
use proc_macros::DeriveIdentity;
|
||||||
|
#[derive(der$0)] struct Test;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
at DeriveIdentity
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derive_flyimport_edit() {
|
||||||
|
check_edit(
|
||||||
|
"DeriveIdentity",
|
||||||
|
r#"
|
||||||
|
//- proc_macros: derive_identity
|
||||||
|
#[derive(der$0)] struct Test;
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
use proc_macros::DeriveIdentity;
|
||||||
|
|
||||||
|
#[derive(DeriveIdentity)] struct Test;
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod lint {
|
mod lint {
|
||||||
|
|
Loading…
Reference in a new issue