// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use rustc::session::Session; use generated_code; use std::cell::Cell; use std::env; use syntax::parse::lexer::{self, StringReader}; use syntax::parse::token::{self, Token}; use syntax::symbol::keywords; use syntax_pos::*; #[derive(Clone)] pub struct SpanUtils<'a> { pub sess: &'a Session, // FIXME given that we clone SpanUtils all over the place, this err_count is // probably useless and any logic relying on it is bogus. pub err_count: Cell, } impl<'a> SpanUtils<'a> { pub fn new(sess: &'a Session) -> SpanUtils<'a> { SpanUtils { sess, err_count: Cell::new(0), } } pub fn make_path_string(path: &FileName) -> String { match *path { FileName::Real(ref path) if !path.is_absolute() => env::current_dir() .unwrap() .join(&path) .display() .to_string(), _ => path.to_string(), } } pub fn snippet(&self, span: Span) -> String { match self.sess.codemap().span_to_snippet(span) { Ok(s) => s, Err(_) => String::new(), } } pub fn retokenise_span(&self, span: Span) -> StringReader<'a> { lexer::StringReader::retokenize(&self.sess.parse_sess, span) } // Re-parses a path and returns the span for the last identifier in the path pub fn span_for_last_ident(&self, span: Span) -> Option { let mut result = None; let mut toks = self.retokenise_span(span); let mut bracket_count = 0; loop { let ts = toks.real_token(); if ts.tok == token::Eof { return result; } if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) { result = Some(ts.sp); } bracket_count += match ts.tok { token::Lt => 1, token::Gt => -1, token::BinOp(token::Shr) => -2, _ => 0, } } } // Return the span for the first identifier in the path. pub fn span_for_first_ident(&self, span: Span) -> Option { let mut toks = self.retokenise_span(span); let mut bracket_count = 0; loop { let ts = toks.real_token(); if ts.tok == token::Eof { return None; } if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) { return Some(ts.sp); } bracket_count += match ts.tok { token::Lt => 1, token::Gt => -1, token::BinOp(token::Shr) => -2, _ => 0, } } } // Return the span for the last ident before a `<` and outside any // angle brackets, or the last span. pub fn sub_span_for_type_name(&self, span: Span) -> Option { let mut toks = self.retokenise_span(span); let mut prev = toks.real_token(); let mut result = None; // We keep track of the following two counts - the depth of nesting of // angle brackets, and the depth of nesting of square brackets. For the // angle bracket count, we only count tokens which occur outside of any // square brackets (i.e. bracket_count == 0). The intutition here is // that we want to count angle brackets in the type, but not any which // could be in expression context (because these could mean 'less than', // etc.). let mut angle_count = 0; let mut bracket_count = 0; loop { let next = toks.real_token(); if (next.tok == token::Lt || next.tok == token::Colon) && angle_count == 0 && bracket_count == 0 && prev.tok.is_ident() { result = Some(prev.sp); } if bracket_count == 0 { angle_count += match prev.tok { token::Lt => 1, token::Gt => -1, token::BinOp(token::Shl) => 2, token::BinOp(token::Shr) => -2, _ => 0, }; } bracket_count += match prev.tok { token::OpenDelim(token::Bracket) => 1, token::CloseDelim(token::Bracket) => -1, _ => 0, }; if next.tok == token::Eof { break; } prev = next; } if angle_count != 0 || bracket_count != 0 { let loc = self.sess.codemap().lookup_char_pos(span.lo()); span_bug!( span, "Mis-counted brackets when breaking path? Parsing '{}' \ in {}, line {}", self.snippet(span), loc.file.name, loc.line ); } if result.is_none() && prev.tok.is_ident() && angle_count == 0 { return Some(prev.sp); } result } pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option { let mut toks = self.retokenise_span(span); let mut prev = toks.real_token(); loop { if prev.tok == token::Eof { return None; } let next = toks.real_token(); if next.tok == tok { return Some(prev.sp); } prev = next; } } pub fn sub_span_of_token(&self, span: Span, tok: Token) -> Option { let mut toks = self.retokenise_span(span); loop { let next = toks.real_token(); if next.tok == token::Eof { return None; } if next.tok == tok { return Some(next.sp); } } } pub fn sub_span_after_keyword(&self, span: Span, keyword: keywords::Keyword) -> Option { self.sub_span_after(span, |t| t.is_keyword(keyword)) } pub fn sub_span_after_token(&self, span: Span, tok: Token) -> Option { self.sub_span_after(span, |t| t == tok) } fn sub_span_after bool>(&self, span: Span, f: F) -> Option { let mut toks = self.retokenise_span(span); loop { let ts = toks.real_token(); if ts.tok == token::Eof { return None; } if f(ts.tok) { let ts = toks.real_token(); if ts.tok == token::Eof { return None; } else { return Some(ts.sp); } } } } // // Return the name for a macro definition (identifier after first `!`) // pub fn span_for_macro_def_name(&self, span: Span) -> Option { // let mut toks = self.retokenise_span(span); // loop { // let ts = toks.real_token(); // if ts.tok == token::Eof { // return None; // } // if ts.tok == token::Not { // let ts = toks.real_token(); // if ts.tok.is_ident() { // return Some(ts.sp); // } else { // return None; // } // } // } // } // // Return the name for a macro use (identifier before first `!`). // pub fn span_for_macro_use_name(&self, span:Span) -> Option { // let mut toks = self.retokenise_span(span); // let mut prev = toks.real_token(); // loop { // if prev.tok == token::Eof { // return None; // } // let ts = toks.real_token(); // if ts.tok == token::Not { // if prev.tok.is_ident() { // return Some(prev.sp); // } else { // return None; // } // } // prev = ts; // } // } /// Return true if the span is generated code, and /// it is not a subspan of the root callsite. /// /// Used to filter out spans of minimal value, /// such as references to macro internal variables. pub fn filter_generated(&self, sub_span: Option, parent: Span) -> bool { if !generated_code(parent) { if sub_span.is_none() { // Edge case - this occurs on generated code with incorrect expansion info. return true; } return false; } // If sub_span is none, filter out generated code. let sub_span = match sub_span { Some(ss) => ss, None => return true, }; //If the span comes from a fake filemap, filter it. if !self.sess .codemap() .lookup_char_pos(parent.lo()) .file .is_real_file() { return true; } // Otherwise, a generated span is deemed invalid if it is not a sub-span of the root // callsite. This filters out macro internal variables and most malformed spans. !parent.source_callsite().contains(sub_span) } } macro_rules! filter { ($util: expr, $span: expr, $parent: expr, None) => { if $util.filter_generated($span, $parent) { return None; } }; ($util: expr, $span: ident, $parent: expr) => { if $util.filter_generated($span, $parent) { return; } }; }