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,
|
2019-11-22 00:56:50 -06:00
|
|
|
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),
|
2019-11-22 00:56:50 -06:00
|
|
|
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) })
|
2019-11-22 00:56:50 -06:00
|
|
|
} 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)
|
|
|
|
}
|
2019-11-22 00:56:50 -06:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|