]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/source_util.rs
Rollup merge of #42006 - jseyfried:fix_include_regression, r=nrc
[rust.git] / src / libsyntax / ext / source_util.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use ast;
12 use syntax_pos::{self, Pos, Span};
13 use ext::base::*;
14 use ext::base;
15 use ext::build::AstBuilder;
16 use parse::{token, DirectoryOwnership};
17 use parse;
18 use print::pprust;
19 use ptr::P;
20 use symbol::Symbol;
21 use tokenstream;
22 use util::small_vector::SmallVector;
23
24 use std::fs::File;
25 use std::io::prelude::*;
26 use std::path::{Path, PathBuf};
27 use std::rc::Rc;
28
29 // These macros all relate to the file system; they either return
30 // the column/row/filename of the expression, or they include
31 // a given file into the current one.
32
33 /// line!(): expands to the current line number
34 pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
35                    -> Box<base::MacResult+'static> {
36     base::check_zero_tts(cx, sp, tts, "line!");
37
38     let topmost = cx.expansion_cause().unwrap_or(sp);
39     let loc = cx.codemap().lookup_char_pos(topmost.lo);
40
41     base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32))
42 }
43
44 /* column!(): expands to the current column number */
45 pub fn expand_column(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
46                   -> Box<base::MacResult+'static> {
47     base::check_zero_tts(cx, sp, tts, "column!");
48
49     let topmost = cx.expansion_cause().unwrap_or(sp);
50     let loc = cx.codemap().lookup_char_pos(topmost.lo);
51
52     base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32))
53 }
54
55 /// file!(): expands to the current filename */
56 /// The filemap (`loc.file`) contains a bunch more information we could spit
57 /// out if we wanted.
58 pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
59                    -> Box<base::MacResult+'static> {
60     base::check_zero_tts(cx, sp, tts, "file!");
61
62     let topmost = cx.expansion_cause().unwrap_or(sp);
63     let loc = cx.codemap().lookup_char_pos(topmost.lo);
64     base::MacEager::expr(cx.expr_str(topmost, Symbol::intern(&loc.file.name)))
65 }
66
67 pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
68                         -> Box<base::MacResult+'static> {
69     let s = pprust::tts_to_string(tts);
70     base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&s)))
71 }
72
73 pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
74                   -> Box<base::MacResult+'static> {
75     base::check_zero_tts(cx, sp, tts, "module_path!");
76     let mod_path = &cx.current_expansion.module.mod_path;
77     let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
78
79     base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&string)))
80 }
81
82 /// include! : parse the given file as an expr
83 /// This is generally a bad idea because it's going to behave
84 /// unhygienically.
85 pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
86                            -> Box<base::MacResult+'cx> {
87     let file = match get_single_str_from_tts(cx, sp, tts, "include!") {
88         Some(f) => f,
89         None => return DummyResult::expr(sp),
90     };
91     // The file will be added to the code map by the parser
92     let path = res_rel_file(cx, sp, Path::new(&file));
93     let directory_ownership = DirectoryOwnership::Owned;
94     let p = parse::new_sub_parser_from_file(cx.parse_sess(), &path, directory_ownership, None, sp);
95
96     struct ExpandResult<'a> {
97         p: parse::parser::Parser<'a>,
98     }
99     impl<'a> base::MacResult for ExpandResult<'a> {
100         fn make_expr(mut self: Box<ExpandResult<'a>>) -> Option<P<ast::Expr>> {
101             Some(panictry!(self.p.parse_expr()))
102         }
103         fn make_items(mut self: Box<ExpandResult<'a>>)
104                       -> Option<SmallVector<P<ast::Item>>> {
105             let mut ret = SmallVector::new();
106             while self.p.token != token::Eof {
107                 match panictry!(self.p.parse_item()) {
108                     Some(item) => ret.push(item),
109                     None => panic!(self.p.diagnostic().span_fatal(self.p.span,
110                                                            &format!("expected item, found `{}`",
111                                                                     self.p.this_token_to_string())))
112                 }
113             }
114             Some(ret)
115         }
116     }
117
118     Box::new(ExpandResult { p: 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<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, Path::new(&file));
129     let mut bytes = Vec::new();
130     match File::open(&file).and_then(|mut f| f.read_to_end(&mut bytes)) {
131         Ok(..) => {}
132         Err(e) => {
133             cx.span_err(sp,
134                         &format!("couldn't read {}: {}",
135                                 file.display(),
136                                 e));
137             return DummyResult::expr(sp);
138         }
139     };
140     match String::from_utf8(bytes) {
141         Ok(src) => {
142             // Add this input file to the code map to make it available as
143             // dependency information
144             let filename = format!("{}", file.display());
145             cx.codemap().new_filemap_and_lines(&filename, &src);
146
147             base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&src)))
148         }
149         Err(_) => {
150             cx.span_err(sp,
151                         &format!("{} wasn't a utf-8 file",
152                                 file.display()));
153             DummyResult::expr(sp)
154         }
155     }
156 }
157
158 pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
159                             -> Box<base::MacResult+'static> {
160     let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") {
161         Some(f) => f,
162         None => return DummyResult::expr(sp)
163     };
164     let file = res_rel_file(cx, sp, Path::new(&file));
165     let mut bytes = Vec::new();
166     match File::open(&file).and_then(|mut f| f.read_to_end(&mut bytes)) {
167         Err(e) => {
168             cx.span_err(sp,
169                         &format!("couldn't read {}: {}", file.display(), e));
170             DummyResult::expr(sp)
171         }
172         Ok(..) => {
173             // Add this input file to the code map to make it available as
174             // dependency information, but don't enter it's contents
175             let filename = format!("{}", file.display());
176             cx.codemap().new_filemap_and_lines(&filename, "");
177
178             base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Rc::new(bytes))))
179         }
180     }
181 }
182
183 // resolve a file-system path to an absolute file-system path (if it
184 // isn't already)
185 fn res_rel_file(cx: &mut ExtCtxt, sp: syntax_pos::Span, arg: &Path) -> PathBuf {
186     // NB: relative paths are resolved relative to the compilation unit
187     if !arg.is_absolute() {
188         let callsite = sp.source_callsite();
189         let mut cu = PathBuf::from(&cx.codemap().span_to_filename(callsite));
190         cu.pop();
191         cu.push(arg);
192         cu
193     } else {
194         arg.to_path_buf()
195     }
196 }