]> git.lizzy.rs Git - rust.git/blob - src/utils.rs
Handle pub(restricted) (#1013)
[rust.git] / src / utils.rs
1 // Copyright 2015 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 std::borrow::Cow;
12 use std::cmp::Ordering;
13
14 use itertools::Itertools;
15
16 use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItemKind, Path};
17 use syntax::codemap::{CodeMap, Span, BytePos};
18 use syntax::abi;
19
20 use Indent;
21 use comment::FindUncommented;
22 use rewrite::{Rewrite, RewriteContext};
23
24 use SKIP_ANNOTATION;
25
26 pub trait CodeMapSpanUtils {
27     fn span_after(&self, original: Span, needle: &str) -> BytePos;
28     fn span_after_last(&self, original: Span, needle: &str) -> BytePos;
29     fn span_before(&self, original: Span, needle: &str) -> BytePos;
30 }
31
32 impl CodeMapSpanUtils for CodeMap {
33     #[inline]
34     fn span_after(&self, original: Span, needle: &str) -> BytePos {
35         let snippet = self.span_to_snippet(original).unwrap();
36         let offset = snippet.find_uncommented(needle).unwrap() + needle.len();
37
38         original.lo + BytePos(offset as u32)
39     }
40
41     #[inline]
42     fn span_after_last(&self, original: Span, needle: &str) -> BytePos {
43         let snippet = self.span_to_snippet(original).unwrap();
44         let mut offset = 0;
45
46         while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) {
47             offset += additional_offset + needle.len();
48         }
49
50         original.lo + BytePos(offset as u32)
51     }
52
53     #[inline]
54     fn span_before(&self, original: Span, needle: &str) -> BytePos {
55         let snippet = self.span_to_snippet(original).unwrap();
56         let offset = snippet.find_uncommented(needle).unwrap();
57
58         original.lo + BytePos(offset as u32)
59     }
60 }
61
62 // Computes the length of a string's last line, minus offset.
63 #[inline]
64 pub fn extra_offset(text: &str, offset: Indent) -> usize {
65     match text.rfind('\n') {
66         // 1 for newline character
67         Some(idx) => text.len() - idx - 1 - offset.width(),
68         None => text.len(),
69     }
70 }
71
72 // Uses Cow to avoid allocating in the common cases.
73 pub fn format_visibility(vis: &Visibility) -> Cow<'static, str> {
74     match *vis {
75         Visibility::Public => Cow::from("pub "),
76         Visibility::Inherited => Cow::from(""),
77         Visibility::Crate(_) => Cow::from("pub(crate) "),
78         Visibility::Restricted { ref path, .. } => {
79             let Path { global, ref segments, .. } = **path;
80             let prefix = if global {
81                 "::"
82             } else {
83                 ""
84             };
85             let mut segments_iter = segments.iter().map(|seg| seg.identifier.name.as_str());
86
87             Cow::from(format!("pub({}{}) ", prefix, segments_iter.join("::")))
88         }
89     }
90 }
91
92 #[inline]
93 pub fn format_unsafety(unsafety: ast::Unsafety) -> &'static str {
94     match unsafety {
95         ast::Unsafety::Unsafe => "unsafe ",
96         ast::Unsafety::Normal => "",
97     }
98 }
99
100 #[inline]
101 pub fn format_mutability(mutability: ast::Mutability) -> &'static str {
102     match mutability {
103         ast::Mutability::Mutable => "mut ",
104         ast::Mutability::Immutable => "",
105     }
106 }
107
108 #[inline]
109 pub fn format_abi(abi: abi::Abi, explicit_abi: bool) -> String {
110     if abi == abi::Abi::C && !explicit_abi {
111         "extern ".into()
112     } else {
113         format!("extern {} ", abi)
114     }
115 }
116
117 // The width of the first line in s.
118 #[inline]
119 pub fn first_line_width(s: &str) -> usize {
120     match s.find('\n') {
121         Some(n) => n,
122         None => s.len(),
123     }
124 }
125
126 // The width of the last line in s.
127 #[inline]
128 pub fn last_line_width(s: &str) -> usize {
129     match s.rfind('\n') {
130         Some(n) => s.len() - n - 1,
131         None => s.len(),
132     }
133 }
134 #[inline]
135 pub fn trimmed_last_line_width(s: &str) -> usize {
136     match s.rfind('\n') {
137         Some(n) => s[(n + 1)..].trim().len(),
138         None => s.trim().len(),
139     }
140 }
141
142 #[inline]
143 fn is_skip(meta_item: &MetaItem) -> bool {
144     match meta_item.node {
145         MetaItemKind::Word(ref s) => *s == SKIP_ANNOTATION,
146         MetaItemKind::List(ref s, ref l) => *s == "cfg_attr" && l.len() == 2 && is_skip(&l[1]),
147         _ => false,
148     }
149 }
150
151 #[inline]
152 pub fn contains_skip(attrs: &[Attribute]) -> bool {
153     attrs.iter().any(|a| is_skip(&a.node.value))
154 }
155
156 // Find the end of a TyParam
157 #[inline]
158 pub fn end_typaram(typaram: &ast::TyParam) -> BytePos {
159     typaram.bounds
160         .last()
161         .map_or(typaram.span, |bound| {
162             match *bound {
163                 ast::RegionTyParamBound(ref lt) => lt.span,
164                 ast::TraitTyParamBound(ref prt, _) => prt.span,
165             }
166         })
167         .hi
168 }
169
170 #[inline]
171 pub fn semicolon_for_expr(expr: &ast::Expr) -> bool {
172     match expr.node {
173         ast::ExprKind::Ret(..) |
174         ast::ExprKind::Again(..) |
175         ast::ExprKind::Break(..) => true,
176         _ => false,
177     }
178 }
179
180 #[inline]
181 pub fn semicolon_for_stmt(stmt: &ast::Stmt) -> bool {
182     match stmt.node {
183         ast::StmtKind::Semi(ref expr, _) => {
184             match expr.node {
185                 ast::ExprKind::While(..) |
186                 ast::ExprKind::WhileLet(..) |
187                 ast::ExprKind::Loop(..) |
188                 ast::ExprKind::ForLoop(..) => false,
189                 _ => true,
190             }
191         }
192         ast::StmtKind::Expr(..) => false,
193         _ => true,
194     }
195 }
196
197 #[inline]
198 pub fn trim_newlines(input: &str) -> &str {
199     match input.find(|c| c != '\n' && c != '\r') {
200         Some(start) => {
201             let end = input.rfind(|c| c != '\n' && c != '\r').unwrap_or(0) + 1;
202             &input[start..end]
203         }
204         None => "",
205     }
206 }
207
208 // Macro for deriving implementations of Decodable for enums
209 #[macro_export]
210 macro_rules! impl_enum_decodable {
211     ( $e:ident, $( $x:ident ),* ) => {
212         impl ::rustc_serialize::Decodable for $e {
213             fn decode<D: ::rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
214                 use std::ascii::AsciiExt;
215                 let s = try!(d.read_str());
216                 $(
217                     if stringify!($x).eq_ignore_ascii_case(&s) {
218                       return Ok($e::$x);
219                     }
220                 )*
221                 Err(d.error("Bad variant"))
222             }
223         }
224
225         impl ::std::str::FromStr for $e {
226             type Err = &'static str;
227
228             fn from_str(s: &str) -> Result<Self, Self::Err> {
229                 use std::ascii::AsciiExt;
230                 $(
231                     if stringify!($x).eq_ignore_ascii_case(s) {
232                         return Ok($e::$x);
233                     }
234                 )*
235                 Err("Bad variant")
236             }
237         }
238
239         impl ::config::ConfigType for $e {
240             fn doc_hint() -> String {
241                 let mut variants = Vec::new();
242                 $(
243                     variants.push(stringify!($x));
244                 )*
245                 format!("[{}]", variants.join("|"))
246             }
247         }
248     };
249 }
250
251 // Same as try!, but for Option
252 #[macro_export]
253 macro_rules! try_opt {
254     ($expr:expr) => (match $expr {
255         Some(val) => val,
256         None => { return None; }
257     })
258 }
259
260 macro_rules! msg {
261     ($($arg:tt)*) => (
262         match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
263             Ok(_) => {},
264             Err(x) => panic!("Unable to write to stderr: {}", x),
265         }
266     )
267 }
268
269
270 // Wraps string-like values in an Option. Returns Some when the string adheres
271 // to the Rewrite constraints defined for the Rewrite trait and else otherwise.
272 pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: Indent) -> Option<S> {
273     {
274         let snippet = s.as_ref();
275
276         if !snippet.contains('\n') && snippet.len() > width {
277             return None;
278         } else {
279             let mut lines = snippet.lines();
280
281             // The caller of this function has already placed `offset`
282             // characters on the first line.
283             let first_line_max_len = try_opt!(max_width.checked_sub(offset.width()));
284             if lines.next().unwrap().len() > first_line_max_len {
285                 return None;
286             }
287
288             // The other lines must fit within the maximum width.
289             if lines.find(|line| line.len() > max_width).is_some() {
290                 return None;
291             }
292
293             // `width` is the maximum length of the last line, excluding
294             // indentation.
295             // A special check for the last line, since the caller may
296             // place trailing characters on this line.
297             if snippet.lines().rev().next().unwrap().len() > offset.width() + width {
298                 return None;
299             }
300         }
301     }
302
303     Some(s)
304 }
305
306 impl Rewrite for String {
307     fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
308         wrap_str(self, context.config.max_width, width, offset).map(ToOwned::to_owned)
309     }
310 }
311
312 // Binary search in integer range. Returns the first Ok value returned by the
313 // callback.
314 // The callback takes an integer and returns either an Ok, or an Err indicating
315 // whether the `guess' was too high (Ordering::Less), or too low.
316 // This function is guaranteed to try to the hi value first.
317 pub fn binary_search<C, T>(mut lo: usize, mut hi: usize, callback: C) -> Option<T>
318     where C: Fn(usize) -> Result<T, Ordering>
319 {
320     let mut middle = hi;
321
322     while lo <= hi {
323         match callback(middle) {
324             Ok(val) => return Some(val),
325             Err(Ordering::Less) => {
326                 hi = middle - 1;
327             }
328             Err(..) => {
329                 lo = middle + 1;
330             }
331         }
332         middle = (hi + lo) / 2;
333     }
334
335     None
336 }
337
338 #[test]
339 fn bin_search_test() {
340     let closure = |i| {
341         match i {
342             4 => Ok(()),
343             j if j > 4 => Err(Ordering::Less),
344             j if j < 4 => Err(Ordering::Greater),
345             _ => unreachable!(),
346         }
347     };
348
349     assert_eq!(Some(()), binary_search(1, 10, &closure));
350     assert_eq!(None, binary_search(1, 3, &closure));
351     assert_eq!(Some(()), binary_search(0, 44, &closure));
352     assert_eq!(Some(()), binary_search(4, 125, &closure));
353     assert_eq!(None, binary_search(6, 100, &closure));
354 }
355
356 pub fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr {
357     match e.node {
358         ast::ExprKind::InPlace(ref e, _) |
359         ast::ExprKind::Call(ref e, _) |
360         ast::ExprKind::Binary(_, ref e, _) |
361         ast::ExprKind::Cast(ref e, _) |
362         ast::ExprKind::Type(ref e, _) |
363         ast::ExprKind::Assign(ref e, _) |
364         ast::ExprKind::AssignOp(_, ref e, _) |
365         ast::ExprKind::Field(ref e, _) |
366         ast::ExprKind::TupField(ref e, _) |
367         ast::ExprKind::Index(ref e, _) |
368         ast::ExprKind::Range(Some(ref e), _, _) => left_most_sub_expr(e),
369         // FIXME needs Try in Syntex
370         // ast::ExprKind::Try(ref f) => left_most_sub_expr(e),
371         _ => e,
372     }
373 }