use std::{ hash::{Hash, Hasher}, sync::Arc, }; use ra_db::{LocationInterner, FileId}; use ra_syntax::{TreeArc, SourceFile, AstNode, ast}; use ra_arena::{RawId, ArenaId, impl_arena_id}; use mbe::MacroRules; use crate::{ Module, DefDatabase, AstId, FileAstId, }; #[derive(Debug, Default)] pub struct HirInterner { macros: LocationInterner, fns: LocationInterner, FunctionId>, structs: LocationInterner, StructId>, enums: LocationInterner, EnumId>, consts: LocationInterner, ConstId>, statics: LocationInterner, StaticId>, traits: LocationInterner, TraitId>, types: LocationInterner, TypeAliasId>, } impl HirInterner { pub fn len(&self) -> usize { self.macros.len() + self.fns.len() + self.structs.len() + self.enums.len() + self.consts.len() + self.statics.len() + self.traits.len() + self.types.len() } } /// hir makes heavy use of ids: integer (u32) handlers to various things. You /// can think of id as a pointer (but without a lifetime) or a file descriptor /// (but for hir objects). /// /// This module defines a bunch of ids we are using. The most important ones are /// probably `HirFileId` and `DefId`. /// Input to the analyzer is a set of files, where each file is identified by /// `FileId` and contains source code. However, another source of source code in /// Rust are macros: each macro can be thought of as producing a "temporary /// file". To assign an id to such a file, we use the id of the macro call that /// produced the file. So, a `HirFileId` is either a `FileId` (source code /// written by user), or a `MacroCallId` (source code produced by macro). /// /// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file /// containing the call plus the offset of the macro call in the file. Note that /// this is a recursive definition! However, the size_of of `HirFileId` is /// finite (because everything bottoms out at the real `FileId`) and small /// (`MacroCallId` uses the location interner). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct HirFileId(HirFileIdRepr); impl HirFileId { /// For macro-expansion files, returns the file original source file the /// expansion originated from. pub fn original_file(self, db: &impl DefDatabase) -> FileId { match self.0 { HirFileIdRepr::File(file_id) => file_id, HirFileIdRepr::Macro(macro_call_id) => { let loc = macro_call_id.loc(db); loc.ast_id.file_id().original_file(db) } } } /// XXX: this is a temporary function, which should go away when we implement the /// nameresolution+macro expansion combo. Prefer using `original_file` if /// possible. pub fn as_original_file(self) -> FileId { match self.0 { HirFileIdRepr::File(file_id) => file_id, HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self), } } pub(crate) fn hir_parse_query( db: &impl DefDatabase, file_id: HirFileId, ) -> TreeArc { match file_id.0 { HirFileIdRepr::File(file_id) => db.parse(file_id), HirFileIdRepr::Macro(macro_call_id) => { // returning an empty string looks fishy... parse_macro(db, macro_call_id).unwrap_or_else(|| SourceFile::parse("")) } } } } fn parse_macro(db: &impl DefDatabase, macro_call_id: MacroCallId) -> Option> { let loc = macro_call_id.loc(db); let macro_call = loc.ast_id.to_node(db); let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?; let macro_rules = db.macro_def(loc.def)?; let tt = macro_rules.expand(¯o_arg).ok()?; Some(mbe::token_tree_to_ast_item_list(&tt)) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum HirFileIdRepr { File(FileId), Macro(MacroCallId), } impl From for HirFileId { fn from(file_id: FileId) -> HirFileId { HirFileId(HirFileIdRepr::File(file_id)) } } impl From for HirFileId { fn from(macro_call_id: MacroCallId) -> HirFileId { HirFileId(HirFileIdRepr::Macro(macro_call_id)) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroDefId(pub(crate) AstId); pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option> { let macro_call = id.0.to_node(db); let arg = macro_call.token_tree()?; let (tt, _) = mbe::ast_to_token_tree(arg)?; let rules = MacroRules::parse(&tt).ok()?; Some(Arc::new(rules)) } /// `MacroCallId` identifies a particular macro invocation, like /// `println!("Hello, {}", world)`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroCallId(RawId); impl_arena_id!(MacroCallId); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct MacroCallLoc { pub(crate) def: MacroDefId, pub(crate) ast_id: AstId, } impl MacroCallId { pub(crate) fn loc(self, db: &impl AsRef) -> MacroCallLoc { db.as_ref().macros.id2loc(self) } } impl MacroCallLoc { pub(crate) fn id(&self, db: &impl AsRef) -> MacroCallId { db.as_ref().macros.loc2id(&self) } } #[derive(Debug)] pub struct ItemLoc { pub(crate) module: Module, ast_id: AstId, } impl PartialEq for ItemLoc { fn eq(&self, other: &Self) -> bool { self.module == other.module && self.ast_id == other.ast_id } } impl Eq for ItemLoc {} impl Hash for ItemLoc { fn hash(&self, hasher: &mut H) { self.module.hash(hasher); self.ast_id.hash(hasher); } } impl Clone for ItemLoc { fn clone(&self) -> ItemLoc { ItemLoc { module: self.module, ast_id: self.ast_id } } } #[derive(Clone, Copy)] pub(crate) struct LocationCtx { db: DB, module: Module, file_id: HirFileId, } impl<'a, DB: DefDatabase> LocationCtx<&'a DB> { pub(crate) fn new(db: &'a DB, module: Module, file_id: HirFileId) -> LocationCtx<&'a DB> { LocationCtx { db, module, file_id } } pub(crate) fn to_def(self, ast: &N) -> DEF where N: AstNode, DEF: AstItemDef, { DEF::from_ast(self, ast) } } pub(crate) trait AstItemDef: ArenaId + Clone { fn interner(interner: &HirInterner) -> &LocationInterner, Self>; fn from_ast(ctx: LocationCtx<&impl DefDatabase>, ast: &N) -> Self { let items = ctx.db.ast_id_map(ctx.file_id); let item_id = items.ast_id(ast); Self::from_ast_id(ctx, item_id) } fn from_ast_id(ctx: LocationCtx<&impl DefDatabase>, ast_id: FileAstId) -> Self { let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) }; Self::interner(ctx.db.as_ref()).loc2id(&loc) } fn source(self, db: &impl DefDatabase) -> (HirFileId, TreeArc) { let int = Self::interner(db.as_ref()); let loc = int.id2loc(self); let ast = loc.ast_id.to_node(db); (loc.ast_id.file_id(), ast) } fn module(self, db: &impl DefDatabase) -> Module { let int = Self::interner(db.as_ref()); let loc = int.id2loc(self); loc.module } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct FunctionId(RawId); impl_arena_id!(FunctionId); impl AstItemDef for FunctionId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { &interner.fns } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct StructId(RawId); impl_arena_id!(StructId); impl AstItemDef for StructId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { &interner.structs } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct EnumId(RawId); impl_arena_id!(EnumId); impl AstItemDef for EnumId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { &interner.enums } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct ConstId(RawId); impl_arena_id!(ConstId); impl AstItemDef for ConstId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { &interner.consts } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct StaticId(RawId); impl_arena_id!(StaticId); impl AstItemDef for StaticId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { &interner.statics } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct TraitId(RawId); impl_arena_id!(TraitId); impl AstItemDef for TraitId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { &interner.traits } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct TypeAliasId(RawId); impl_arena_id!(TypeAliasId); impl AstItemDef for TypeAliasId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { &interner.types } }