Skip to content

Commit ef2a9ae

Browse files
Merge #2205
2205: Implement bulitin line! macro r=matklad a=edwin0cheng This PR implements bulitin macro `line!` and add basic infra-structure for other bulitin macros: 1. Extend `MacroDefId` to support builtin macros 2. Add a `quote!` macro for simple quasi quoting. Note that for support others builtin macros, eager macro expansion have to be supported first, this PR not try to handle it. :) Co-authored-by: Edwin Cheng <[email protected]>
2 parents 5ac4ffb + 4f7df2a commit ef2a9ae

File tree

9 files changed

+458
-22
lines changed

9 files changed

+458
-22
lines changed

crates/ra_hir/src/ty/tests.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4810,3 +4810,22 @@ fn no_such_field_diagnostics() {
48104810
"###
48114811
);
48124812
}
4813+
4814+
#[test]
4815+
fn infer_builtin_macros_line() {
4816+
assert_snapshot!(
4817+
infer(r#"
4818+
#[rustc_builtin_macro]
4819+
macro_rules! line {() => {}}
4820+
4821+
fn main() {
4822+
let x = line!();
4823+
}
4824+
"#),
4825+
@r###"
4826+
![0; 1) '6': i32
4827+
[64; 88) '{ ...!(); }': ()
4828+
[74; 75) 'x': i32
4829+
"###
4830+
);
4831+
}

crates/ra_hir_def/src/nameres/collector.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! FIXME: write short doc here
22
33
use hir_expand::{
4+
builtin_macro::find_builtin_macro,
45
name::{self, AsName, Name},
5-
HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind,
6+
HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFileKind,
67
};
78
use ra_cfg::CfgOptions;
89
use ra_db::{CrateId, FileId};
@@ -692,10 +693,30 @@ where
692693
fn collect_macro(&mut self, mac: &raw::MacroData) {
693694
let ast_id = AstId::new(self.file_id, mac.ast_id);
694695

696+
// Case 0: builtin macros
697+
if mac.builtin {
698+
if let Some(name) = &mac.name {
699+
let krate = self.def_collector.def_map.krate;
700+
if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) {
701+
self.def_collector.define_macro(
702+
self.module_id,
703+
name.clone(),
704+
macro_id,
705+
mac.export,
706+
);
707+
return;
708+
}
709+
}
710+
}
711+
695712
// Case 1: macro rules, define a macro in crate-global mutable scope
696713
if is_macro_rules(&mac.path) {
697714
if let Some(name) = &mac.name {
698-
let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate };
715+
let macro_id = MacroDefId {
716+
ast_id,
717+
krate: self.def_collector.def_map.krate,
718+
kind: MacroDefKind::Declarative,
719+
};
699720
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
700721
}
701722
return;

crates/ra_hir_def/src/nameres/raw.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ pub(super) struct MacroData {
200200
pub(super) path: Path,
201201
pub(super) name: Option<Name>,
202202
pub(super) export: bool,
203+
pub(super) builtin: bool,
203204
}
204205

205206
struct RawItemsCollector {
@@ -367,7 +368,11 @@ impl RawItemsCollector {
367368
// FIXME: cfg_attr
368369
let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
369370

370-
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export });
371+
// FIXME: cfg_attr
372+
let builtin =
373+
m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "rustc_builtin_macro");
374+
375+
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export, builtin });
371376
self.push_item(current_module, attrs, RawItemKind::Macro(m));
372377
}
373378

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! Builtin macro
2+
use crate::db::AstDatabase;
3+
use crate::{
4+
ast::{self, AstNode},
5+
name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
6+
TextUnit,
7+
};
8+
9+
use crate::quote;
10+
11+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12+
pub enum BuiltinExpander {
13+
Line,
14+
}
15+
16+
impl BuiltinExpander {
17+
pub fn expand(
18+
&self,
19+
db: &dyn AstDatabase,
20+
id: MacroCallId,
21+
tt: &tt::Subtree,
22+
) -> Result<tt::Subtree, mbe::ExpandError> {
23+
match self {
24+
BuiltinExpander::Line => line_expand(db, id, tt),
25+
}
26+
}
27+
}
28+
29+
pub fn find_builtin_macro(
30+
ident: &name::Name,
31+
krate: CrateId,
32+
ast_id: AstId<ast::MacroCall>,
33+
) -> Option<MacroDefId> {
34+
// FIXME: Better registering method
35+
if ident == &name::LINE_MACRO {
36+
Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Line) })
37+
} else {
38+
None
39+
}
40+
}
41+
42+
fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
43+
// FIXME: Use expansion info
44+
let file_id = file.original_file(db);
45+
let text = db.file_text(file_id);
46+
let mut line_num = 1;
47+
48+
// Count line end
49+
for (i, c) in text.chars().enumerate() {
50+
if i == pos.to_usize() {
51+
break;
52+
}
53+
if c == '\n' {
54+
line_num += 1;
55+
}
56+
}
57+
58+
line_num
59+
}
60+
61+
fn line_expand(
62+
db: &dyn AstDatabase,
63+
id: MacroCallId,
64+
_tt: &tt::Subtree,
65+
) -> Result<tt::Subtree, mbe::ExpandError> {
66+
let loc = db.lookup_intern_macro(id);
67+
let macro_call = loc.ast_id.to_node(db);
68+
69+
let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
70+
let arg_start = arg.syntax().text_range().start();
71+
72+
let file = id.as_file(MacroFileKind::Expr);
73+
let line_num = to_line_number(db, file, arg_start);
74+
75+
let expanded = quote! {
76+
#line_num
77+
};
78+
79+
Ok(expanded)
80+
}

crates/ra_hir_expand/src/db.rs

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,37 @@ use ra_prof::profile;
99
use ra_syntax::{AstNode, Parse, SyntaxNode};
1010

1111
use crate::{
12-
ast_id_map::AstIdMap, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId,
13-
MacroFile, MacroFileKind,
12+
ast_id_map::AstIdMap, BuiltinExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc,
13+
MacroDefId, MacroDefKind, MacroFile, MacroFileKind,
1414
};
1515

16+
#[derive(Debug, Clone, Eq, PartialEq)]
17+
pub enum TokenExpander {
18+
MacroRules(mbe::MacroRules),
19+
Builtin(BuiltinExpander),
20+
}
21+
22+
impl TokenExpander {
23+
pub fn expand(
24+
&self,
25+
db: &dyn AstDatabase,
26+
id: MacroCallId,
27+
tt: &tt::Subtree,
28+
) -> Result<tt::Subtree, mbe::ExpandError> {
29+
match self {
30+
TokenExpander::MacroRules(it) => it.expand(tt),
31+
TokenExpander::Builtin(it) => it.expand(db, id, tt),
32+
}
33+
}
34+
35+
pub fn shift(&self) -> u32 {
36+
match self {
37+
TokenExpander::MacroRules(it) => it.shift(),
38+
TokenExpander::Builtin(_) => 0,
39+
}
40+
}
41+
}
42+
1643
// FIXME: rename to ExpandDatabase
1744
#[salsa::query_group(AstDatabaseStorage)]
1845
pub trait AstDatabase: SourceDatabase {
@@ -24,7 +51,7 @@ pub trait AstDatabase: SourceDatabase {
2451
#[salsa::interned]
2552
fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId;
2653
fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
27-
fn macro_def(&self, id: MacroDefId) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>>;
54+
fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
2855
fn parse_macro(
2956
&self,
3057
macro_file: MacroFile,
@@ -41,18 +68,25 @@ pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdM
4168
pub(crate) fn macro_def(
4269
db: &dyn AstDatabase,
4370
id: MacroDefId,
44-
) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>> {
45-
let macro_call = id.ast_id.to_node(db);
46-
let arg = macro_call.token_tree()?;
47-
let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
48-
log::warn!("fail on macro_def to token tree: {:#?}", arg);
49-
None
50-
})?;
51-
let rules = MacroRules::parse(&tt).ok().or_else(|| {
52-
log::warn!("fail on macro_def parse: {:#?}", tt);
53-
None
54-
})?;
55-
Some(Arc::new((rules, tmap)))
71+
) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
72+
match id.kind {
73+
MacroDefKind::Declarative => {
74+
let macro_call = id.ast_id.to_node(db);
75+
let arg = macro_call.token_tree()?;
76+
let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
77+
log::warn!("fail on macro_def to token tree: {:#?}", arg);
78+
None
79+
})?;
80+
let rules = MacroRules::parse(&tt).ok().or_else(|| {
81+
log::warn!("fail on macro_def parse: {:#?}", tt);
82+
None
83+
})?;
84+
Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
85+
}
86+
MacroDefKind::BuiltIn(expander) => {
87+
Some(Arc::new((TokenExpander::Builtin(expander.clone()), mbe::TokenMap::default())))
88+
}
89+
}
5690
}
5791

5892
pub(crate) fn macro_arg(
@@ -74,7 +108,7 @@ pub(crate) fn macro_expand(
74108
let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
75109

76110
let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
77-
let tt = macro_rules.0.expand(&macro_arg.0).map_err(|err| format!("{:?}", err))?;
111+
let tt = macro_rules.0.expand(db, id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
78112
// Set a hard limit for the expanded tt
79113
let count = tt.count();
80114
if count > 65536 {

crates/ra_hir_expand/src/hygiene.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
db::AstDatabase,
1010
either::Either,
1111
name::{AsName, Name},
12-
HirFileId, HirFileIdRepr,
12+
HirFileId, HirFileIdRepr, MacroDefKind,
1313
};
1414

1515
#[derive(Debug)]
@@ -24,7 +24,10 @@ impl Hygiene {
2424
HirFileIdRepr::FileId(_) => None,
2525
HirFileIdRepr::MacroFile(macro_file) => {
2626
let loc = db.lookup_intern_macro(macro_file.macro_call_id);
27-
Some(loc.def.krate)
27+
match loc.def.kind {
28+
MacroDefKind::Declarative => Some(loc.def.krate),
29+
MacroDefKind::BuiltIn(_) => None,
30+
}
2831
}
2932
};
3033
Hygiene { def_crate }

crates/ra_hir_expand/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub mod either;
1010
pub mod name;
1111
pub mod hygiene;
1212
pub mod diagnostics;
13+
pub mod builtin_macro;
14+
pub mod quote;
1315

1416
use std::hash::{Hash, Hasher};
1517
use std::sync::Arc;
@@ -21,6 +23,7 @@ use ra_syntax::{
2123
};
2224

2325
use crate::ast_id_map::FileAstId;
26+
use crate::builtin_macro::BuiltinExpander;
2427

2528
/// Input to the analyzer is a set of files, where each file is identified by
2629
/// `FileId` and contains source code. However, another source of source code in
@@ -122,6 +125,13 @@ impl salsa::InternKey for MacroCallId {
122125
pub struct MacroDefId {
123126
pub krate: CrateId,
124127
pub ast_id: AstId<ast::MacroCall>,
128+
pub kind: MacroDefKind,
129+
}
130+
131+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
132+
pub enum MacroDefKind {
133+
Declarative,
134+
BuiltIn(BuiltinExpander),
125135
}
126136

127137
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -144,7 +154,7 @@ pub struct ExpansionInfo {
144154
pub(crate) def_start: (HirFileId, TextUnit),
145155
pub(crate) shift: u32,
146156

147-
pub(crate) macro_def: Arc<(mbe::MacroRules, mbe::TokenMap)>,
157+
pub(crate) macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
148158
pub(crate) macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
149159
pub(crate) exp_map: Arc<mbe::RevTokenMap>,
150160
}

crates/ra_hir_expand/src/name.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,6 @@ pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
140140
pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
141141
pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
142142
pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
143+
144+
// Builtin Macros
145+
pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line");

0 commit comments

Comments
 (0)