]> git.lizzy.rs Git - rust.git/blob - src/librustc_save_analysis/span_utils.rs
4d93e81a78fa128b09bb66cb10d594ff203b6976
[rust.git] / src / librustc_save_analysis / span_utils.rs
1 // Copyright 2012-2014 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 rustc::session::Session;
12
13 use generated_code;
14
15 use std::cell::Cell;
16 use std::env;
17
18 use syntax::parse::lexer::{self, StringReader};
19 use syntax::parse::token::{self, Token};
20 use syntax::symbol::keywords;
21 use syntax_pos::*;
22
23 #[derive(Clone)]
24 pub struct SpanUtils<'a> {
25     pub sess: &'a Session,
26     // FIXME given that we clone SpanUtils all over the place, this err_count is
27     // probably useless and any logic relying on it is bogus.
28     pub err_count: Cell<isize>,
29 }
30
31 impl<'a> SpanUtils<'a> {
32     pub fn new(sess: &'a Session) -> SpanUtils<'a> {
33         SpanUtils {
34             sess,
35             err_count: Cell::new(0),
36         }
37     }
38
39     pub fn make_path_string(path: &FileName) -> String {
40         match *path {
41             FileName::Real(ref path) if !path.is_absolute() =>
42                 env::current_dir()
43                     .unwrap()
44                     .join(&path)
45                     .display()
46                     .to_string(),
47             _ => path.to_string(),
48         }
49     }
50
51     pub fn snippet(&self, span: Span) -> String {
52         match self.sess.codemap().span_to_snippet(span) {
53             Ok(s) => s,
54             Err(_) => String::new(),
55         }
56     }
57
58     pub fn retokenise_span(&self, span: Span) -> StringReader<'a> {
59         lexer::StringReader::retokenize(&self.sess.parse_sess, span)
60     }
61
62     // Re-parses a path and returns the span for the last identifier in the path
63     pub fn span_for_last_ident(&self, span: Span) -> Option<Span> {
64         let mut result = None;
65
66         let mut toks = self.retokenise_span(span);
67         let mut bracket_count = 0;
68         loop {
69             let ts = toks.real_token();
70             if ts.tok == token::Eof {
71                 return result;
72             }
73             if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) {
74                 result = Some(ts.sp);
75             }
76
77             bracket_count += match ts.tok {
78                 token::Lt => 1,
79                 token::Gt => -1,
80                 token::BinOp(token::Shr) => -2,
81                 _ => 0,
82             }
83         }
84     }
85
86     // Return the span for the first identifier in the path.
87     pub fn span_for_first_ident(&self, span: Span) -> Option<Span> {
88         let mut toks = self.retokenise_span(span);
89         let mut bracket_count = 0;
90         loop {
91             let ts = toks.real_token();
92             if ts.tok == token::Eof {
93                 return None;
94             }
95             if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) {
96                 return Some(ts.sp);
97             }
98
99             bracket_count += match ts.tok {
100                 token::Lt => 1,
101                 token::Gt => -1,
102                 token::BinOp(token::Shr) => -2,
103                 _ => 0,
104             }
105         }
106     }
107
108     // Return the span for the last ident before a `<` and outside any
109     // angle brackets, or the last span.
110     pub fn sub_span_for_type_name(&self, span: Span) -> Option<Span> {
111         let mut toks = self.retokenise_span(span);
112         let mut prev = toks.real_token();
113         let mut result = None;
114
115         // We keep track of the following two counts - the depth of nesting of
116         // angle brackets, and the depth of nesting of square brackets. For the
117         // angle bracket count, we only count tokens which occur outside of any
118         // square brackets (i.e. bracket_count == 0). The intuition here is
119         // that we want to count angle brackets in the type, but not any which
120         // could be in expression context (because these could mean 'less than',
121         // etc.).
122         let mut angle_count = 0;
123         let mut bracket_count = 0;
124         loop {
125             let next = toks.real_token();
126
127             if (next.tok == token::Lt || next.tok == token::Colon) && angle_count == 0
128                 && bracket_count == 0 && prev.tok.is_ident()
129             {
130                 result = Some(prev.sp);
131             }
132
133             if bracket_count == 0 {
134                 angle_count += match prev.tok {
135                     token::Lt => 1,
136                     token::Gt => -1,
137                     token::BinOp(token::Shl) => 2,
138                     token::BinOp(token::Shr) => -2,
139                     _ => 0,
140                 };
141             }
142
143             bracket_count += match prev.tok {
144                 token::OpenDelim(token::Bracket) => 1,
145                 token::CloseDelim(token::Bracket) => -1,
146                 _ => 0,
147             };
148
149             if next.tok == token::Eof {
150                 break;
151             }
152             prev = next;
153         }
154         #[cfg(debug_assertions)] {
155             if angle_count != 0 || bracket_count != 0 {
156                 let loc = self.sess.codemap().lookup_char_pos(span.lo());
157                 span_bug!(
158                     span,
159                     "Mis-counted brackets when breaking path? Parsing '{}' \
160                      in {}, line {}",
161                     self.snippet(span),
162                     loc.file.name,
163                     loc.line
164                 );
165             }
166         }
167         if result.is_none() && prev.tok.is_ident() {
168             return Some(prev.sp);
169         }
170         result
171     }
172
173     pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option<Span> {
174         let mut toks = self.retokenise_span(span);
175         let mut prev = toks.real_token();
176         loop {
177             if prev.tok == token::Eof {
178                 return None;
179             }
180             let next = toks.real_token();
181             if next.tok == tok {
182                 return Some(prev.sp);
183             }
184             prev = next;
185         }
186     }
187
188     pub fn sub_span_of_token(&self, span: Span, tok: Token) -> Option<Span> {
189         let mut toks = self.retokenise_span(span);
190         loop {
191             let next = toks.real_token();
192             if next.tok == token::Eof {
193                 return None;
194             }
195             if next.tok == tok {
196                 return Some(next.sp);
197             }
198         }
199     }
200
201     pub fn sub_span_after_keyword(&self, span: Span, keyword: keywords::Keyword) -> Option<Span> {
202         self.sub_span_after(span, |t| t.is_keyword(keyword))
203     }
204
205     fn sub_span_after<F: Fn(Token) -> bool>(&self, span: Span, f: F) -> Option<Span> {
206         let mut toks = self.retokenise_span(span);
207         loop {
208             let ts = toks.real_token();
209             if ts.tok == token::Eof {
210                 return None;
211             }
212             if f(ts.tok) {
213                 let ts = toks.real_token();
214                 if ts.tok == token::Eof {
215                     return None;
216                 } else {
217                     return Some(ts.sp);
218                 }
219             }
220         }
221     }
222
223     // // Return the name for a macro definition (identifier after first `!`)
224     // pub fn span_for_macro_def_name(&self, span: Span) -> Option<Span> {
225     //     let mut toks = self.retokenise_span(span);
226     //     loop {
227     //         let ts = toks.real_token();
228     //         if ts.tok == token::Eof {
229     //             return None;
230     //         }
231     //         if ts.tok == token::Not {
232     //             let ts = toks.real_token();
233     //             if ts.tok.is_ident() {
234     //                 return Some(ts.sp);
235     //             } else {
236     //                 return None;
237     //             }
238     //         }
239     //     }
240     // }
241
242     // // Return the name for a macro use (identifier before first `!`).
243     // pub fn span_for_macro_use_name(&self, span:Span) -> Option<Span> {
244     //     let mut toks = self.retokenise_span(span);
245     //     let mut prev = toks.real_token();
246     //     loop {
247     //         if prev.tok == token::Eof {
248     //             return None;
249     //         }
250     //         let ts = toks.real_token();
251     //         if ts.tok == token::Not {
252     //             if prev.tok.is_ident() {
253     //                 return Some(prev.sp);
254     //             } else {
255     //                 return None;
256     //             }
257     //         }
258     //         prev = ts;
259     //     }
260     // }
261
262     /// Return true if the span is generated code, and
263     /// it is not a subspan of the root callsite.
264     ///
265     /// Used to filter out spans of minimal value,
266     /// such as references to macro internal variables.
267     pub fn filter_generated(&self, sub_span: Option<Span>, parent: Span) -> bool {
268         if !generated_code(parent) {
269             if sub_span.is_none() {
270                 // Edge case - this occurs on generated code with incorrect expansion info.
271                 return true;
272             }
273             return false;
274         }
275         // If sub_span is none, filter out generated code.
276         let sub_span = match sub_span {
277             Some(ss) => ss,
278             None => return true,
279         };
280
281         //If the span comes from a fake filemap, filter it.
282         if !self.sess
283             .codemap()
284             .lookup_char_pos(parent.lo())
285             .file
286             .is_real_file()
287         {
288             return true;
289         }
290
291         // Otherwise, a generated span is deemed invalid if it is not a sub-span of the root
292         // callsite. This filters out macro internal variables and most malformed spans.
293         !parent.source_callsite().contains(sub_span)
294     }
295 }
296
297 macro_rules! filter {
298     ($util: expr, $span: expr, $parent: expr, None) => {
299         if $util.filter_generated($span, $parent) {
300             return None;
301         }
302     };
303     ($util: expr, $span: ident, $parent: expr) => {
304         if $util.filter_generated($span, $parent) {
305             return;
306         }
307     };
308 }