]> git.proxmox.com Git - rustc.git/blob - src/librustc_save_analysis/span_utils.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / librustc_save_analysis / span_utils.rs
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 generated_code;
14
15 use std::cell::Cell;
16 use std::env;
17 use std::path::Path;
18
19 use syntax::parse::lexer::{self, StringReader};
20 use syntax::parse::token::{self, Token};
21 use syntax::symbol::keywords;
22 use syntax_pos::*;
23
24 #[derive(Clone)]
25 pub struct SpanUtils<'a> {
26 pub sess: &'a Session,
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.
29 pub err_count: Cell<isize>,
30 }
31
32 impl<'a> SpanUtils<'a> {
33 pub fn new(sess: &'a Session) -> SpanUtils<'a> {
34 SpanUtils {
35 sess,
36 err_count: Cell::new(0),
37 }
38 }
39
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()
46 .unwrap()
47 .join(&path)
48 .display()
49 .to_string()
50 }
51 }
52
53 pub fn snippet(&self, span: Span) -> String {
54 match self.sess.codemap().span_to_snippet(span) {
55 Ok(s) => s,
56 Err(_) => String::new(),
57 }
58 }
59
60 pub fn retokenise_span(&self, span: Span) -> StringReader<'a> {
61 lexer::StringReader::retokenize(&self.sess.parse_sess, span)
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);
69 let mut bracket_count = 0;
70 loop {
71 let ts = toks.real_token();
72 if ts.tok == token::Eof {
73 return result;
74 }
75 if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) {
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,
83 _ => 0,
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);
91 let mut bracket_count = 0;
92 loop {
93 let ts = toks.real_token();
94 if ts.tok == token::Eof {
95 return None;
96 }
97 if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) {
98 return Some(ts.sp);
99 }
100
101 bracket_count += match ts.tok {
102 token::Lt => 1,
103 token::Gt => -1,
104 token::BinOp(token::Shr) => -2,
105 _ => 0,
106 }
107 }
108 }
109
110 // Return the span for the last ident before a `<` and outside any
111 // angle brackets, or the last span.
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;
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;
125 let mut bracket_count = 0;
126 loop {
127 let next = toks.real_token();
128
129 if (next.tok == token::Lt || next.tok == token::Colon) && angle_count == 0
130 && bracket_count == 0 && prev.tok.is_ident()
131 {
132 result = Some(prev.sp);
133 }
134
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
145 bracket_count += match prev.tok {
146 token::OpenDelim(token::Bracket) => 1,
147 token::CloseDelim(token::Bracket) => -1,
148 _ => 0,
149 };
150
151 if next.tok == token::Eof {
152 break;
153 }
154 prev = next;
155 }
156 if angle_count != 0 || bracket_count != 0 {
157 let loc = self.sess.codemap().lookup_char_pos(span.lo());
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 );
166 }
167 if result.is_none() && prev.tok.is_ident() && angle_count == 0 {
168 return Some(prev.sp);
169 }
170 result
171 }
172
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 {
182 return Some(prev.sp);
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 {
196 return Some(next.sp);
197 }
198 }
199 }
200
201 pub fn sub_span_after_keyword(&self, span: Span, keyword: keywords::Keyword) -> Option<Span> {
202 self.sub_span_after(span, |t| t.is_keyword(keyword))
203 }
204
205 pub fn sub_span_after_token(&self, span: Span, tok: Token) -> Option<Span> {
206 self.sub_span_after(span, |t| t == tok)
207 }
208
209 fn sub_span_after<F: Fn(Token) -> bool>(&self, span: Span, f: F) -> Option<Span> {
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 }
216 if f(ts.tok) {
217 let ts = toks.real_token();
218 if ts.tok == token::Eof {
219 return None;
220 } else {
221 return Some(ts.sp);
222 }
223 }
224 }
225 }
226
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 // }
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.
280 let sub_span = match sub_span {
281 Some(ss) => ss,
282 None => return true,
283 };
284
285 //If the span comes from a fake filemap, filter it.
286 if !self.sess
287 .codemap()
288 .lookup_char_pos(parent.lo())
289 .file
290 .is_real_file()
291 {
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.
297 !parent.source_callsite().contains(sub_span)
298 }
299 }
300
301 macro_rules! filter {
302 ($util: expr, $span: expr, $parent: expr, None) => {
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 };
312 }