]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/source_util.rs
libsyntax => 2018
[rust.git] / src / libsyntax / ext / source_util.rs
1 use crate::ast;
2 use crate::ext::base::{self, *};
3 use crate::ext::build::AstBuilder;
4 use crate::parse::{self, token, DirectoryOwnership};
5 use crate::print::pprust;
6 use crate::ptr::P;
7 use crate::symbol::Symbol;
8 use crate::tokenstream;
9
10 use smallvec::SmallVec;
11 use syntax_pos::{self, Pos, Span, FileName};
12
13 use std::fs;
14 use std::io::ErrorKind;
15 use std::path::PathBuf;
16 use rustc_data_structures::sync::Lrc;
17
18 // These macros all relate to the file system; they either return
19 // the column/row/filename of the expression, or they include
20 // a given file into the current one.
21
22 /// line!(): expands to the current line number
23 pub fn expand_line(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
24                    -> Box<dyn base::MacResult+'static> {
25     base::check_zero_tts(cx, sp, tts, "line!");
26
27     let topmost = cx.expansion_cause().unwrap_or(sp);
28     let loc = cx.source_map().lookup_char_pos(topmost.lo());
29
30     base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32))
31 }
32
33 /* column!(): expands to the current column number */
34 pub fn expand_column(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
35                   -> Box<dyn base::MacResult+'static> {
36     base::check_zero_tts(cx, sp, tts, "column!");
37
38     let topmost = cx.expansion_cause().unwrap_or(sp);
39     let loc = cx.source_map().lookup_char_pos(topmost.lo());
40
41     base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32 + 1))
42 }
43
44 /* __rust_unstable_column!(): expands to the current column number */
45 pub fn expand_column_gated(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
46                   -> Box<dyn base::MacResult+'static> {
47     if sp.allows_unstable() {
48         expand_column(cx, sp, tts)
49     } else {
50         cx.span_fatal(sp, "the __rust_unstable_column macro is unstable");
51     }
52 }
53
54 /// file!(): expands to the current filename */
55 /// The source_file (`loc.file`) contains a bunch more information we could spit
56 /// out if we wanted.
57 pub fn expand_file(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
58                    -> Box<dyn base::MacResult+'static> {
59     base::check_zero_tts(cx, sp, tts, "file!");
60
61     let topmost = cx.expansion_cause().unwrap_or(sp);
62     let loc = cx.source_map().lookup_char_pos(topmost.lo());
63     base::MacEager::expr(cx.expr_str(topmost, Symbol::intern(&loc.file.name.to_string())))
64 }
65
66 pub fn expand_stringify(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
67                         -> Box<dyn base::MacResult+'static> {
68     let s = pprust::tts_to_string(tts);
69     base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&s)))
70 }
71
72 pub fn expand_mod(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
73                   -> Box<dyn base::MacResult+'static> {
74     base::check_zero_tts(cx, sp, tts, "module_path!");
75     let mod_path = &cx.current_expansion.module.mod_path;
76     let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
77
78     base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&string)))
79 }
80
81 /// include! : parse the given file as an expr
82 /// This is generally a bad idea because it's going to behave
83 /// unhygienically.
84 pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
85                            -> Box<dyn base::MacResult+'cx> {
86     let file = match get_single_str_from_tts(cx, sp, tts, "include!") {
87         Some(f) => f,
88         None => return DummyResult::any(sp),
89     };
90     // The file will be added to the code map by the parser
91     let path = res_rel_file(cx, sp, file);
92     let directory_ownership = DirectoryOwnership::Owned { relative: None };
93     let p = parse::new_sub_parser_from_file(cx.parse_sess(), &path, directory_ownership, None, sp);
94
95     struct ExpandResult<'a> {
96         p: parse::parser::Parser<'a>,
97     }
98     impl<'a> base::MacResult for ExpandResult<'a> {
99         fn make_expr(mut self: Box<ExpandResult<'a>>) -> Option<P<ast::Expr>> {
100             Some(panictry!(self.p.parse_expr()))
101         }
102
103         fn make_items(mut self: Box<ExpandResult<'a>>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
104             let mut ret = SmallVec::new();
105             while self.p.token != token::Eof {
106                 match panictry!(self.p.parse_item()) {
107                     Some(item) => ret.push(item),
108                     None => self.p.diagnostic().span_fatal(self.p.span,
109                                                            &format!("expected item, found `{}`",
110                                                                     self.p.this_token_to_string()))
111                                                .raise()
112                 }
113             }
114             Some(ret)
115         }
116     }
117
118     Box::new(ExpandResult { p })
119 }
120
121 // include_str! : read the given file, insert it as a literal string expr
122 pub fn expand_include_str(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
123                           -> Box<dyn base::MacResult+'static> {
124     let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") {
125         Some(f) => f,
126         None => return DummyResult::expr(sp)
127     };
128     let file = res_rel_file(cx, sp, file);
129     match fs::read_to_string(&file) {
130         Ok(src) => {
131             let interned_src = Symbol::intern(&src);
132
133             // Add this input file to the code map to make it available as
134             // dependency information
135             cx.source_map().new_source_file(file.into(), src);
136
137             base::MacEager::expr(cx.expr_str(sp, interned_src))
138         },
139         Err(ref e) if e.kind() == ErrorKind::InvalidData => {
140             cx.span_err(sp, &format!("{} wasn't a utf-8 file", file.display()));
141             DummyResult::expr(sp)
142         }
143         Err(e) => {
144             cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e));
145             DummyResult::expr(sp)
146         }
147     }
148 }
149
150 pub fn expand_include_bytes(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
151                             -> Box<dyn base::MacResult+'static> {
152     let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") {
153         Some(f) => f,
154         None => return DummyResult::expr(sp)
155     };
156     let file = res_rel_file(cx, sp, file);
157     match fs::read(&file) {
158         Ok(bytes) => {
159             // Add the contents to the source map if it contains UTF-8.
160             let (contents, bytes) = match String::from_utf8(bytes) {
161                 Ok(s) => {
162                     let bytes = s.as_bytes().to_owned();
163                     (s, bytes)
164                 },
165                 Err(e) => (String::new(), e.into_bytes()),
166             };
167             cx.source_map().new_source_file(file.into(), contents);
168
169             base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::new(bytes))))
170         },
171         Err(e) => {
172             cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e));
173             DummyResult::expr(sp)
174         }
175     }
176 }
177
178 // resolve a file-system path to an absolute file-system path (if it
179 // isn't already)
180 fn res_rel_file(cx: &mut ExtCtxt<'_>, sp: syntax_pos::Span, arg: String) -> PathBuf {
181     let arg = PathBuf::from(arg);
182     // Relative paths are resolved relative to the file in which they are found
183     // after macro expansion (that is, they are unhygienic).
184     if !arg.is_absolute() {
185         let callsite = sp.source_callsite();
186         let mut path = match cx.source_map().span_to_unmapped_path(callsite) {
187             FileName::Real(path) => path,
188             FileName::DocTest(path, _) => path,
189             other => panic!("cannot resolve relative path in non-file source `{}`", other),
190         };
191         path.pop();
192         path.push(arg);
193         path
194     } else {
195         arg
196     }
197 }