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.
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.
11 use rustc::session::Session;
13 use save::generated_code;
18 use syntax::codemap::*;
19 use syntax::parse::lexer;
20 use syntax::parse::lexer::{Reader,StringReader};
21 use syntax::parse::token;
22 use syntax::parse::token::{keywords, Token};
24 pub struct SpanUtils<'a> {
25 pub sess: &'a Session,
26 pub err_count: Cell<int>,
29 impl<'a> SpanUtils<'a> {
30 // Standard string for extents/location.
31 pub fn extent_str(&self, span: Span) -> String {
32 let lo_loc = self.sess.codemap().lookup_char_pos(span.lo);
33 let hi_loc = self.sess.codemap().lookup_char_pos(span.hi);
34 let lo_pos = self.sess.codemap().bytepos_to_file_charpos(span.lo);
35 let hi_pos = self.sess.codemap().bytepos_to_file_charpos(span.hi);
36 let lo_pos_byte = self.sess.codemap().lookup_byte_offset(span.lo).pos;
37 let hi_pos_byte = self.sess.codemap().lookup_byte_offset(span.hi).pos;
39 format!("file_name,{},file_line,{},file_col,{},extent_start,{},extent_start_bytes,{},\
40 file_line_end,{},file_col_end,{},extent_end,{},extent_end_bytes,{}",
42 lo_loc.line, lo_loc.col.to_uint(), lo_pos.to_uint(), lo_pos_byte.to_uint(),
43 hi_loc.line, hi_loc.col.to_uint(), hi_pos.to_uint(), hi_pos_byte.to_uint())
46 // sub_span starts at span.lo, so we need to adjust the positions etc.
47 // If sub_span is None, we don't need to adjust.
48 pub fn make_sub_span(&self, span: Span, sub_span: Option<Span>) -> Option<Span> {
49 let loc = self.sess.codemap().lookup_char_pos(span.lo);
50 assert!(!generated_code(span),
51 "generated code; we should not be processing this `{}` in {}, line {}",
52 self.snippet(span), loc.file.name, loc.line);
57 let FileMapAndBytePos {fm, pos} =
58 self.sess.codemap().lookup_byte_offset(span.lo);
59 let base = pos + fm.start_pos;
61 lo: base + self.sess.codemap().lookup_byte_offset(sub.lo).pos,
62 hi: base + self.sess.codemap().lookup_byte_offset(sub.hi).pos,
63 expn_id: NO_EXPANSION,
69 pub fn snippet(&self, span: Span) -> String {
70 match self.sess.codemap().span_to_snippet(span) {
72 None => String::new(),
76 pub fn retokenise_span(&self, span: Span) -> StringReader<'a> {
77 // sadness - we don't have spans for sub-expressions nor access to the tokens
78 // so in order to get extents for the function name itself (which dxr expects)
79 // we need to re-tokenise the fn definition
81 // Note: this is a bit awful - it adds the contents of span to the end of
82 // the codemap as a new filemap. This is mostly OK, but means we should
83 // not iterate over the codemap. Also, any spans over the new filemap
84 // are incompatible with spans over other filemaps.
85 let filemap = self.sess.codemap().new_filemap(String::from_str("<anon-dxr>"),
88 lexer::StringReader::new(s.diagnostic(), filemap)
91 // Re-parses a path and returns the span for the last identifier in the path
92 pub fn span_for_last_ident(&self, span: Span) -> Option<Span> {
93 let mut result = None;
95 let mut toks = self.retokenise_span(span);
96 let mut bracket_count = 0u;
98 let ts = toks.real_token();
99 if ts.tok == token::Eof {
100 return self.make_sub_span(span, result)
102 if bracket_count == 0 &&
103 (ts.tok.is_ident() || ts.tok.is_keyword(keywords::Self)) {
104 result = Some(ts.sp);
107 bracket_count += match ts.tok {
110 token::BinOp(token::Shr) => -2,
116 // Return the span for the first identifier in the path.
117 pub fn span_for_first_ident(&self, span: Span) -> Option<Span> {
118 let mut toks = self.retokenise_span(span);
119 let mut bracket_count = 0u;
121 let ts = toks.real_token();
122 if ts.tok == token::Eof {
125 if bracket_count == 0 &&
126 (ts.tok.is_ident() || ts.tok.is_keyword(keywords::Self)) {
127 return self.make_sub_span(span, Some(ts.sp));
130 bracket_count += match ts.tok {
133 token::BinOp(token::Shr) => -2,
139 // Return the span for the last ident before a `(` or `<` or '::<' and outside any
140 // any brackets, or the last span.
141 pub fn sub_span_for_meth_name(&self, span: Span) -> Option<Span> {
142 let mut toks = self.retokenise_span(span);
143 let mut prev = toks.real_token();
144 let mut result = None;
145 let mut bracket_count = 0u;
146 let mut last_span = None;
147 while prev.tok != token::Eof {
149 let mut next = toks.real_token();
151 if (next.tok == token::OpenDelim(token::Paren) ||
152 next.tok == token::Lt) &&
153 bracket_count == 0 &&
154 prev.tok.is_ident() {
155 result = Some(prev.sp);
158 if bracket_count == 0 &&
159 next.tok == token::ModSep {
162 next = toks.real_token();
163 if next.tok == token::Lt &&
165 result = Some(old.sp);
169 bracket_count += match prev.tok {
170 token::OpenDelim(token::Paren) | token::Lt => 1,
171 token::CloseDelim(token::Paren) | token::Gt => -1,
172 token::BinOp(token::Shr) => -2,
176 if prev.tok.is_ident() && bracket_count == 0 {
177 last_span = Some(prev.sp);
181 if result.is_none() && last_span.is_some() {
182 return self.make_sub_span(span, last_span);
184 return self.make_sub_span(span, result);
187 // Return the span for the last ident before a `<` and outside any
188 // brackets, or the last span.
189 pub fn sub_span_for_type_name(&self, span: Span) -> Option<Span> {
190 let mut toks = self.retokenise_span(span);
191 let mut prev = toks.real_token();
192 let mut result = None;
193 let mut bracket_count = 0u;
195 let next = toks.real_token();
197 if (next.tok == token::Lt ||
198 next.tok == token::Colon) &&
199 bracket_count == 0 &&
200 prev.tok.is_ident() {
201 result = Some(prev.sp);
204 bracket_count += match prev.tok {
207 token::BinOp(token::Shr) => -2,
211 if next.tok == token::Eof {
216 if bracket_count != 0 {
217 let loc = self.sess.codemap().lookup_char_pos(span.lo);
218 self.sess.span_bug(span,
219 format!("Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}",
220 self.snippet(span), loc.file.name, loc.line).as_slice());
222 if result.is_none() && prev.tok.is_ident() && bracket_count == 0 {
223 return self.make_sub_span(span, Some(prev.sp));
225 self.make_sub_span(span, result)
228 // Reparse span and return an owned vector of sub spans of the first limit
229 // identifier tokens in the given nesting level.
230 // example with Foo<Bar<T,V>, Bar<T,V>>
231 // Nesting = 0: all idents outside of brackets: ~[Foo]
232 // Nesting = 1: idents within one level of brackets: ~[Bar, Bar]
233 pub fn spans_with_brackets(&self, span: Span, nesting: int, limit: int) -> Vec<Span> {
234 let mut result: Vec<Span> = vec!();
236 let mut toks = self.retokenise_span(span);
237 // We keep track of how many brackets we're nested in
238 let mut bracket_count = 0i;
240 let ts = toks.real_token();
241 if ts.tok == token::Eof {
242 if bracket_count != 0 {
243 let loc = self.sess.codemap().lookup_char_pos(span.lo);
244 self.sess.span_bug(span, format!(
245 "Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}",
246 self.snippet(span), loc.file.name, loc.line).as_slice());
250 if (result.len() as int) == limit {
253 bracket_count += match ts.tok {
256 token::BinOp(token::Shl) => 2,
257 token::BinOp(token::Shr) => -2,
260 if ts.tok.is_ident() &&
261 bracket_count == nesting {
262 result.push(self.make_sub_span(span, Some(ts.sp)).unwrap());
267 pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option<Span> {
268 let mut toks = self.retokenise_span(span);
269 let mut prev = toks.real_token();
271 if prev.tok == token::Eof {
274 let next = toks.real_token();
276 return self.make_sub_span(span, Some(prev.sp));
282 pub fn sub_span_after_keyword(&self,
284 keyword: keywords::Keyword) -> Option<Span> {
285 let mut toks = self.retokenise_span(span);
287 let ts = toks.real_token();
288 if ts.tok == token::Eof {
291 if ts.tok.is_keyword(keyword) {
292 let ts = toks.real_token();
293 if ts.tok == token::Eof {
296 return self.make_sub_span(span, Some(ts.sp));
302 // Returns a list of the spans of idents in a patch.
303 // E.g., For foo::bar<x,t>::baz, we return [foo, bar, baz] (well, their spans)
304 pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec<Span> {
305 if generated_code(path.span) {
309 self.spans_with_brackets(path.span, 0, -1)
312 // Return an owned vector of the subspans of the param identifier
313 // tokens found in span.
314 pub fn spans_for_ty_params(&self, span: Span, number: int) -> Vec<Span> {
315 if generated_code(span) {
318 // Type params are nested within one level of brackets:
319 // i.e. we want ~[A, B] from Foo<A, B<T,U>>
320 self.spans_with_brackets(span, 1, number)
323 pub fn report_span_err(&self, kind: &str, span: Span) {
324 let loc = self.sess.codemap().lookup_char_pos(span.lo);
325 info!("({}) Could not find sub_span in `{}` in {}, line {}",
326 kind, self.snippet(span), loc.file.name, loc.line);
327 self.err_count.set(self.err_count.get()+1);
328 if self.err_count.get() > 1000 {
329 self.sess.bug("span errors reached 1000, giving up");