mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Merge #3179
3179: Introduce AsMacroCall trait r=matklad a=edwin0cheng This PR introduce `AsMacroCall` trait to help convert `ast::MacroCall` to `MacroCallId`. The main goal here is to centralize various conversions to single place and make implementing eager macro calls without further ado. ```rust pub trait AsMacroCall { fn as_call_id( &self, db: &(impl db::DefDatabase + AstDatabase), resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, ) -> Option<MacroCallId>; } ``` Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
daffdd8674
4 changed files with 127 additions and 77 deletions
|
@ -15,11 +15,9 @@ use hir_def::{
|
||||||
},
|
},
|
||||||
expr::{ExprId, PatId},
|
expr::{ExprId, PatId},
|
||||||
resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs},
|
resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs},
|
||||||
DefWithBodyId, TraitId,
|
AsMacroCall, DefWithBodyId, TraitId,
|
||||||
};
|
|
||||||
use hir_expand::{
|
|
||||||
hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind,
|
|
||||||
};
|
};
|
||||||
|
use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId};
|
||||||
use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment};
|
use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
|
@ -363,12 +361,10 @@ impl SourceAnalyzer {
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
macro_call: InFile<&ast::MacroCall>,
|
macro_call: InFile<&ast::MacroCall>,
|
||||||
) -> Option<Expansion> {
|
) -> Option<Expansion> {
|
||||||
let def = self.resolve_macro_call(db, macro_call)?.id;
|
let macro_call_id = macro_call.as_call_id(db, |path| {
|
||||||
let ast_id = AstId::new(
|
self.resolver.resolve_path_as_macro(db, &path).map(|it| it.into())
|
||||||
macro_call.file_id,
|
})?;
|
||||||
db.ast_id_map(macro_call.file_id).ast_id(macro_call.value),
|
Some(Expansion { macro_call_id })
|
||||||
);
|
|
||||||
Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,7 @@ use std::{mem, ops::Index, sync::Arc};
|
||||||
|
|
||||||
use drop_bomb::DropBomb;
|
use drop_bomb::DropBomb;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{
|
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
|
||||||
ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId,
|
|
||||||
};
|
|
||||||
use ra_arena::{map::ArenaMap, Arena};
|
use ra_arena::{map::ArenaMap, Arena};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::{ast, AstNode, AstPtr};
|
use ra_syntax::{ast, AstNode, AstPtr};
|
||||||
|
@ -23,7 +21,7 @@ use crate::{
|
||||||
nameres::CrateDefMap,
|
nameres::CrateDefMap,
|
||||||
path::{ModPath, Path},
|
path::{ModPath, Path},
|
||||||
src::HasSource,
|
src::HasSource,
|
||||||
DefWithBodyId, HasModule, Lookup, ModuleId,
|
AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) struct Expander {
|
pub(crate) struct Expander {
|
||||||
|
@ -51,14 +49,11 @@ impl Expander {
|
||||||
db: &DB,
|
db: &DB,
|
||||||
macro_call: ast::MacroCall,
|
macro_call: ast::MacroCall,
|
||||||
) -> Option<(Mark, T)> {
|
) -> Option<(Mark, T)> {
|
||||||
let ast_id = AstId::new(
|
let macro_call = InFile::new(self.current_file_id, ¯o_call);
|
||||||
self.current_file_id,
|
|
||||||
db.ast_id_map(self.current_file_id).ast_id(¯o_call),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(path) = macro_call.path().and_then(|path| self.parse_mod_path(path)) {
|
if let Some(call_id) =
|
||||||
if let Some(def) = self.resolve_path_as_macro(db, &path) {
|
macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path))
|
||||||
let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id));
|
{
|
||||||
let file_id = call_id.as_file();
|
let file_id = call_id.as_file();
|
||||||
if let Some(node) = db.parse_or_expand(file_id) {
|
if let Some(node) = db.parse_or_expand(file_id) {
|
||||||
if let Some(expr) = T::cast(node) {
|
if let Some(expr) = T::cast(node) {
|
||||||
|
@ -77,7 +72,6 @@ impl Expander {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Instead of just dropping the error from expansion
|
// FIXME: Instead of just dropping the error from expansion
|
||||||
// report it
|
// report it
|
||||||
|
@ -99,10 +93,6 @@ impl Expander {
|
||||||
Path::from_src(path, &self.hygiene)
|
Path::from_src(path, &self.hygiene)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_mod_path(&mut self, path: ast::Path) -> Option<ModPath> {
|
|
||||||
ModPath::from_src(path, &self.hygiene)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> {
|
fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> {
|
||||||
self.crate_def_map
|
self.crate_def_map
|
||||||
.resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
|
.resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
|
||||||
|
|
|
@ -46,7 +46,10 @@ mod marks;
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use hir_expand::{ast_id_map::FileAstId, AstId, HirFileId, InFile, MacroDefId};
|
use hir_expand::{
|
||||||
|
ast_id_map::FileAstId, db::AstDatabase, hygiene::Hygiene, AstId, HirFileId, InFile,
|
||||||
|
MacroCallId, MacroCallKind, MacroDefId,
|
||||||
|
};
|
||||||
use ra_arena::{impl_arena_id, RawId};
|
use ra_arena::{impl_arena_id, RawId};
|
||||||
use ra_db::{impl_intern_key, salsa, CrateId};
|
use ra_db::{impl_intern_key, salsa, CrateId};
|
||||||
use ra_syntax::{ast, AstNode};
|
use ra_syntax::{ast, AstNode};
|
||||||
|
@ -413,3 +416,61 @@ impl HasModule for StaticLoc {
|
||||||
self.container.module(db)
|
self.container.module(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A helper trait for converting to MacroCallId
|
||||||
|
pub trait AsMacroCall {
|
||||||
|
fn as_call_id(
|
||||||
|
&self,
|
||||||
|
db: &(impl db::DefDatabase + AstDatabase),
|
||||||
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
|
) -> Option<MacroCallId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||||
|
fn as_call_id(
|
||||||
|
&self,
|
||||||
|
db: &(impl db::DefDatabase + AstDatabase),
|
||||||
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
|
) -> Option<MacroCallId> {
|
||||||
|
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
||||||
|
let h = Hygiene::new(db, self.file_id);
|
||||||
|
let path = path::ModPath::from_src(self.value.path()?, &h)?;
|
||||||
|
|
||||||
|
AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper wrapper for `AstId` with `ModPath`
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct AstIdWithPath<T: ast::AstNode> {
|
||||||
|
pub ast_id: AstId<T>,
|
||||||
|
pub path: path::ModPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ast::AstNode> AstIdWithPath<T> {
|
||||||
|
pub fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: path::ModPath) -> AstIdWithPath<T> {
|
||||||
|
AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
|
||||||
|
fn as_call_id(
|
||||||
|
&self,
|
||||||
|
db: &impl AstDatabase,
|
||||||
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
|
) -> Option<MacroCallId> {
|
||||||
|
let def = resolver(self.path.clone())?;
|
||||||
|
Some(def.as_call_id(db, MacroCallKind::FnLike(self.ast_id.clone())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
|
||||||
|
fn as_call_id(
|
||||||
|
&self,
|
||||||
|
db: &impl AstDatabase,
|
||||||
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
|
) -> Option<MacroCallId> {
|
||||||
|
let def = resolver(self.path.clone())?;
|
||||||
|
Some(def.as_call_id(db, MacroCallKind::Attr(self.ast_id.clone())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use hir_expand::{
|
||||||
builtin_derive::find_builtin_derive,
|
builtin_derive::find_builtin_derive,
|
||||||
builtin_macro::find_builtin_macro,
|
builtin_macro::find_builtin_macro,
|
||||||
name::{name, AsName, Name},
|
name::{name, AsName, Name},
|
||||||
HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
|
HirFileId, MacroCallId, MacroDefId, MacroDefKind,
|
||||||
};
|
};
|
||||||
use ra_cfg::CfgOptions;
|
use ra_cfg::CfgOptions;
|
||||||
use ra_db::{CrateId, FileId};
|
use ra_db::{CrateId, FileId};
|
||||||
|
@ -25,8 +25,9 @@ use crate::{
|
||||||
path::{ImportAlias, ModPath, PathKind},
|
path::{ImportAlias, ModPath, PathKind},
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
visibility::Visibility,
|
visibility::Visibility,
|
||||||
AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
|
AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
|
||||||
LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
|
FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
|
||||||
|
TraitLoc, TypeAliasLoc, UnionLoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap {
|
pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap {
|
||||||
|
@ -99,11 +100,16 @@ struct ImportDirective {
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
struct MacroDirective {
|
struct MacroDirective {
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
ast_id: AstId<ast::MacroCall>,
|
ast_id: AstIdWithPath<ast::MacroCall>,
|
||||||
path: ModPath,
|
|
||||||
legacy: Option<MacroCallId>,
|
legacy: Option<MacroCallId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct DeriveDirective {
|
||||||
|
module_id: LocalModuleId,
|
||||||
|
ast_id: AstIdWithPath<ast::ModuleItem>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Walks the tree of module recursively
|
/// Walks the tree of module recursively
|
||||||
struct DefCollector<'a, DB> {
|
struct DefCollector<'a, DB> {
|
||||||
db: &'a DB,
|
db: &'a DB,
|
||||||
|
@ -112,7 +118,7 @@ struct DefCollector<'a, DB> {
|
||||||
unresolved_imports: Vec<ImportDirective>,
|
unresolved_imports: Vec<ImportDirective>,
|
||||||
resolved_imports: Vec<ImportDirective>,
|
resolved_imports: Vec<ImportDirective>,
|
||||||
unexpanded_macros: Vec<MacroDirective>,
|
unexpanded_macros: Vec<MacroDirective>,
|
||||||
unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, ModPath)>,
|
unexpanded_attribute_macros: Vec<DeriveDirective>,
|
||||||
mod_dirs: FxHashMap<LocalModuleId, ModDir>,
|
mod_dirs: FxHashMap<LocalModuleId, ModDir>,
|
||||||
cfg_options: &'a CfgOptions,
|
cfg_options: &'a CfgOptions,
|
||||||
}
|
}
|
||||||
|
@ -515,16 +521,16 @@ where
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| {
|
||||||
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
||||||
self.db,
|
self.db,
|
||||||
ResolveMode::Other,
|
ResolveMode::Other,
|
||||||
directive.module_id,
|
directive.module_id,
|
||||||
&directive.path,
|
&path,
|
||||||
BuiltinShadowMode::Module,
|
BuiltinShadowMode::Module,
|
||||||
);
|
);
|
||||||
|
resolved_res.resolved_def.take_macros()
|
||||||
if let Some(def) = resolved_res.resolved_def.take_macros() {
|
}) {
|
||||||
let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(directive.ast_id));
|
|
||||||
resolved.push((directive.module_id, call_id));
|
resolved.push((directive.module_id, call_id));
|
||||||
res = ReachedFixedPoint::No;
|
res = ReachedFixedPoint::No;
|
||||||
return false;
|
return false;
|
||||||
|
@ -532,12 +538,11 @@ where
|
||||||
|
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
attribute_macros.retain(|(module_id, ast_id, path)| {
|
attribute_macros.retain(|directive| {
|
||||||
let resolved_res = self.resolve_attribute_macro(path);
|
if let Some(call_id) =
|
||||||
|
directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path))
|
||||||
if let Some(def) = resolved_res {
|
{
|
||||||
let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id));
|
resolved.push((directive.module_id, call_id));
|
||||||
resolved.push((*module_id, call_id));
|
|
||||||
res = ReachedFixedPoint::No;
|
res = ReachedFixedPoint::No;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -833,20 +838,22 @@ where
|
||||||
};
|
};
|
||||||
let path = ModPath::from_tt_ident(ident);
|
let path = ModPath::from_tt_ident(ident);
|
||||||
|
|
||||||
let ast_id = AstId::new(self.file_id, def.kind.ast_id());
|
let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path);
|
||||||
self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path));
|
self.def_collector
|
||||||
|
.unexpanded_attribute_macros
|
||||||
|
.push(DeriveDirective { module_id: self.module_id, ast_id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_macro(&mut self, mac: &raw::MacroData) {
|
fn collect_macro(&mut self, mac: &raw::MacroData) {
|
||||||
let ast_id = AstId::new(self.file_id, mac.ast_id);
|
let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
|
||||||
|
|
||||||
// Case 0: builtin macros
|
// Case 0: builtin macros
|
||||||
if mac.builtin {
|
if mac.builtin {
|
||||||
if let Some(name) = &mac.name {
|
if let Some(name) = &mac.name {
|
||||||
let krate = self.def_collector.def_map.krate;
|
let krate = self.def_collector.def_map.krate;
|
||||||
if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) {
|
if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
|
||||||
self.def_collector.define_macro(
|
self.def_collector.define_macro(
|
||||||
self.module_id,
|
self.module_id,
|
||||||
name.clone(),
|
name.clone(),
|
||||||
|
@ -862,7 +869,7 @@ where
|
||||||
if is_macro_rules(&mac.path) {
|
if is_macro_rules(&mac.path) {
|
||||||
if let Some(name) = &mac.name {
|
if let Some(name) = &mac.name {
|
||||||
let macro_id = MacroDefId {
|
let macro_id = MacroDefId {
|
||||||
ast_id: Some(ast_id),
|
ast_id: Some(ast_id.ast_id),
|
||||||
krate: Some(self.def_collector.def_map.krate),
|
krate: Some(self.def_collector.def_map.krate),
|
||||||
kind: MacroDefKind::Declarative,
|
kind: MacroDefKind::Declarative,
|
||||||
};
|
};
|
||||||
|
@ -872,15 +879,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: try to resolve in legacy scope and expand macro_rules
|
// Case 2: try to resolve in legacy scope and expand macro_rules
|
||||||
if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
|
if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| {
|
||||||
|
path.as_ident().and_then(|name| {
|
||||||
self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
|
self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
let macro_call_id =
|
|
||||||
macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id));
|
|
||||||
|
|
||||||
self.def_collector.unexpanded_macros.push(MacroDirective {
|
self.def_collector.unexpanded_macros.push(MacroDirective {
|
||||||
module_id: self.module_id,
|
module_id: self.module_id,
|
||||||
path: mac.path.clone(),
|
|
||||||
ast_id,
|
ast_id,
|
||||||
legacy: Some(macro_call_id),
|
legacy: Some(macro_call_id),
|
||||||
});
|
});
|
||||||
|
@ -890,14 +895,12 @@ where
|
||||||
|
|
||||||
// Case 3: resolve in module scope, expand during name resolution.
|
// Case 3: resolve in module scope, expand during name resolution.
|
||||||
// We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only.
|
// We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only.
|
||||||
let mut path = mac.path.clone();
|
if ast_id.path.is_ident() {
|
||||||
if path.is_ident() {
|
ast_id.path.kind = PathKind::Super(0);
|
||||||
path.kind = PathKind::Super(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.def_collector.unexpanded_macros.push(MacroDirective {
|
self.def_collector.unexpanded_macros.push(MacroDirective {
|
||||||
module_id: self.module_id,
|
module_id: self.module_id,
|
||||||
path,
|
|
||||||
ast_id,
|
ast_id,
|
||||||
legacy: None,
|
legacy: None,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue