2 use crate::db::AstDatabase;
5 name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
11 macro_rules! register_builtin {
12 ( $(($name:ident, $kind: ident) => $expand:ident),* ) => {
13 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14 pub enum BuiltinFnLikeExpander {
18 impl BuiltinFnLikeExpander {
24 ) -> Result<tt::Subtree, mbe::ExpandError> {
25 let expander = match *self {
26 $( BuiltinFnLikeExpander::$kind => $expand, )*
32 pub fn find_builtin_macro(
35 ast_id: AstId<ast::MacroCall>,
36 ) -> Option<MacroDefId> {
37 let kind = match ident {
38 $( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )*
42 Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) })
48 (COLUMN_MACRO, Column) => column_expand,
49 (FILE_MACRO, File) => file_expand,
50 (LINE_MACRO, Line) => line_expand,
51 (STRINGIFY_MACRO, Stringify) => stringify_expand
54 fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
55 // FIXME: Use expansion info
56 let file_id = file.original_file(db);
57 let text = db.file_text(file_id);
61 for (i, c) in text.chars().enumerate() {
62 if i == pos.to_usize() {
77 ) -> Result<tt::Subtree, mbe::ExpandError> {
78 let loc = db.lookup_intern_macro(id);
79 let macro_call = loc.ast_id.to_node(db);
81 let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
82 let arg_start = arg.syntax().text_range().start();
84 let file = id.as_file(MacroFileKind::Expr);
85 let line_num = to_line_number(db, file, arg_start);
87 let expanded = quote! {
98 ) -> Result<tt::Subtree, mbe::ExpandError> {
99 let loc = db.lookup_intern_macro(id);
100 let macro_call = loc.ast_id.to_node(db);
102 let macro_content = {
103 let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
104 let macro_args = arg.syntax().clone();
105 let text = macro_args.text();
106 let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')');
107 text.slice(without_parens).to_string()
110 let expanded = quote! {
117 fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
118 // FIXME: Use expansion info
119 let file_id = file.original_file(db);
120 let text = db.file_text(file_id);
123 for c in text[..pos.to_usize()].chars().rev() {
127 col_num = col_num + 1;
134 db: &dyn AstDatabase,
137 ) -> Result<tt::Subtree, mbe::ExpandError> {
138 let loc = db.lookup_intern_macro(id);
139 let macro_call = loc.ast_id.to_node(db);
141 let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
142 let col_start = macro_call.syntax().text_range().start();
144 let file = id.as_file(MacroFileKind::Expr);
145 let col_num = to_col_number(db, file, col_start);
147 let expanded = quote! {
155 db: &dyn AstDatabase,
158 ) -> Result<tt::Subtree, mbe::ExpandError> {
159 let loc = db.lookup_intern_macro(id);
160 let macro_call = loc.ast_id.to_node(db);
162 let _ = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
164 // FIXME: RA purposefully lacks knowledge of absolute file names
165 // so just return "".
168 let expanded = quote! {
178 use crate::{test_db::TestDB, MacroCallLoc};
179 use ra_db::{fixture::WithFixture, SourceDatabase};
181 fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String {
182 let (db, file_id) = TestDB::with_single_file(&s);
183 let parsed = db.parse(file_id);
184 let macro_calls: Vec<_> =
185 parsed.syntax_node().descendants().filter_map(|it| ast::MacroCall::cast(it)).collect();
187 let ast_id_map = db.ast_id_map(file_id.into());
189 // the first one should be a macro_rules
190 let def = MacroDefId {
192 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0])),
193 kind: MacroDefKind::BuiltIn(expander),
196 let loc = MacroCallLoc {
198 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[1])),
201 let id = db.intern_macro(loc);
202 let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap();
204 parsed.text().to_string()
208 fn test_column_expand() {
209 let expanded = expand_builtin_macro(
211 #[rustc_builtin_macro]
212 macro_rules! column {() => {}}
215 BuiltinFnLikeExpander::Column,
218 assert_eq!(expanded, "9");
222 fn test_line_expand() {
223 let expanded = expand_builtin_macro(
225 #[rustc_builtin_macro]
226 macro_rules! line {() => {}}
229 BuiltinFnLikeExpander::Line,
232 assert_eq!(expanded, "4");
236 fn test_stringify_expand() {
237 let expanded = expand_builtin_macro(
239 #[rustc_builtin_macro]
240 macro_rules! stringify {() => {}}
243 BuiltinFnLikeExpander::Stringify,
246 assert_eq!(expanded, "\"a b c\"");
250 fn test_file_expand() {
251 let expanded = expand_builtin_macro(
253 #[rustc_builtin_macro]
254 macro_rules! file {() => {}}
257 BuiltinFnLikeExpander::File,
260 assert_eq!(expanded, "\"\"");