rust/crates/ra_hir_expand/src/builtin_macro.rs

132 lines
3.4 KiB
Rust
Raw Normal View History

2019-11-09 21:03:24 -06:00
//! Builtin macro
2019-11-11 00:15:09 -06:00
use crate::db::AstDatabase;
use crate::{
ast::{self, AstNode},
2019-11-11 04:45:55 -06:00
name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
2019-11-11 00:15:09 -06:00
TextUnit,
};
use crate::quote;
2019-11-09 21:03:24 -06:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinExpander {
2019-11-22 09:05:04 -06:00
File,
2019-11-11 00:15:09 -06:00
Line,
Stringify,
2019-11-09 21:03:24 -06:00
}
impl BuiltinExpander {
2019-11-11 00:15:09 -06:00
pub fn expand(
&self,
db: &dyn AstDatabase,
id: MacroCallId,
tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
match self {
2019-11-22 09:05:04 -06:00
BuiltinExpander::File => file_expand(db, id, tt),
2019-11-11 00:15:09 -06:00
BuiltinExpander::Line => line_expand(db, id, tt),
BuiltinExpander::Stringify => stringify_expand(db, id, tt),
2019-11-11 00:15:09 -06:00
}
2019-11-09 21:03:24 -06:00
}
}
pub fn find_builtin_macro(
ident: &name::Name,
krate: CrateId,
ast_id: AstId<ast::MacroCall>,
) -> Option<MacroDefId> {
// FIXME: Better registering method
2019-11-22 09:05:04 -06:00
if ident == &name::FILE_MACRO {
Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::File) })
} else if ident == &name::LINE_MACRO {
2019-11-11 04:45:55 -06:00
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) })
2019-11-09 21:03:24 -06:00
} else {
None
}
}
2019-11-11 00:15:09 -06:00
fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
// FIXME: Use expansion info
let file_id = file.original_file(db);
let text = db.file_text(file_id);
let mut line_num = 1;
// Count line end
for (i, c) in text.chars().enumerate() {
if i == pos.to_usize() {
break;
}
if c == '\n' {
line_num += 1;
}
}
line_num
}
fn line_expand(
db: &dyn AstDatabase,
id: MacroCallId,
_tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
let loc = db.lookup_intern_macro(id);
let macro_call = loc.ast_id.to_node(db);
let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
let arg_start = arg.syntax().text_range().start();
let file = id.as_file(MacroFileKind::Expr);
let line_num = to_line_number(db, file, arg_start);
let expanded = quote! {
#line_num
};
Ok(expanded)
}
fn stringify_expand(
db: &dyn AstDatabase,
id: MacroCallId,
_tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
let loc = db.lookup_intern_macro(id);
let macro_call = loc.ast_id.to_node(db);
let macro_content = {
let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
let macro_args = arg.syntax().clone();
let text = macro_args.text();
let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')');
text.slice(without_parens).to_string()
};
let expanded = quote! {
#macro_content
};
Ok(expanded)
}
2019-11-22 09:05:04 -06:00
fn file_expand(
db: &dyn AstDatabase,
id: MacroCallId,
_tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
let loc = db.lookup_intern_macro(id);
let macro_call = loc.ast_id.to_node(db);
let _ = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
// FIXME: RA purposefully lacks knowledge of absolute file names
// so just return "".
let file_name = "";
let expanded = quote! {
#file_name
};
Ok(expanded)
}