diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 4da56529de..c0e0436c0a 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -8,47 +8,47 @@ use crate::{ use crate::quote; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum BuiltinExpander { - Column, - File, - Line, - Stringify, -} - -impl BuiltinExpander { - pub fn expand( - &self, - db: &dyn AstDatabase, - id: MacroCallId, - tt: &tt::Subtree, - ) -> Result { - match self { - BuiltinExpander::Column => column_expand(db, id, tt), - BuiltinExpander::File => file_expand(db, id, tt), - BuiltinExpander::Line => line_expand(db, id, tt), - BuiltinExpander::Stringify => stringify_expand(db, id, tt), +macro_rules! register_builtin { + ( $(($name:ident, $kind: ident) => $expand:ident),* ) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub enum BuiltinFnLikeExpander { + $($kind),* } - } + + impl BuiltinFnLikeExpander { + pub fn expand( + &self, + db: &dyn AstDatabase, + id: MacroCallId, + tt: &tt::Subtree, + ) -> Result { + let expander = match *self { + $( BuiltinFnLikeExpander::$kind => $expand, )* + }; + expander(db, id, tt) + } + } + + pub fn find_builtin_macro( + ident: &name::Name, + krate: CrateId, + ast_id: AstId, + ) -> Option { + let kind = match ident { + $( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )* + _ => return None, + }; + + Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) }) + } + }; } -pub fn find_builtin_macro( - ident: &name::Name, - krate: CrateId, - ast_id: AstId, -) -> Option { - // FIXME: Better registering method - if ident == &name::COLUMN_MACRO { - Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Column) }) - } else if ident == &name::FILE_MACRO { - Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::File) }) - } else if ident == &name::LINE_MACRO { - Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Line) }) - } else if ident == &name::STRINGIFY_MACRO { - Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Stringify) }) - } else { - None - } +register_builtin! { + (COLUMN_MACRO, Column) => column_expand, + (FILE_MACRO, File) => file_expand, + (LINE_MACRO, Line) => line_expand, + (STRINGIFY_MACRO, Stringify) => stringify_expand } fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { @@ -171,3 +171,92 @@ fn file_expand( Ok(expanded) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{test_db::TestDB, MacroCallLoc}; + use ra_db::{fixture::WithFixture, SourceDatabase}; + + fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String { + let (db, file_id) = TestDB::with_single_file(&s); + let parsed = db.parse(file_id); + let macro_calls: Vec<_> = + parsed.syntax_node().descendants().filter_map(|it| ast::MacroCall::cast(it)).collect(); + + let ast_id_map = db.ast_id_map(file_id.into()); + + // the first one should be a macro_rules + let def = MacroDefId { + krate: CrateId(0), + ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0])), + kind: MacroDefKind::BuiltIn(expander), + }; + + let loc = MacroCallLoc { + def, + ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[1])), + }; + + let id = db.intern_macro(loc); + let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap(); + + parsed.text().to_string() + } + + #[test] + fn test_column_expand() { + let expanded = expand_builtin_macro( + r#" + #[rustc_builtin_macro] + macro_rules! column {() => {}} + column!() +"#, + BuiltinFnLikeExpander::Column, + ); + + assert_eq!(expanded, "9"); + } + + #[test] + fn test_line_expand() { + let expanded = expand_builtin_macro( + r#" + #[rustc_builtin_macro] + macro_rules! line {() => {}} + line!() +"#, + BuiltinFnLikeExpander::Line, + ); + + assert_eq!(expanded, "4"); + } + + #[test] + fn test_stringify_expand() { + let expanded = expand_builtin_macro( + r#" + #[rustc_builtin_macro] + macro_rules! stringify {() => {}} + stringify!(a b c) +"#, + BuiltinFnLikeExpander::Stringify, + ); + + assert_eq!(expanded, "\"a b c\""); + } + + #[test] + fn test_file_expand() { + let expanded = expand_builtin_macro( + r#" + #[rustc_builtin_macro] + macro_rules! file {() => {}} + file!() +"#, + BuiltinFnLikeExpander::File, + ); + + assert_eq!(expanded, "\"\""); + } +} diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index e1d93a8ef5..8e46fa177d 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs @@ -9,14 +9,14 @@ use ra_prof::profile; use ra_syntax::{AstNode, Parse, SyntaxNode}; use crate::{ - ast_id_map::AstIdMap, BuiltinExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, - MacroDefId, MacroDefKind, MacroFile, MacroFileKind, + ast_id_map::AstIdMap, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, + MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, MacroFileKind, }; #[derive(Debug, Clone, Eq, PartialEq)] pub enum TokenExpander { MacroRules(mbe::MacroRules), - Builtin(BuiltinExpander), + Builtin(BuiltinFnLikeExpander), } impl TokenExpander { diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 126d12fbbe..4f3ccf1d0d 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -24,7 +24,10 @@ use ra_syntax::{ }; use crate::ast_id_map::FileAstId; -use crate::builtin_macro::BuiltinExpander; +use crate::builtin_macro::BuiltinFnLikeExpander; + +#[cfg(test)] +mod test_db; /// 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 @@ -135,7 +138,7 @@ pub struct MacroDefId { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MacroDefKind { Declarative, - BuiltIn(BuiltinExpander), + BuiltIn(BuiltinFnLikeExpander), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs new file mode 100644 index 0000000000..d23e75d9ee --- /dev/null +++ b/crates/ra_hir_expand/src/test_db.rs @@ -0,0 +1,50 @@ +//! Database used for testing `hir_expand`. + +use std::{ + panic, + sync::{Arc, Mutex}, +}; + +use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; + +#[salsa::database( + ra_db::SourceDatabaseExtStorage, + ra_db::SourceDatabaseStorage, + crate::db::AstDatabaseStorage +)] +#[derive(Debug, Default)] +pub struct TestDB { + runtime: salsa::Runtime, + events: Mutex>>>, +} + +impl salsa::Database for TestDB { + fn salsa_runtime(&self) -> &salsa::Runtime { + &self.runtime + } + + fn salsa_event(&self, event: impl Fn() -> salsa::Event) { + let mut events = self.events.lock().unwrap(); + if let Some(events) = &mut *events { + events.push(event()); + } + } +} + +impl panic::RefUnwindSafe for TestDB {} + +impl FileLoader for TestDB { + fn file_text(&self, file_id: FileId) -> Arc { + FileLoaderDelegate(self).file_text(file_id) + } + fn resolve_relative_path( + &self, + anchor: FileId, + relative_path: &RelativePath, + ) -> Option { + FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) + } + fn relevant_crates(&self, file_id: FileId) -> Arc> { + FileLoaderDelegate(self).relevant_crates(file_id) + } +}