Handle proc-macro functions as the proc-macro they resolve to

This commit is contained in:
Lukas Wirth 2022-02-11 22:06:03 +01:00
parent 1dee488a68
commit cef8a17ea5
7 changed files with 78 additions and 15 deletions

View file

@ -1380,6 +1380,23 @@ impl Function {
db.function_data(self.id).has_body()
}
pub fn as_proc_macro(self, db: &dyn HirDatabase) -> Option<MacroDef> {
let function_data = db.function_data(self.id);
let attrs = &function_data.attrs;
if !(attrs.is_proc_macro()
|| attrs.is_proc_macro_attribute()
|| attrs.is_proc_macro_derive())
{
return None;
}
let loc = self.id.lookup(db.upcast());
let krate = loc.krate(db);
let def_map = db.crate_def_map(krate.into());
let name = &function_data.name;
let mut exported_proc_macros = def_map.exported_proc_macros();
exported_proc_macros.find(|(_, mac_name)| mac_name == name).map(|(id, _)| MacroDef { id })
}
/// A textual representation of the HIR of this function for debugging purposes.
pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
let body = db.body(self.id.into());

View file

@ -236,7 +236,9 @@ impl Attrs {
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
}
impl Attrs {
pub fn cfg(&self) -> Option<CfgExpr> {
let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>();
match cfgs.len() {
@ -298,6 +300,18 @@ impl Attrs {
matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "hidden")
})
}
pub fn is_proc_macro(&self) -> bool {
self.by_key("proc_macro").exists()
}
pub fn is_proc_macro_attribute(&self) -> bool {
self.by_key("proc_macro_attribute").exists()
}
pub fn is_proc_macro_derive(&self) -> bool {
self.by_key("proc_macro_derive").exists()
}
}
impl AttrsWithOwner {

View file

@ -31,12 +31,12 @@ impl ProcMacroKind {
impl Attrs {
#[rustfmt::skip]
pub(super) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
if self.by_key("proc_macro").exists() {
if self.is_proc_macro() {
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike })
} else if self.by_key("proc_macro_attribute").exists() {
} else if self.is_proc_macro_attribute() {
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
} else if self.by_key("proc_macro_derive").exists() {
let derive = self.by_key("proc_macro_derive").tt_values().next().unwrap();
let derive = self.by_key("proc_macro_derive").tt_values().next()?;
match &*derive.token_trees {
// `#[proc_macro_derive(Trait)]`

View file

@ -57,8 +57,6 @@ pub(crate) fn find_all_refs(
let syntax = sema.parse(position.file_id).syntax().clone();
let make_searcher = |literal_search: bool| {
move |def: Definition| {
let mut usages =
def.usages(sema).set_scope(search_scope.clone()).include_self_refs().all();
let declaration = match def {
Definition::Module(module) => {
Some(NavigationTarget::from_module_to_decl(sema.db, module))
@ -72,6 +70,8 @@ pub(crate) fn find_all_refs(
nav,
}
});
let mut usages =
def.usages(sema).set_scope(search_scope.clone()).include_self_refs().all();
if literal_search {
retain_adt_literal_usages(&mut usages, def, sema);
}
@ -1535,4 +1535,34 @@ trait Trait {
"#]],
)
}
#[test]
fn attr() {
check(
r#"
//- proc_macros: identity
#[proc_macros::$0identity]
fn func() {}
"#,
expect![[r#"
identity Attribute FileId(1) 1..107 32..40
FileId(0) 16..24
"#]],
)
}
#[test]
fn derive() {
check(
r#"
//- proc_macros: derive_identity
#[derive(proc_macros::DeriveIdentity$0)]
struct Foo;
"#,
expect![[r#""#]],
)
}
}

View file

@ -82,6 +82,9 @@ pub fn pick_best_token(
) -> Option<SyntaxToken> {
tokens.max_by_key(move |t| f(t.kind()))
}
pub fn pick_token<T: AstToken>(mut tokens: TokenAtOffset<SyntaxToken>) -> Option<T> {
tokens.find_map(T::cast)
}
/// Converts the mod path struct into its ast representation.
pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {

View file

@ -309,13 +309,14 @@ impl Definition {
}
pub fn usages<'a>(self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> {
FindUsages {
def: self,
sema,
scope: None,
include_self_kw_refs: None,
search_self_mod: false,
}
let def = match self {
def @ Definition::Function(f) => {
// search for proc-macro usages if this function describes a proc macro
f.as_proc_macro(sema.db).map(Definition::Macro).unwrap_or(def)
}
def => def,
};
FindUsages { def, sema, scope: None, include_self_kw_refs: None, search_self_mod: false }
}
}

View file

@ -275,9 +275,7 @@ pub(crate) fn handle_on_type_formatting(
let char_typed = params.ch.chars().next().unwrap_or('\0');
let text = snap.analysis.file_text(position.file_id)?;
if !text[usize::from(position.offset)..].starts_with(char_typed) {
// Add `always!` here once VS Code bug is fixed:
// https://github.com/rust-analyzer/rust-analyzer/issues/10002
if stdx::never!(!text[usize::from(position.offset)..].starts_with(char_typed)) {
return Ok(None);
}