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:
bors[bot] 2020-02-17 08:38:09 +00:00 committed by GitHub
commit daffdd8674
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 127 additions and 77 deletions

View file

@ -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)) })
} }
} }

View file

@ -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, &macro_call);
self.current_file_id,
db.ast_id_map(self.current_file_id).ast_id(&macro_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)

View file

@ -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())))
}
}

View file

@ -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,
}); });