]>
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 | ||
13 | use save::generated_code; | |
14 | ||
15 | use std::cell::Cell; | |
92a42be0 SL |
16 | use std::env; |
17 | use std::path::Path; | |
1a4d82fc JJ |
18 | |
19 | use syntax::ast; | |
20 | use syntax::codemap::*; | |
21 | use syntax::parse::lexer; | |
e9174d1e | 22 | use syntax::parse::lexer::{Reader, StringReader}; |
1a4d82fc JJ |
23 | use syntax::parse::token; |
24 | use syntax::parse::token::{keywords, Token}; | |
25 | ||
26 | #[derive(Clone)] | |
27 | pub struct SpanUtils<'a> { | |
28 | pub sess: &'a Session, | |
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 SL |
34 | SpanUtils { |
35 | sess: sess, | |
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 { | |
45 | env::current_dir().unwrap().join(&path).display().to_string() | |
46 | } | |
47 | } | |
48 | ||
1a4d82fc | 49 | // Standard string for extents/location. |
b039eaaf | 50 | #[rustfmt_skip] |
1a4d82fc JJ |
51 | pub fn extent_str(&self, span: Span) -> String { |
52 | let lo_loc = self.sess.codemap().lookup_char_pos(span.lo); | |
53 | let hi_loc = self.sess.codemap().lookup_char_pos(span.hi); | |
54 | let lo_pos = self.sess.codemap().bytepos_to_file_charpos(span.lo); | |
55 | let hi_pos = self.sess.codemap().bytepos_to_file_charpos(span.hi); | |
56 | let lo_pos_byte = self.sess.codemap().lookup_byte_offset(span.lo).pos; | |
57 | let hi_pos_byte = self.sess.codemap().lookup_byte_offset(span.hi).pos; | |
58 | ||
85aaf69f | 59 | format!("file_name,\"{}\",file_line,{},file_col,{},extent_start,{},extent_start_bytes,{},\ |
1a4d82fc | 60 | file_line_end,{},file_col_end,{},extent_end,{},extent_end_bytes,{}", |
92a42be0 | 61 | SpanUtils::make_path_string(&lo_loc.file.name), |
85aaf69f SL |
62 | lo_loc.line, lo_loc.col.to_usize(), lo_pos.to_usize(), lo_pos_byte.to_usize(), |
63 | hi_loc.line, hi_loc.col.to_usize(), hi_pos.to_usize(), hi_pos_byte.to_usize()) | |
1a4d82fc JJ |
64 | } |
65 | ||
66 | // sub_span starts at span.lo, so we need to adjust the positions etc. | |
67 | // If sub_span is None, we don't need to adjust. | |
68 | pub fn make_sub_span(&self, span: Span, sub_span: Option<Span>) -> Option<Span> { | |
1a4d82fc JJ |
69 | match sub_span { |
70 | None => None, | |
71 | Some(sub) => { | |
e9174d1e | 72 | let FileMapAndBytePos {fm, pos} = self.sess.codemap().lookup_byte_offset(span.lo); |
1a4d82fc JJ |
73 | let base = pos + fm.start_pos; |
74 | Some(Span { | |
75 | lo: base + self.sess.codemap().lookup_byte_offset(sub.lo).pos, | |
76 | hi: base + self.sess.codemap().lookup_byte_offset(sub.hi).pos, | |
7453a54e | 77 | expn_id: span.expn_id, |
1a4d82fc JJ |
78 | }) |
79 | } | |
80 | } | |
81 | } | |
82 | ||
83 | pub fn snippet(&self, span: Span) -> String { | |
84 | match self.sess.codemap().span_to_snippet(span) { | |
85aaf69f SL |
85 | Ok(s) => s, |
86 | Err(_) => String::new(), | |
1a4d82fc JJ |
87 | } |
88 | } | |
89 | ||
90 | pub fn retokenise_span(&self, span: Span) -> StringReader<'a> { | |
91 | // sadness - we don't have spans for sub-expressions nor access to the tokens | |
92 | // so in order to get extents for the function name itself (which dxr expects) | |
93 | // we need to re-tokenise the fn definition | |
94 | ||
95 | // Note: this is a bit awful - it adds the contents of span to the end of | |
96 | // the codemap as a new filemap. This is mostly OK, but means we should | |
97 | // not iterate over the codemap. Also, any spans over the new filemap | |
98 | // are incompatible with spans over other filemaps. | |
b039eaaf SL |
99 | let filemap = self.sess |
100 | .codemap() | |
101 | .new_filemap(String::from("<anon-dxr>"), self.snippet(span)); | |
1a4d82fc JJ |
102 | let s = self.sess; |
103 | lexer::StringReader::new(s.diagnostic(), filemap) | |
104 | } | |
105 | ||
106 | // Re-parses a path and returns the span for the last identifier in the path | |
107 | pub fn span_for_last_ident(&self, span: Span) -> Option<Span> { | |
108 | let mut result = None; | |
109 | ||
110 | let mut toks = self.retokenise_span(span); | |
85aaf69f | 111 | let mut bracket_count = 0; |
1a4d82fc JJ |
112 | loop { |
113 | let ts = toks.real_token(); | |
114 | if ts.tok == token::Eof { | |
115 | return self.make_sub_span(span, result) | |
116 | } | |
e9174d1e | 117 | if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) { |
1a4d82fc JJ |
118 | result = Some(ts.sp); |
119 | } | |
120 | ||
121 | bracket_count += match ts.tok { | |
122 | token::Lt => 1, | |
123 | token::Gt => -1, | |
124 | token::BinOp(token::Shr) => -2, | |
e9174d1e | 125 | _ => 0, |
1a4d82fc JJ |
126 | } |
127 | } | |
128 | } | |
129 | ||
130 | // Return the span for the first identifier in the path. | |
131 | pub fn span_for_first_ident(&self, span: Span) -> Option<Span> { | |
132 | let mut toks = self.retokenise_span(span); | |
85aaf69f | 133 | let mut bracket_count = 0; |
1a4d82fc JJ |
134 | loop { |
135 | let ts = toks.real_token(); | |
136 | if ts.tok == token::Eof { | |
137 | return None; | |
138 | } | |
e9174d1e | 139 | if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) { |
1a4d82fc JJ |
140 | return self.make_sub_span(span, Some(ts.sp)); |
141 | } | |
142 | ||
143 | bracket_count += match ts.tok { | |
144 | token::Lt => 1, | |
145 | token::Gt => -1, | |
146 | token::BinOp(token::Shr) => -2, | |
e9174d1e | 147 | _ => 0, |
1a4d82fc JJ |
148 | } |
149 | } | |
150 | } | |
151 | ||
152 | // Return the span for the last ident before a `(` or `<` or '::<' and outside any | |
153 | // any brackets, or the last span. | |
154 | pub fn sub_span_for_meth_name(&self, span: Span) -> Option<Span> { | |
155 | let mut toks = self.retokenise_span(span); | |
156 | let mut prev = toks.real_token(); | |
157 | let mut result = None; | |
85aaf69f | 158 | let mut bracket_count = 0; |
1a4d82fc JJ |
159 | let mut last_span = None; |
160 | while prev.tok != token::Eof { | |
161 | last_span = None; | |
162 | let mut next = toks.real_token(); | |
163 | ||
e9174d1e SL |
164 | if (next.tok == token::OpenDelim(token::Paren) || next.tok == token::Lt) && |
165 | bracket_count == 0 && prev.tok.is_ident() { | |
1a4d82fc JJ |
166 | result = Some(prev.sp); |
167 | } | |
168 | ||
e9174d1e | 169 | if bracket_count == 0 && next.tok == token::ModSep { |
1a4d82fc JJ |
170 | let old = prev; |
171 | prev = next; | |
172 | next = toks.real_token(); | |
e9174d1e | 173 | if next.tok == token::Lt && old.tok.is_ident() { |
1a4d82fc JJ |
174 | result = Some(old.sp); |
175 | } | |
176 | } | |
177 | ||
178 | bracket_count += match prev.tok { | |
179 | token::OpenDelim(token::Paren) | token::Lt => 1, | |
180 | token::CloseDelim(token::Paren) | token::Gt => -1, | |
181 | token::BinOp(token::Shr) => -2, | |
e9174d1e | 182 | _ => 0, |
1a4d82fc JJ |
183 | }; |
184 | ||
185 | if prev.tok.is_ident() && bracket_count == 0 { | |
186 | last_span = Some(prev.sp); | |
187 | } | |
188 | prev = next; | |
189 | } | |
190 | if result.is_none() && last_span.is_some() { | |
191 | return self.make_sub_span(span, last_span); | |
192 | } | |
193 | return self.make_sub_span(span, result); | |
194 | } | |
195 | ||
196 | // Return the span for the last ident before a `<` and outside any | |
197 | // brackets, or the last span. | |
198 | pub fn sub_span_for_type_name(&self, span: Span) -> Option<Span> { | |
199 | let mut toks = self.retokenise_span(span); | |
200 | let mut prev = toks.real_token(); | |
201 | let mut result = None; | |
85aaf69f | 202 | let mut bracket_count = 0; |
1a4d82fc JJ |
203 | loop { |
204 | let next = toks.real_token(); | |
205 | ||
e9174d1e | 206 | if (next.tok == token::Lt || next.tok == token::Colon) && bracket_count == 0 && |
1a4d82fc JJ |
207 | prev.tok.is_ident() { |
208 | result = Some(prev.sp); | |
209 | } | |
210 | ||
211 | bracket_count += match prev.tok { | |
212 | token::Lt => 1, | |
213 | token::Gt => -1, | |
85aaf69f | 214 | token::BinOp(token::Shl) => 2, |
1a4d82fc | 215 | token::BinOp(token::Shr) => -2, |
e9174d1e | 216 | _ => 0, |
1a4d82fc JJ |
217 | }; |
218 | ||
219 | if next.tok == token::Eof { | |
220 | break; | |
221 | } | |
222 | prev = next; | |
223 | } | |
224 | if bracket_count != 0 { | |
225 | let loc = self.sess.codemap().lookup_char_pos(span.lo); | |
226 | self.sess.span_bug(span, | |
b039eaaf SL |
227 | &format!("Mis-counted brackets when breaking path? Parsing '{}' \ |
228 | in {}, line {}", | |
229 | self.snippet(span), | |
230 | loc.file.name, | |
231 | loc.line)); | |
1a4d82fc JJ |
232 | } |
233 | if result.is_none() && prev.tok.is_ident() && bracket_count == 0 { | |
234 | return self.make_sub_span(span, Some(prev.sp)); | |
235 | } | |
236 | self.make_sub_span(span, result) | |
237 | } | |
238 | ||
239 | // Reparse span and return an owned vector of sub spans of the first limit | |
240 | // identifier tokens in the given nesting level. | |
241 | // example with Foo<Bar<T,V>, Bar<T,V>> | |
d9579d0f AL |
242 | // Nesting = 0: all idents outside of brackets: [Foo] |
243 | // Nesting = 1: idents within one level of brackets: [Bar, Bar] | |
c34b1796 | 244 | pub fn spans_with_brackets(&self, span: Span, nesting: isize, limit: isize) -> Vec<Span> { |
1a4d82fc JJ |
245 | let mut result: Vec<Span> = vec!(); |
246 | ||
247 | let mut toks = self.retokenise_span(span); | |
248 | // We keep track of how many brackets we're nested in | |
d9579d0f | 249 | let mut bracket_count: isize = 0; |
c34b1796 | 250 | let mut found_ufcs_sep = false; |
1a4d82fc JJ |
251 | loop { |
252 | let ts = toks.real_token(); | |
253 | if ts.tok == token::Eof { | |
254 | if bracket_count != 0 { | |
7453a54e SL |
255 | if generated_code(span) { |
256 | return vec!(); | |
257 | } | |
1a4d82fc | 258 | let loc = self.sess.codemap().lookup_char_pos(span.lo); |
b039eaaf SL |
259 | self.sess.span_bug(span, |
260 | &format!("Mis-counted brackets when breaking path? \ | |
261 | Parsing '{}' in {}, line {}", | |
262 | self.snippet(span), | |
263 | loc.file.name, | |
264 | loc.line)); | |
1a4d82fc JJ |
265 | } |
266 | return result | |
267 | } | |
c34b1796 | 268 | if (result.len() as isize) == limit { |
1a4d82fc JJ |
269 | return result; |
270 | } | |
271 | bracket_count += match ts.tok { | |
272 | token::Lt => 1, | |
d9579d0f | 273 | token::Gt => -1, |
1a4d82fc JJ |
274 | token::BinOp(token::Shl) => 2, |
275 | token::BinOp(token::Shr) => -2, | |
e9174d1e | 276 | _ => 0, |
1a4d82fc | 277 | }; |
d9579d0f AL |
278 | |
279 | // Ignore the `>::` in `<Type as Trait>::AssocTy`. | |
280 | ||
281 | // The root cause of this hack is that the AST representation of | |
282 | // qpaths is horrible. It treats <A as B>::C as a path with two | |
283 | // segments, B and C and notes that there is also a self type A at | |
284 | // position 0. Because we don't have spans for individual idents, | |
285 | // only the whole path, we have to iterate over the tokens in the | |
286 | // path, trying to pull out the non-nested idents (e.g., avoiding 'a | |
287 | // in `<A as B<'a>>::C`). So we end up with a span for `B>::C` from | |
288 | // the start of the first ident to the end of the path. | |
289 | if !found_ufcs_sep && bracket_count == -1 { | |
290 | found_ufcs_sep = true; | |
291 | bracket_count += 1; | |
292 | } | |
c34b1796 | 293 | if ts.tok.is_ident() && bracket_count == nesting { |
1a4d82fc JJ |
294 | result.push(self.make_sub_span(span, Some(ts.sp)).unwrap()); |
295 | } | |
296 | } | |
297 | } | |
298 | ||
299 | pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option<Span> { | |
300 | let mut toks = self.retokenise_span(span); | |
301 | let mut prev = toks.real_token(); | |
302 | loop { | |
303 | if prev.tok == token::Eof { | |
304 | return None; | |
305 | } | |
306 | let next = toks.real_token(); | |
307 | if next.tok == tok { | |
308 | return self.make_sub_span(span, Some(prev.sp)); | |
309 | } | |
310 | prev = next; | |
311 | } | |
312 | } | |
313 | ||
314 | pub fn sub_span_of_token(&self, span: Span, tok: Token) -> Option<Span> { | |
315 | let mut toks = self.retokenise_span(span); | |
316 | loop { | |
317 | let next = toks.real_token(); | |
318 | if next.tok == token::Eof { | |
319 | return None; | |
320 | } | |
321 | if next.tok == tok { | |
322 | return self.make_sub_span(span, Some(next.sp)); | |
323 | } | |
324 | } | |
325 | } | |
326 | ||
e9174d1e | 327 | pub fn sub_span_after_keyword(&self, span: Span, keyword: keywords::Keyword) -> Option<Span> { |
85aaf69f SL |
328 | self.sub_span_after(span, |t| t.is_keyword(keyword)) |
329 | } | |
330 | ||
e9174d1e | 331 | pub fn sub_span_after_token(&self, span: Span, tok: Token) -> Option<Span> { |
85aaf69f SL |
332 | self.sub_span_after(span, |t| t == tok) |
333 | } | |
334 | ||
e9174d1e | 335 | fn sub_span_after<F: Fn(Token) -> bool>(&self, span: Span, f: F) -> Option<Span> { |
1a4d82fc JJ |
336 | let mut toks = self.retokenise_span(span); |
337 | loop { | |
338 | let ts = toks.real_token(); | |
339 | if ts.tok == token::Eof { | |
340 | return None; | |
341 | } | |
85aaf69f | 342 | if f(ts.tok) { |
1a4d82fc JJ |
343 | let ts = toks.real_token(); |
344 | if ts.tok == token::Eof { | |
345 | return None | |
346 | } else { | |
347 | return self.make_sub_span(span, Some(ts.sp)); | |
348 | } | |
349 | } | |
350 | } | |
351 | } | |
352 | ||
85aaf69f | 353 | |
d9579d0f | 354 | // Returns a list of the spans of idents in a path. |
1a4d82fc JJ |
355 | // E.g., For foo::bar<x,t>::baz, we return [foo, bar, baz] (well, their spans) |
356 | pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec<Span> { | |
1a4d82fc JJ |
357 | self.spans_with_brackets(path.span, 0, -1) |
358 | } | |
359 | ||
360 | // Return an owned vector of the subspans of the param identifier | |
361 | // tokens found in span. | |
c34b1796 | 362 | pub fn spans_for_ty_params(&self, span: Span, number: isize) -> Vec<Span> { |
1a4d82fc | 363 | // Type params are nested within one level of brackets: |
d9579d0f | 364 | // i.e. we want Vec<A, B> from Foo<A, B<T,U>> |
1a4d82fc JJ |
365 | self.spans_with_brackets(span, 1, number) |
366 | } | |
367 | ||
368 | pub fn report_span_err(&self, kind: &str, span: Span) { | |
369 | let loc = self.sess.codemap().lookup_char_pos(span.lo); | |
370 | info!("({}) Could not find sub_span in `{}` in {}, line {}", | |
b039eaaf SL |
371 | kind, |
372 | self.snippet(span), | |
373 | loc.file.name, | |
374 | loc.line); | |
375 | self.err_count.set(self.err_count.get() + 1); | |
1a4d82fc JJ |
376 | if self.err_count.get() > 1000 { |
377 | self.sess.bug("span errors reached 1000, giving up"); | |
378 | } | |
379 | } | |
7453a54e SL |
380 | |
381 | // Return the name for a macro definition (identifier after first `!`) | |
382 | pub fn span_for_macro_def_name(&self, span: Span) -> Option<Span> { | |
383 | let mut toks = self.retokenise_span(span); | |
384 | loop { | |
385 | let ts = toks.real_token(); | |
386 | if ts.tok == token::Eof { | |
387 | return None; | |
388 | } | |
389 | if ts.tok == token::Not { | |
390 | let ts = toks.real_token(); | |
391 | if ts.tok.is_ident() { | |
392 | return self.make_sub_span(span, Some(ts.sp)); | |
393 | } else { | |
394 | return None; | |
395 | } | |
396 | } | |
397 | } | |
398 | } | |
399 | ||
400 | // Return the name for a macro use (identifier before first `!`). | |
401 | pub fn span_for_macro_use_name(&self, span:Span) -> Option<Span> { | |
402 | let mut toks = self.retokenise_span(span); | |
403 | let mut prev = toks.real_token(); | |
404 | loop { | |
405 | if prev.tok == token::Eof { | |
406 | return None; | |
407 | } | |
408 | let ts = toks.real_token(); | |
409 | if ts.tok == token::Not { | |
410 | if prev.tok.is_ident() { | |
411 | return self.make_sub_span(span, Some(prev.sp)); | |
412 | } else { | |
413 | return None; | |
414 | } | |
415 | } | |
416 | prev = ts; | |
417 | } | |
418 | } | |
419 | ||
420 | /// Return true if the span is generated code, and | |
421 | /// it is not a subspan of the root callsite. | |
422 | /// | |
423 | /// Used to filter out spans of minimal value, | |
424 | /// such as references to macro internal variables. | |
425 | pub fn filter_generated(&self, sub_span: Option<Span>, parent: Span) -> bool { | |
426 | if !generated_code(parent) { | |
427 | if sub_span.is_none() { | |
428 | // Edge case - this occurs on generated code with incorrect expansion info. | |
429 | return true; | |
430 | } | |
431 | return false; | |
432 | } | |
433 | // If sub_span is none, filter out generated code. | |
434 | if sub_span.is_none() { | |
435 | return true; | |
436 | } | |
437 | ||
438 | //If the span comes from a fake filemap, filter it. | |
439 | if !self.sess.codemap().lookup_char_pos(parent.lo).file.is_real_file() { | |
440 | return true; | |
441 | } | |
442 | ||
443 | // Otherwise, a generated span is deemed invalid if it is not a sub-span of the root | |
444 | // callsite. This filters out macro internal variables and most malformed spans. | |
445 | let span = self.sess.codemap().source_callsite(parent); | |
446 | !(span.contains(parent)) | |
447 | } | |
448 | } | |
449 | ||
450 | macro_rules! filter { | |
451 | ($util: expr, $span: ident, $parent: expr, None) => { | |
452 | if $util.filter_generated($span, $parent) { | |
453 | return None; | |
454 | } | |
455 | }; | |
456 | ($util: expr, $span: ident, $parent: expr) => { | |
457 | if $util.filter_generated($span, $parent) { | |
458 | return; | |
459 | } | |
460 | }; | |
1a4d82fc | 461 | } |