mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 21:35:20 +00:00
Merge #8093
8093: Record custom derive helpers in `DefMap` r=jonas-schievink a=jonas-schievink Also clean up proc macro attribute parsing a bit bors r+ Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
commit
017dd0b45a
4 changed files with 117 additions and 25 deletions
|
@ -53,11 +53,12 @@ mod path_resolution;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
mod proc_macro;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::{CrateId, Edition, FileId};
|
use base_db::{CrateId, Edition, FileId};
|
||||||
use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile};
|
use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile, MacroDefId};
|
||||||
use la_arena::Arena;
|
use la_arena::Arena;
|
||||||
use profile::Count;
|
use profile::Count;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
@ -73,6 +74,8 @@ use crate::{
|
||||||
AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId,
|
AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use self::proc_macro::ProcMacroDef;
|
||||||
|
|
||||||
/// Contains the results of (early) name resolution.
|
/// Contains the results of (early) name resolution.
|
||||||
///
|
///
|
||||||
/// A `DefMap` stores the module tree and the definitions that are in scope in every module after
|
/// A `DefMap` stores the module tree and the definitions that are in scope in every module after
|
||||||
|
@ -95,6 +98,12 @@ pub struct DefMap {
|
||||||
prelude: Option<ModuleId>,
|
prelude: Option<ModuleId>,
|
||||||
extern_prelude: FxHashMap<Name, ModuleDefId>,
|
extern_prelude: FxHashMap<Name, ModuleDefId>,
|
||||||
|
|
||||||
|
/// Side table with additional proc. macro info, for use by name resolution in downstream
|
||||||
|
/// crates.
|
||||||
|
///
|
||||||
|
/// (the primary purpose is to resolve derive helpers)
|
||||||
|
exported_proc_macros: FxHashMap<MacroDefId, ProcMacroDef>,
|
||||||
|
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
diagnostics: Vec<DefDiagnostic>,
|
diagnostics: Vec<DefDiagnostic>,
|
||||||
}
|
}
|
||||||
|
@ -237,6 +246,7 @@ impl DefMap {
|
||||||
krate,
|
krate,
|
||||||
edition,
|
edition,
|
||||||
extern_prelude: FxHashMap::default(),
|
extern_prelude: FxHashMap::default(),
|
||||||
|
exported_proc_macros: FxHashMap::default(),
|
||||||
prelude: None,
|
prelude: None,
|
||||||
root,
|
root,
|
||||||
modules,
|
modules,
|
||||||
|
|
|
@ -18,7 +18,6 @@ use hir_expand::{
|
||||||
use hir_expand::{InFile, MacroCallLoc};
|
use hir_expand::{InFile, MacroCallLoc};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use tt::{Leaf, TokenTree};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::Attrs,
|
attr::Attrs,
|
||||||
|
@ -42,6 +41,8 @@ use crate::{
|
||||||
UnresolvedMacro,
|
UnresolvedMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::proc_macro::ProcMacroDef;
|
||||||
|
|
||||||
const GLOB_RECURSION_LIMIT: usize = 100;
|
const GLOB_RECURSION_LIMIT: usize = 100;
|
||||||
const EXPANSION_DEPTH_LIMIT: usize = 128;
|
const EXPANSION_DEPTH_LIMIT: usize = 128;
|
||||||
const FIXED_POINT_LIMIT: usize = 8192;
|
const FIXED_POINT_LIMIT: usize = 8192;
|
||||||
|
@ -353,9 +354,9 @@ impl DefCollector<'_> {
|
||||||
/// use a dummy expander that always errors. This comes with the drawback of macros potentially
|
/// use a dummy expander that always errors. This comes with the drawback of macros potentially
|
||||||
/// going out of sync with what the build system sees (since we resolve using VFS state, but
|
/// going out of sync with what the build system sees (since we resolve using VFS state, but
|
||||||
/// Cargo builds only on-disk files). We could and probably should add diagnostics for that.
|
/// Cargo builds only on-disk files). We could and probably should add diagnostics for that.
|
||||||
fn resolve_proc_macro(&mut self, name: &Name, ast_id: AstId<ast::Fn>) {
|
fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>) {
|
||||||
self.exports_proc_macros = true;
|
self.exports_proc_macros = true;
|
||||||
let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) {
|
let macro_def = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
|
||||||
Some((_, expander)) => MacroDefId {
|
Some((_, expander)) => MacroDefId {
|
||||||
krate: self.def_map.krate,
|
krate: self.def_map.krate,
|
||||||
kind: MacroDefKind::ProcMacro(*expander, ast_id),
|
kind: MacroDefKind::ProcMacro(*expander, ast_id),
|
||||||
|
@ -368,7 +369,8 @@ impl DefCollector<'_> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
self.define_proc_macro(name.clone(), macro_def);
|
self.define_proc_macro(def.name.clone(), macro_def);
|
||||||
|
self.def_map.exported_proc_macros.insert(macro_def, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a macro with `macro_rules`.
|
/// Define a macro with `macro_rules`.
|
||||||
|
@ -1386,26 +1388,9 @@ impl ModCollector<'_, '_> {
|
||||||
/// If `attrs` registers a procedural macro, collects its definition.
|
/// If `attrs` registers a procedural macro, collects its definition.
|
||||||
fn collect_proc_macro_def(&mut self, func_name: &Name, ast_id: AstId<ast::Fn>, attrs: &Attrs) {
|
fn collect_proc_macro_def(&mut self, func_name: &Name, ast_id: AstId<ast::Fn>, attrs: &Attrs) {
|
||||||
// FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
|
// FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
|
||||||
// FIXME: distinguish the type of macro
|
if let Some(proc_macro) = attrs.parse_proc_macro_decl(func_name) {
|
||||||
let macro_name = if attrs.by_key("proc_macro").exists()
|
self.def_collector.export_proc_macro(proc_macro, ast_id);
|
||||||
|| attrs.by_key("proc_macro_attribute").exists()
|
|
||||||
{
|
|
||||||
func_name.clone()
|
|
||||||
} else {
|
|
||||||
let derive = attrs.by_key("proc_macro_derive");
|
|
||||||
if let Some(arg) = derive.tt_values().next() {
|
|
||||||
if let [TokenTree::Leaf(Leaf::Ident(trait_name)), ..] = &*arg.token_trees {
|
|
||||||
trait_name.as_name()
|
|
||||||
} else {
|
|
||||||
log::trace!("malformed `#[proc_macro_derive]`: {}", arg);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.def_collector.resolve_proc_macro(¯o_name, ast_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) {
|
fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) {
|
||||||
|
|
71
crates/hir_def/src/nameres/proc_macro.rs
Normal file
71
crates/hir_def/src/nameres/proc_macro.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
//! Nameres-specific procedural macro data and helpers.
|
||||||
|
|
||||||
|
use hir_expand::name::{AsName, Name};
|
||||||
|
use tt::{Leaf, TokenTree};
|
||||||
|
|
||||||
|
use crate::attr::Attrs;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub(super) struct ProcMacroDef {
|
||||||
|
pub(super) name: Name,
|
||||||
|
pub(super) kind: ProcMacroKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub(super) enum ProcMacroKind {
|
||||||
|
CustomDerive { helpers: Box<[Name]> },
|
||||||
|
FnLike,
|
||||||
|
Attr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attrs {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub(super) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
|
||||||
|
if self.by_key("proc_macro").exists() {
|
||||||
|
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike })
|
||||||
|
} else if self.by_key("proc_macro_attribute").exists() {
|
||||||
|
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();
|
||||||
|
|
||||||
|
match &*derive.token_trees {
|
||||||
|
// `#[proc_macro_derive(Trait)]`
|
||||||
|
[TokenTree::Leaf(Leaf::Ident(trait_name))] => Some(ProcMacroDef {
|
||||||
|
name: trait_name.as_name(),
|
||||||
|
kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) },
|
||||||
|
}),
|
||||||
|
|
||||||
|
// `#[proc_macro_derive(Trait, attibutes(helper1, helper2, ...))]`
|
||||||
|
[
|
||||||
|
TokenTree::Leaf(Leaf::Ident(trait_name)),
|
||||||
|
TokenTree::Leaf(Leaf::Punct(comma)),
|
||||||
|
TokenTree::Leaf(Leaf::Ident(attributes)),
|
||||||
|
TokenTree::Subtree(helpers)
|
||||||
|
] if comma.char == ',' && attributes.text == "attributes" =>
|
||||||
|
{
|
||||||
|
let helpers = helpers.token_trees.iter()
|
||||||
|
.filter(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','))
|
||||||
|
.map(|tt| {
|
||||||
|
match tt {
|
||||||
|
TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Option<Box<[_]>>>()?;
|
||||||
|
|
||||||
|
Some(ProcMacroDef {
|
||||||
|
name: trait_name.as_name(),
|
||||||
|
kind: ProcMacroKind::CustomDerive { helpers },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
log::trace!("malformed `#[proc_macro_derive]`: {}", derive);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::nameres::proc_macro::{ProcMacroDef, ProcMacroKind};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn macro_rules_are_globally_visible() {
|
fn macro_rules_are_globally_visible() {
|
||||||
|
@ -790,3 +791,28 @@ fn proc_macro_censoring() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collects_derive_helpers() {
|
||||||
|
let def_map = compute_crate_def_map(
|
||||||
|
r"
|
||||||
|
struct TokenStream;
|
||||||
|
|
||||||
|
#[proc_macro_derive(AnotherTrait, attributes(helper_attr))]
|
||||||
|
pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
|
||||||
|
TokenStream
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(def_map.exported_proc_macros.len(), 1);
|
||||||
|
match def_map.exported_proc_macros.values().next() {
|
||||||
|
Some(ProcMacroDef { kind: ProcMacroKind::CustomDerive { helpers }, .. }) => {
|
||||||
|
match &**helpers {
|
||||||
|
[attr] => assert_eq!(attr.to_string(), "helper_attr"),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue