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