4 use rustc_ast
::tokenstream
::TokenStream
;
5 use rustc_ast_pretty
::pprust
;
6 use rustc_expand
::base
::{self, *}
;
7 use rustc_expand
::module
::DirOwnership
;
8 use rustc_parse
::parser
::{ForceCollect, Parser}
;
9 use rustc_parse
::{self, new_parser_from_file}
;
10 use rustc_session
::lint
::builtin
::INCOMPLETE_INCLUDE
;
11 use rustc_span
::symbol
::Symbol
;
12 use rustc_span
::{self, Pos, Span}
;
14 use smallvec
::SmallVec
;
17 // These macros all relate to the file system; they either return
18 // the column/row/filename of the expression, or they include
19 // a given file into the current one.
21 /// line!(): expands to the current line number
26 ) -> Box
<dyn base
::MacResult
+ '
static> {
27 let sp
= cx
.with_def_site_ctxt(sp
);
28 base
::check_zero_tts(cx
, sp
, tts
, "line!");
30 let topmost
= cx
.expansion_cause().unwrap_or(sp
);
31 let loc
= cx
.source_map().lookup_char_pos(topmost
.lo());
33 base
::MacEager
::expr(cx
.expr_u32(topmost
, loc
.line
as u32))
36 /* column!(): expands to the current column number */
41 ) -> Box
<dyn base
::MacResult
+ '
static> {
42 let sp
= cx
.with_def_site_ctxt(sp
);
43 base
::check_zero_tts(cx
, sp
, tts
, "column!");
45 let topmost
= cx
.expansion_cause().unwrap_or(sp
);
46 let loc
= cx
.source_map().lookup_char_pos(topmost
.lo());
48 base
::MacEager
::expr(cx
.expr_u32(topmost
, loc
.col
.to_usize() as u32 + 1))
51 /// file!(): expands to the current filename */
52 /// The source_file (`loc.file`) contains a bunch more information we could spit
58 ) -> Box
<dyn base
::MacResult
+ '
static> {
59 let sp
= cx
.with_def_site_ctxt(sp
);
60 base
::check_zero_tts(cx
, sp
, tts
, "file!");
62 let topmost
= cx
.expansion_cause().unwrap_or(sp
);
63 let loc
= cx
.source_map().lookup_char_pos(topmost
.lo());
65 cx
.expr_str(topmost
, Symbol
::intern(&loc
.file
.name
.prefer_remapped().to_string_lossy())),
69 pub fn expand_stringify(
73 ) -> Box
<dyn base
::MacResult
+ '
static> {
74 let sp
= cx
.with_def_site_ctxt(sp
);
75 let s
= pprust
::tts_to_string(&tts
);
76 base
::MacEager
::expr(cx
.expr_str(sp
, Symbol
::intern(&s
)))
83 ) -> Box
<dyn base
::MacResult
+ '
static> {
84 let sp
= cx
.with_def_site_ctxt(sp
);
85 base
::check_zero_tts(cx
, sp
, tts
, "module_path!");
86 let mod_path
= &cx
.current_expansion
.module
.mod_path
;
87 let string
= mod_path
.iter().map(|x
| x
.to_string()).collect
::<Vec
<String
>>().join("::");
89 base
::MacEager
::expr(cx
.expr_str(sp
, Symbol
::intern(&string
)))
92 /// include! : parse the given file as an expr
93 /// This is generally a bad idea because it's going to behave
95 pub fn expand_include
<'cx
>(
96 cx
: &'cx
mut ExtCtxt
<'_
>,
99 ) -> Box
<dyn base
::MacResult
+ 'cx
> {
100 let sp
= cx
.with_def_site_ctxt(sp
);
101 let Some(file
) = get_single_str_from_tts(cx
, sp
, tts
, "include!") else {
102 return DummyResult
::any(sp
);
104 // The file will be added to the code map by the parser
105 let file
= match resolve_path(&cx
.sess
.parse_sess
, file
.as_str(), sp
) {
109 return DummyResult
::any(sp
);
112 let p
= new_parser_from_file(cx
.parse_sess(), &file
, Some(sp
));
114 // If in the included file we have e.g., `mod bar;`,
115 // then the path of `bar.rs` should be relative to the directory of `file`.
116 // See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion.
117 // `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained.
118 let dir_path
= file
.parent().unwrap_or(&file
).to_owned();
119 cx
.current_expansion
.module
= Rc
::new(cx
.current_expansion
.module
.with_dir_path(dir_path
));
120 cx
.current_expansion
.dir_ownership
= DirOwnership
::Owned { relative: None }
;
122 struct ExpandResult
<'a
> {
124 node_id
: ast
::NodeId
,
126 impl<'a
> base
::MacResult
for ExpandResult
<'a
> {
127 fn make_expr(mut self: Box
<ExpandResult
<'a
>>) -> Option
<P
<ast
::Expr
>> {
128 let r
= base
::parse_expr(&mut self.p
)?
;
129 if self.p
.token
!= token
::Eof
{
130 self.p
.sess
.buffer_lint(
134 "include macro expected single expression in source",
140 fn make_items(mut self: Box
<ExpandResult
<'a
>>) -> Option
<SmallVec
<[P
<ast
::Item
>; 1]>> {
141 let mut ret
= SmallVec
::new();
143 match self.p
.parse_item(ForceCollect
::No
) {
148 Ok(Some(item
)) => ret
.push(item
),
150 if self.p
.token
!= token
::Eof
{
151 let token
= pprust
::token_to_string(&self.p
.token
);
152 let msg
= format
!("expected item, found `{}`", token
);
153 self.p
.struct_span_err(self.p
.token
.span
, &msg
).emit();
164 Box
::new(ExpandResult { p, node_id: cx.current_expansion.lint_node_id }
)
167 /// `include_str!`: read the given file, insert it as a literal string expr
168 pub fn expand_include_str(
169 cx
: &mut ExtCtxt
<'_
>,
172 ) -> Box
<dyn base
::MacResult
+ '
static> {
173 let sp
= cx
.with_def_site_ctxt(sp
);
174 let Some(file
) = get_single_str_from_tts(cx
, sp
, tts
, "include_str!") else {
175 return DummyResult
::any(sp
);
177 let file
= match resolve_path(&cx
.sess
.parse_sess
, file
.as_str(), sp
) {
181 return DummyResult
::any(sp
);
184 match cx
.source_map().load_binary_file(&file
) {
185 Ok(bytes
) => match std
::str::from_utf8(&bytes
) {
187 let interned_src
= Symbol
::intern(&src
);
188 base
::MacEager
::expr(cx
.expr_str(sp
, interned_src
))
191 cx
.span_err(sp
, &format
!("{} wasn't a utf-8 file", file
.display()));
196 cx
.span_err(sp
, &format
!("couldn't read {}: {}", file
.display(), e
));
202 pub fn expand_include_bytes(
203 cx
: &mut ExtCtxt
<'_
>,
206 ) -> Box
<dyn base
::MacResult
+ '
static> {
207 let sp
= cx
.with_def_site_ctxt(sp
);
208 let Some(file
) = get_single_str_from_tts(cx
, sp
, tts
, "include_bytes!") else {
209 return DummyResult
::any(sp
);
211 let file
= match resolve_path(&cx
.sess
.parse_sess
, file
.as_str(), sp
) {
215 return DummyResult
::any(sp
);
218 match cx
.source_map().load_binary_file(&file
) {
220 let expr
= cx
.expr(sp
, ast
::ExprKind
::IncludedBytes(bytes
.into()));
221 base
::MacEager
::expr(expr
)
224 cx
.span_err(sp
, &format
!("couldn't read {}: {}", file
.display(), e
));