]>
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 JJ |
18 | |
19 | use syntax::ast; | |
32a655c1 | 20 | use syntax::parse::lexer::{self, StringReader}; |
476ff2be SL |
21 | use syntax::parse::token::{self, Token}; |
22 | use syntax::symbol::keywords; | |
32a655c1 | 23 | use syntax::tokenstream::TokenTree; |
3157f602 | 24 | use syntax_pos::*; |
1a4d82fc JJ |
25 | |
26 | #[derive(Clone)] | |
27 | pub struct SpanUtils<'a> { | |
28 | pub sess: &'a Session, | |
a7813a04 XL |
29 | // FIXME given that we clone SpanUtils all over the place, this err_count is |
30 | // probably useless and any logic relying on it is bogus. | |
c34b1796 | 31 | pub err_count: Cell<isize>, |
1a4d82fc JJ |
32 | } |
33 | ||
34 | impl<'a> SpanUtils<'a> { | |
c1a9b12d | 35 | pub fn new(sess: &'a Session) -> SpanUtils<'a> { |
b039eaaf SL |
36 | SpanUtils { |
37 | sess: sess, | |
38 | err_count: Cell::new(0), | |
39 | } | |
c1a9b12d SL |
40 | } |
41 | ||
92a42be0 SL |
42 | pub fn make_path_string(file_name: &str) -> String { |
43 | let path = Path::new(file_name); | |
44 | if path.is_absolute() { | |
45 | path.clone().display().to_string() | |
46 | } else { | |
47 | env::current_dir().unwrap().join(&path).display().to_string() | |
48 | } | |
49 | } | |
50 | ||
1a4d82fc JJ |
51 | pub fn snippet(&self, span: Span) -> String { |
52 | match self.sess.codemap().span_to_snippet(span) { | |
85aaf69f SL |
53 | Ok(s) => s, |
54 | Err(_) => String::new(), | |
1a4d82fc JJ |
55 | } |
56 | } | |
57 | ||
58 | pub fn retokenise_span(&self, span: Span) -> StringReader<'a> { | |
8bb4bdeb | 59 | lexer::StringReader::retokenize(&self.sess.parse_sess, span) |
1a4d82fc JJ |
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); | |
85aaf69f | 67 | let mut bracket_count = 0; |
1a4d82fc JJ |
68 | loop { |
69 | let ts = toks.real_token(); | |
70 | if ts.tok == token::Eof { | |
8bb4bdeb | 71 | return result |
1a4d82fc | 72 | } |
e9174d1e | 73 | if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) { |
1a4d82fc JJ |
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, | |
e9174d1e | 81 | _ => 0, |
1a4d82fc JJ |
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); | |
85aaf69f | 89 | let mut bracket_count = 0; |
1a4d82fc JJ |
90 | loop { |
91 | let ts = toks.real_token(); | |
92 | if ts.tok == token::Eof { | |
93 | return None; | |
94 | } | |
e9174d1e | 95 | if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) { |
8bb4bdeb | 96 | return Some(ts.sp); |
1a4d82fc JJ |
97 | } |
98 | ||
99 | bracket_count += match ts.tok { | |
100 | token::Lt => 1, | |
101 | token::Gt => -1, | |
102 | token::BinOp(token::Shr) => -2, | |
e9174d1e | 103 | _ => 0, |
1a4d82fc JJ |
104 | } |
105 | } | |
106 | } | |
107 | ||
108 | // Return the span for the last ident before a `(` or `<` or '::<' and outside any | |
109 | // any brackets, or the last span. | |
110 | pub fn sub_span_for_meth_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; | |
85aaf69f | 114 | let mut bracket_count = 0; |
c30ab7b3 | 115 | let mut prev_span = None; |
1a4d82fc | 116 | while prev.tok != token::Eof { |
c30ab7b3 | 117 | prev_span = None; |
1a4d82fc JJ |
118 | let mut next = toks.real_token(); |
119 | ||
e9174d1e SL |
120 | if (next.tok == token::OpenDelim(token::Paren) || next.tok == token::Lt) && |
121 | bracket_count == 0 && prev.tok.is_ident() { | |
1a4d82fc JJ |
122 | result = Some(prev.sp); |
123 | } | |
124 | ||
e9174d1e | 125 | if bracket_count == 0 && next.tok == token::ModSep { |
1a4d82fc JJ |
126 | let old = prev; |
127 | prev = next; | |
128 | next = toks.real_token(); | |
e9174d1e | 129 | if next.tok == token::Lt && old.tok.is_ident() { |
1a4d82fc JJ |
130 | result = Some(old.sp); |
131 | } | |
132 | } | |
133 | ||
134 | bracket_count += match prev.tok { | |
135 | token::OpenDelim(token::Paren) | token::Lt => 1, | |
136 | token::CloseDelim(token::Paren) | token::Gt => -1, | |
137 | token::BinOp(token::Shr) => -2, | |
e9174d1e | 138 | _ => 0, |
1a4d82fc JJ |
139 | }; |
140 | ||
141 | if prev.tok.is_ident() && bracket_count == 0 { | |
c30ab7b3 | 142 | prev_span = Some(prev.sp); |
1a4d82fc JJ |
143 | } |
144 | prev = next; | |
145 | } | |
8bb4bdeb | 146 | result.or(prev_span) |
1a4d82fc JJ |
147 | } |
148 | ||
149 | // Return the span for the last ident before a `<` and outside any | |
476ff2be | 150 | // angle brackets, or the last span. |
1a4d82fc JJ |
151 | pub fn sub_span_for_type_name(&self, span: Span) -> Option<Span> { |
152 | let mut toks = self.retokenise_span(span); | |
153 | let mut prev = toks.real_token(); | |
154 | let mut result = None; | |
476ff2be SL |
155 | |
156 | // We keep track of the following two counts - the depth of nesting of | |
157 | // angle brackets, and the depth of nesting of square brackets. For the | |
158 | // angle bracket count, we only count tokens which occur outside of any | |
159 | // square brackets (i.e. bracket_count == 0). The intutition here is | |
160 | // that we want to count angle brackets in the type, but not any which | |
161 | // could be in expression context (because these could mean 'less than', | |
162 | // etc.). | |
163 | let mut angle_count = 0; | |
85aaf69f | 164 | let mut bracket_count = 0; |
1a4d82fc JJ |
165 | loop { |
166 | let next = toks.real_token(); | |
167 | ||
476ff2be SL |
168 | if (next.tok == token::Lt || next.tok == token::Colon) && |
169 | angle_count == 0 && | |
170 | bracket_count == 0 && | |
1a4d82fc JJ |
171 | prev.tok.is_ident() { |
172 | result = Some(prev.sp); | |
173 | } | |
174 | ||
476ff2be SL |
175 | if bracket_count == 0 { |
176 | angle_count += match prev.tok { | |
177 | token::Lt => 1, | |
178 | token::Gt => -1, | |
179 | token::BinOp(token::Shl) => 2, | |
180 | token::BinOp(token::Shr) => -2, | |
181 | _ => 0, | |
182 | }; | |
183 | } | |
184 | ||
1a4d82fc | 185 | bracket_count += match prev.tok { |
476ff2be SL |
186 | token::OpenDelim(token::Bracket) => 1, |
187 | token::CloseDelim(token::Bracket) => -1, | |
e9174d1e | 188 | _ => 0, |
1a4d82fc JJ |
189 | }; |
190 | ||
191 | if next.tok == token::Eof { | |
192 | break; | |
193 | } | |
194 | prev = next; | |
195 | } | |
476ff2be | 196 | if angle_count != 0 || bracket_count != 0 { |
1a4d82fc | 197 | let loc = self.sess.codemap().lookup_char_pos(span.lo); |
54a0048b SL |
198 | span_bug!(span, |
199 | "Mis-counted brackets when breaking path? Parsing '{}' \ | |
200 | in {}, line {}", | |
201 | self.snippet(span), | |
202 | loc.file.name, | |
203 | loc.line); | |
1a4d82fc | 204 | } |
476ff2be | 205 | if result.is_none() && prev.tok.is_ident() && angle_count == 0 { |
8bb4bdeb | 206 | return Some(prev.sp); |
1a4d82fc | 207 | } |
8bb4bdeb | 208 | result |
1a4d82fc JJ |
209 | } |
210 | ||
211 | // Reparse span and return an owned vector of sub spans of the first limit | |
212 | // identifier tokens in the given nesting level. | |
213 | // example with Foo<Bar<T,V>, Bar<T,V>> | |
476ff2be SL |
214 | // Nesting = 0: all idents outside of angle brackets: [Foo] |
215 | // Nesting = 1: idents within one level of angle brackets: [Bar, Bar] | |
c34b1796 | 216 | pub fn spans_with_brackets(&self, span: Span, nesting: isize, limit: isize) -> Vec<Span> { |
c30ab7b3 | 217 | let mut result: Vec<Span> = vec![]; |
1a4d82fc JJ |
218 | |
219 | let mut toks = self.retokenise_span(span); | |
220 | // We keep track of how many brackets we're nested in | |
476ff2be | 221 | let mut angle_count: isize = 0; |
d9579d0f | 222 | let mut bracket_count: isize = 0; |
c34b1796 | 223 | let mut found_ufcs_sep = false; |
1a4d82fc JJ |
224 | loop { |
225 | let ts = toks.real_token(); | |
226 | if ts.tok == token::Eof { | |
476ff2be | 227 | if angle_count != 0 || bracket_count != 0 { |
7453a54e | 228 | if generated_code(span) { |
c30ab7b3 | 229 | return vec![]; |
7453a54e | 230 | } |
1a4d82fc | 231 | let loc = self.sess.codemap().lookup_char_pos(span.lo); |
54a0048b SL |
232 | span_bug!(span, |
233 | "Mis-counted brackets when breaking path? \ | |
234 | Parsing '{}' in {}, line {}", | |
235 | self.snippet(span), | |
236 | loc.file.name, | |
237 | loc.line); | |
1a4d82fc JJ |
238 | } |
239 | return result | |
240 | } | |
c34b1796 | 241 | if (result.len() as isize) == limit { |
1a4d82fc JJ |
242 | return result; |
243 | } | |
244 | bracket_count += match ts.tok { | |
476ff2be SL |
245 | token::OpenDelim(token::Bracket) => 1, |
246 | token::CloseDelim(token::Bracket) => -1, | |
247 | _ => 0, | |
248 | }; | |
249 | if bracket_count > 0 { | |
250 | continue; | |
251 | } | |
252 | angle_count += match ts.tok { | |
1a4d82fc | 253 | token::Lt => 1, |
d9579d0f | 254 | token::Gt => -1, |
1a4d82fc JJ |
255 | token::BinOp(token::Shl) => 2, |
256 | token::BinOp(token::Shr) => -2, | |
e9174d1e | 257 | _ => 0, |
1a4d82fc | 258 | }; |
d9579d0f AL |
259 | |
260 | // Ignore the `>::` in `<Type as Trait>::AssocTy`. | |
261 | ||
262 | // The root cause of this hack is that the AST representation of | |
263 | // qpaths is horrible. It treats <A as B>::C as a path with two | |
264 | // segments, B and C and notes that there is also a self type A at | |
265 | // position 0. Because we don't have spans for individual idents, | |
266 | // only the whole path, we have to iterate over the tokens in the | |
267 | // path, trying to pull out the non-nested idents (e.g., avoiding 'a | |
268 | // in `<A as B<'a>>::C`). So we end up with a span for `B>::C` from | |
269 | // the start of the first ident to the end of the path. | |
476ff2be | 270 | if !found_ufcs_sep && angle_count == -1 { |
d9579d0f | 271 | found_ufcs_sep = true; |
476ff2be | 272 | angle_count += 1; |
d9579d0f | 273 | } |
476ff2be | 274 | if ts.tok.is_ident() && angle_count == nesting { |
8bb4bdeb | 275 | result.push(ts.sp); |
1a4d82fc JJ |
276 | } |
277 | } | |
278 | } | |
279 | ||
32a655c1 SL |
280 | /// `span` must be the span for an item such as a function or struct. This |
281 | /// function returns the program text from the start of the span until the | |
282 | /// end of the 'signature' part, that is up to, but not including an opening | |
283 | /// brace or semicolon. | |
284 | pub fn signature_string_for_span(&self, span: Span) -> String { | |
8bb4bdeb XL |
285 | let mut toks = self.retokenise_span(span); |
286 | toks.real_token(); | |
287 | let mut toks = toks.parse_all_token_trees().unwrap().trees(); | |
32a655c1 | 288 | let mut prev = toks.next().unwrap(); |
8bb4bdeb XL |
289 | |
290 | let first_span = prev.span(); | |
32a655c1 SL |
291 | let mut angle_count = 0; |
292 | for tok in toks { | |
293 | if let TokenTree::Token(_, ref tok) = prev { | |
294 | angle_count += match *tok { | |
295 | token::Eof => { break; } | |
296 | token::Lt => 1, | |
297 | token::Gt => -1, | |
298 | token::BinOp(token::Shl) => 2, | |
299 | token::BinOp(token::Shr) => -2, | |
300 | _ => 0, | |
301 | }; | |
302 | } | |
303 | if angle_count > 0 { | |
304 | prev = tok; | |
305 | continue; | |
306 | } | |
307 | if let TokenTree::Token(_, token::Semi) = tok { | |
8bb4bdeb | 308 | return self.snippet(mk_sp(first_span.lo, prev.span().hi)); |
32a655c1 SL |
309 | } else if let TokenTree::Delimited(_, ref d) = tok { |
310 | if d.delim == token::Brace { | |
8bb4bdeb | 311 | return self.snippet(mk_sp(first_span.lo, prev.span().hi)); |
32a655c1 SL |
312 | } |
313 | } | |
314 | prev = tok; | |
315 | } | |
316 | self.snippet(span) | |
317 | } | |
318 | ||
1a4d82fc JJ |
319 | pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option<Span> { |
320 | let mut toks = self.retokenise_span(span); | |
321 | let mut prev = toks.real_token(); | |
322 | loop { | |
323 | if prev.tok == token::Eof { | |
324 | return None; | |
325 | } | |
326 | let next = toks.real_token(); | |
327 | if next.tok == tok { | |
8bb4bdeb | 328 | return Some(prev.sp); |
1a4d82fc JJ |
329 | } |
330 | prev = next; | |
331 | } | |
332 | } | |
333 | ||
334 | pub fn sub_span_of_token(&self, span: Span, tok: Token) -> Option<Span> { | |
335 | let mut toks = self.retokenise_span(span); | |
336 | loop { | |
337 | let next = toks.real_token(); | |
338 | if next.tok == token::Eof { | |
339 | return None; | |
340 | } | |
341 | if next.tok == tok { | |
8bb4bdeb | 342 | return Some(next.sp); |
1a4d82fc JJ |
343 | } |
344 | } | |
345 | } | |
346 | ||
e9174d1e | 347 | pub fn sub_span_after_keyword(&self, span: Span, keyword: keywords::Keyword) -> Option<Span> { |
85aaf69f SL |
348 | self.sub_span_after(span, |t| t.is_keyword(keyword)) |
349 | } | |
350 | ||
e9174d1e | 351 | pub fn sub_span_after_token(&self, span: Span, tok: Token) -> Option<Span> { |
85aaf69f SL |
352 | self.sub_span_after(span, |t| t == tok) |
353 | } | |
354 | ||
e9174d1e | 355 | fn sub_span_after<F: Fn(Token) -> bool>(&self, span: Span, f: F) -> Option<Span> { |
1a4d82fc JJ |
356 | let mut toks = self.retokenise_span(span); |
357 | loop { | |
358 | let ts = toks.real_token(); | |
359 | if ts.tok == token::Eof { | |
360 | return None; | |
361 | } | |
85aaf69f | 362 | if f(ts.tok) { |
1a4d82fc JJ |
363 | let ts = toks.real_token(); |
364 | if ts.tok == token::Eof { | |
365 | return None | |
366 | } else { | |
8bb4bdeb | 367 | return Some(ts.sp); |
1a4d82fc JJ |
368 | } |
369 | } | |
370 | } | |
371 | } | |
372 | ||
85aaf69f | 373 | |
d9579d0f | 374 | // Returns a list of the spans of idents in a path. |
1a4d82fc JJ |
375 | // E.g., For foo::bar<x,t>::baz, we return [foo, bar, baz] (well, their spans) |
376 | pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec<Span> { | |
1a4d82fc JJ |
377 | self.spans_with_brackets(path.span, 0, -1) |
378 | } | |
379 | ||
380 | // Return an owned vector of the subspans of the param identifier | |
381 | // tokens found in span. | |
c34b1796 | 382 | pub fn spans_for_ty_params(&self, span: Span, number: isize) -> Vec<Span> { |
1a4d82fc | 383 | // Type params are nested within one level of brackets: |
d9579d0f | 384 | // i.e. we want Vec<A, B> from Foo<A, B<T,U>> |
1a4d82fc JJ |
385 | self.spans_with_brackets(span, 1, number) |
386 | } | |
387 | ||
388 | pub fn report_span_err(&self, kind: &str, span: Span) { | |
389 | let loc = self.sess.codemap().lookup_char_pos(span.lo); | |
390 | info!("({}) Could not find sub_span in `{}` in {}, line {}", | |
b039eaaf SL |
391 | kind, |
392 | self.snippet(span), | |
393 | loc.file.name, | |
394 | loc.line); | |
395 | self.err_count.set(self.err_count.get() + 1); | |
1a4d82fc | 396 | if self.err_count.get() > 1000 { |
54a0048b | 397 | bug!("span errors reached 1000, giving up"); |
1a4d82fc JJ |
398 | } |
399 | } | |
7453a54e SL |
400 | |
401 | // Return the name for a macro definition (identifier after first `!`) | |
402 | pub fn span_for_macro_def_name(&self, span: Span) -> Option<Span> { | |
403 | let mut toks = self.retokenise_span(span); | |
404 | loop { | |
405 | let ts = toks.real_token(); | |
406 | if ts.tok == token::Eof { | |
407 | return None; | |
408 | } | |
409 | if ts.tok == token::Not { | |
410 | let ts = toks.real_token(); | |
411 | if ts.tok.is_ident() { | |
8bb4bdeb | 412 | return Some(ts.sp); |
7453a54e SL |
413 | } else { |
414 | return None; | |
415 | } | |
416 | } | |
417 | } | |
418 | } | |
419 | ||
420 | // Return the name for a macro use (identifier before first `!`). | |
421 | pub fn span_for_macro_use_name(&self, span:Span) -> Option<Span> { | |
422 | let mut toks = self.retokenise_span(span); | |
423 | let mut prev = toks.real_token(); | |
424 | loop { | |
425 | if prev.tok == token::Eof { | |
426 | return None; | |
427 | } | |
428 | let ts = toks.real_token(); | |
429 | if ts.tok == token::Not { | |
430 | if prev.tok.is_ident() { | |
8bb4bdeb | 431 | return Some(prev.sp); |
7453a54e SL |
432 | } else { |
433 | return None; | |
434 | } | |
435 | } | |
436 | prev = ts; | |
437 | } | |
438 | } | |
439 | ||
440 | /// Return true if the span is generated code, and | |
441 | /// it is not a subspan of the root callsite. | |
442 | /// | |
443 | /// Used to filter out spans of minimal value, | |
444 | /// such as references to macro internal variables. | |
445 | pub fn filter_generated(&self, sub_span: Option<Span>, parent: Span) -> bool { | |
446 | if !generated_code(parent) { | |
447 | if sub_span.is_none() { | |
448 | // Edge case - this occurs on generated code with incorrect expansion info. | |
449 | return true; | |
450 | } | |
451 | return false; | |
452 | } | |
453 | // If sub_span is none, filter out generated code. | |
454 | if sub_span.is_none() { | |
455 | return true; | |
456 | } | |
457 | ||
458 | //If the span comes from a fake filemap, filter it. | |
459 | if !self.sess.codemap().lookup_char_pos(parent.lo).file.is_real_file() { | |
460 | return true; | |
461 | } | |
462 | ||
463 | // Otherwise, a generated span is deemed invalid if it is not a sub-span of the root | |
464 | // callsite. This filters out macro internal variables and most malformed spans. | |
465 | let span = self.sess.codemap().source_callsite(parent); | |
466 | !(span.contains(parent)) | |
467 | } | |
468 | } | |
469 | ||
470 | macro_rules! filter { | |
471 | ($util: expr, $span: ident, $parent: expr, None) => { | |
472 | if $util.filter_generated($span, $parent) { | |
473 | return None; | |
474 | } | |
475 | }; | |
476 | ($util: expr, $span: ident, $parent: expr) => { | |
477 | if $util.filter_generated($span, $parent) { | |
478 | return; | |
479 | } | |
480 | }; | |
1a4d82fc | 481 | } |