]> git.lizzy.rs Git - rust.git/blob - src/imports.rs
Fix libsyntax updates
[rust.git] / src / imports.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::cmp::Ordering;
12
13 use config::lists::*;
14 use syntax::ast;
15 use syntax::codemap::{BytePos, Span};
16
17 use codemap::SpanUtils;
18 use config::IndentStyle;
19 use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
20 use rewrite::{Rewrite, RewriteContext};
21 use shape::Shape;
22 use types::{rewrite_path, PathContext};
23 use utils::{format_visibility, mk_sp};
24 use visitor::FmtVisitor;
25
26 /// Returns a name imported by a `use` declaration. e.g. returns `Ordering`
27 /// for `std::cmp::Ordering` and `self` for `std::cmp::self`.
28 pub fn path_to_imported_ident(path: &ast::Path) -> ast::Ident {
29     path.segments.last().unwrap().identifier
30 }
31
32 pub fn same_rename(opt_ident: &Option<ast::Ident>, path: &ast::Path) -> bool {
33     opt_ident.map_or(true, |ident| path_to_imported_ident(path) == ident)
34 }
35
36 fn rewrite_prefix(path: &ast::Path, context: &RewriteContext, shape: Shape) -> Option<String> {
37     if path.segments.len() > 1 && path_to_imported_ident(path).to_string() == "self" {
38         let path = &ast::Path {
39             span: path.span,
40             segments: path.segments[..path.segments.len() - 1].to_owned(),
41         };
42         rewrite_path(context, PathContext::Import, None, path, shape)
43     } else {
44         rewrite_path(context, PathContext::Import, None, path, shape)
45     }
46 }
47
48 impl Rewrite for ast::UseTree {
49     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
50         match self.kind {
51             ast::UseTreeKind::Nested(ref items) => {
52                 rewrite_nested_use_tree(shape, &self.prefix, items, self.span, context)
53             }
54             ast::UseTreeKind::Glob => {
55                 let prefix_shape = shape.sub_width(3)?;
56
57                 if !self.prefix.segments.is_empty() {
58                     let path_str = rewrite_prefix(&self.prefix, context, prefix_shape)?;
59                     Some(format!("{}::*", path_str))
60                 } else {
61                     Some("*".to_owned())
62                 }
63             }
64             ast::UseTreeKind::Simple(opt_ident) => {
65                 if same_rename(&opt_ident, &self.prefix) {
66                     rewrite_prefix(&self.prefix, context, shape)
67                         .or_else(|| Some(context.snippet(self.prefix.span).to_owned()))
68                 } else {
69                     let ident_str = opt_ident?.to_string();
70                     // 4 = " as ".len()
71                     let prefix_shape = shape.sub_width(ident_str.len() + 4)?;
72                     let path_str = rewrite_prefix(&self.prefix, context, prefix_shape)
73                         .unwrap_or_else(|| context.snippet(self.prefix.span).to_owned());
74                     Some(format!("{} as {}", path_str, ident_str))
75                 }
76             }
77         }
78     }
79 }
80
81 fn is_unused_import(tree: &ast::UseTree, attrs: &[ast::Attribute]) -> bool {
82     attrs.is_empty() && is_unused_import_inner(tree)
83 }
84
85 fn is_unused_import_inner(tree: &ast::UseTree) -> bool {
86     match tree.kind {
87         ast::UseTreeKind::Nested(ref items) => match items.len() {
88             0 => true,
89             1 => is_unused_import_inner(&items[0].0),
90             _ => false,
91         },
92         _ => false,
93     }
94 }
95
96 // Rewrite `use foo;` WITHOUT attributes.
97 pub fn rewrite_import(
98     context: &RewriteContext,
99     vis: &ast::Visibility,
100     tree: &ast::UseTree,
101     attrs: &[ast::Attribute],
102     shape: Shape,
103 ) -> Option<String> {
104     let vis = format_visibility(vis);
105     // 4 = `use `, 1 = `;`
106     let rw = shape
107         .offset_left(vis.len() + 4)
108         .and_then(|shape| shape.sub_width(1))
109         .and_then(|shape| {
110             // If we have an empty nested group with no attributes, we erase it
111             if is_unused_import(tree, attrs) {
112                 Some("".to_owned())
113             } else {
114                 tree.rewrite(context, shape)
115             }
116         });
117     match rw {
118         Some(ref s) if !s.is_empty() => Some(format!("{}use {};", vis, s)),
119         _ => rw,
120     }
121 }
122
123 impl<'a> FmtVisitor<'a> {
124     pub fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) {
125         let span = item.span;
126         let shape = self.shape();
127         let rw = rewrite_import(&self.get_context(), &item.vis, tree, &item.attrs, shape);
128         match rw {
129             Some(ref s) if s.is_empty() => {
130                 // Format up to last newline
131                 let prev_span = mk_sp(self.last_pos, source!(self, span).lo());
132                 let trimmed_snippet = self.snippet(prev_span).trim_right();
133                 let span_end = self.last_pos + BytePos(trimmed_snippet.len() as u32);
134                 self.format_missing(span_end);
135                 // We have an excessive newline from the removed import.
136                 if self.buffer.ends_with('\n') {
137                     self.buffer.pop();
138                     self.line_number -= 1;
139                 }
140                 self.last_pos = source!(self, span).hi();
141             }
142             Some(ref s) => {
143                 self.format_missing_with_indent(source!(self, span).lo());
144                 self.push_str(s);
145                 self.last_pos = source!(self, span).hi();
146             }
147             None => {
148                 self.format_missing_with_indent(source!(self, span).lo());
149                 self.format_missing(source!(self, span).hi());
150             }
151         }
152     }
153 }
154
155 fn rewrite_nested_use_tree_single(
156     context: &RewriteContext,
157     path_str: &str,
158     tree: &ast::UseTree,
159     shape: Shape,
160 ) -> Option<String> {
161     match tree.kind {
162         ast::UseTreeKind::Simple(opt_rename) => {
163             let mut item_str = rewrite_prefix(&tree.prefix, context, shape)?;
164             if item_str == "self" {
165                 item_str = "".to_owned();
166             }
167
168             let path_item_str = if path_str.is_empty() {
169                 if item_str.is_empty() {
170                     "self".to_owned()
171                 } else {
172                     item_str
173                 }
174             } else if item_str.is_empty() {
175                 path_str.to_owned()
176             } else {
177                 format!("{}::{}", path_str, item_str)
178             };
179
180             Some(if same_rename(&opt_rename, &tree.prefix) {
181                 path_item_str
182             } else {
183                 format!("{} as {}", path_item_str, opt_rename?)
184             })
185         }
186         ast::UseTreeKind::Glob | ast::UseTreeKind::Nested(..) => {
187             // 2 = "::"
188             let nested_shape = shape.offset_left(path_str.len() + 2)?;
189             tree.rewrite(context, nested_shape)
190                 .map(|item| format!("{}::{}", path_str, item))
191         }
192     }
193 }
194
195 #[derive(Eq, PartialEq)]
196 enum ImportItem<'a> {
197     // `self` or `self as a`
198     SelfImport(&'a str),
199     // name_one, name_two, ...
200     SnakeCase(&'a str),
201     // NameOne, NameTwo, ...
202     CamelCase(&'a str),
203     // NAME_ONE, NAME_TWO, ...
204     AllCaps(&'a str),
205     // Failed to format the import item
206     Invalid,
207 }
208
209 impl<'a> ImportItem<'a> {
210     fn from_str(s: &str) -> ImportItem {
211         if s == "self" || s.starts_with("self as") {
212             ImportItem::SelfImport(s)
213         } else if s.chars().all(|c| c.is_lowercase() || c == '_' || c == ' ') {
214             ImportItem::SnakeCase(s)
215         } else if s.chars().all(|c| c.is_uppercase() || c == '_' || c == ' ') {
216             ImportItem::AllCaps(s)
217         } else {
218             ImportItem::CamelCase(s)
219         }
220     }
221
222     fn from_opt_str(s: Option<&String>) -> ImportItem {
223         s.map_or(ImportItem::Invalid, |s| ImportItem::from_str(s))
224     }
225
226     fn to_str(&self) -> Option<&str> {
227         match *self {
228             ImportItem::SelfImport(s)
229             | ImportItem::SnakeCase(s)
230             | ImportItem::CamelCase(s)
231             | ImportItem::AllCaps(s) => Some(s),
232             ImportItem::Invalid => None,
233         }
234     }
235
236     fn to_u32(&self) -> u32 {
237         match *self {
238             ImportItem::SelfImport(..) => 0,
239             ImportItem::SnakeCase(..) => 1,
240             ImportItem::CamelCase(..) => 2,
241             ImportItem::AllCaps(..) => 3,
242             ImportItem::Invalid => 4,
243         }
244     }
245 }
246
247 impl<'a> PartialOrd for ImportItem<'a> {
248     fn partial_cmp(&self, other: &ImportItem<'a>) -> Option<Ordering> {
249         Some(self.cmp(other))
250     }
251 }
252
253 impl<'a> Ord for ImportItem<'a> {
254     fn cmp(&self, other: &ImportItem<'a>) -> Ordering {
255         let res = self.to_u32().cmp(&other.to_u32());
256         if res != Ordering::Equal {
257             return res;
258         }
259         self.to_str().map_or(Ordering::Greater, |self_str| {
260             other
261                 .to_str()
262                 .map_or(Ordering::Less, |other_str| self_str.cmp(other_str))
263         })
264     }
265 }
266
267 // Pretty prints a multi-item import.
268 // If the path list is empty, it leaves the braces empty.
269 fn rewrite_nested_use_tree(
270     shape: Shape,
271     path: &ast::Path,
272     trees: &[(ast::UseTree, ast::NodeId)],
273     span: Span,
274     context: &RewriteContext,
275 ) -> Option<String> {
276     // Returns a different option to distinguish `::foo` and `foo`
277     let path_str = rewrite_path(context, PathContext::Import, None, path, shape)?;
278
279     match trees.len() {
280         0 => {
281             let shape = shape.offset_left(path_str.len() + 3)?;
282             return rewrite_path(context, PathContext::Import, None, path, shape)
283                 .map(|path_str| format!("{}::{{}}", path_str));
284         }
285         1 => {
286             return rewrite_nested_use_tree_single(context, &path_str, &trees[0].0, shape);
287         }
288         _ => (),
289     }
290
291     let path_str = if path_str.is_empty() {
292         path_str
293     } else {
294         format!("{}::", path_str)
295     };
296
297     // 2 = "{}"
298     let remaining_width = shape.width.checked_sub(path_str.len() + 2).unwrap_or(0);
299     let nested_indent = match context.config.imports_indent() {
300         IndentStyle::Block => shape.indent.block_indent(context.config),
301         // 1 = `{`
302         IndentStyle::Visual => shape.visual_indent(path_str.len() + 1).indent,
303     };
304
305     let nested_shape = match context.config.imports_indent() {
306         IndentStyle::Block => Shape::indented(nested_indent, context.config).sub_width(1)?,
307         IndentStyle::Visual => Shape::legacy(remaining_width, nested_indent),
308     };
309
310     let mut items = {
311         // Dummy value, see explanation below.
312         let mut items = vec![ListItem::from_str("")];
313         let iter = itemize_list(
314             context.snippet_provider,
315             trees.iter().map(|tree| &tree.0),
316             "}",
317             ",",
318             |tree| tree.span.lo(),
319             |tree| tree.span.hi(),
320             |tree| tree.rewrite(context, nested_shape),
321             context.snippet_provider.span_after(span, "{"),
322             span.hi(),
323             false,
324         );
325         items.extend(iter);
326         items
327     };
328
329     // We prefixed the item list with a dummy value so that we can
330     // potentially move "self" to the front of the vector without touching
331     // the rest of the items.
332     let has_self = move_self_to_front(&mut items);
333     let first_index = if has_self { 0 } else { 1 };
334
335     if context.config.reorder_imported_names() {
336         items[1..].sort_by(|a, b| {
337             let a = ImportItem::from_opt_str(a.item.as_ref());
338             let b = ImportItem::from_opt_str(b.item.as_ref());
339             a.cmp(&b)
340         });
341     }
342
343     let tactic = definitive_tactic(
344         &items[first_index..],
345         context.config.imports_layout(),
346         Separator::Comma,
347         remaining_width,
348     );
349
350     let ends_with_newline = context.config.imports_indent() == IndentStyle::Block
351         && tactic != DefinitiveListTactic::Horizontal;
352
353     let fmt = ListFormatting {
354         tactic,
355         separator: ",",
356         trailing_separator: if ends_with_newline {
357             context.config.trailing_comma()
358         } else {
359             SeparatorTactic::Never
360         },
361         separator_place: SeparatorPlace::Back,
362         shape: nested_shape,
363         ends_with_newline,
364         preserve_newline: true,
365         config: context.config,
366     };
367     let list_str = write_list(&items[first_index..], &fmt)?;
368
369     let result = if list_str.contains('\n') && context.config.imports_indent() == IndentStyle::Block
370     {
371         format!(
372             "{}{{\n{}{}\n{}}}",
373             path_str,
374             nested_shape.indent.to_string(context.config),
375             list_str,
376             shape.indent.to_string(context.config)
377         )
378     } else {
379         format!("{}{{{}}}", path_str, list_str)
380     };
381     Some(result)
382 }
383
384 // Returns true when self item was found.
385 fn move_self_to_front(items: &mut Vec<ListItem>) -> bool {
386     match items
387         .iter()
388         .position(|item| item.item.as_ref().map(|x| &x[..]) == Some("self"))
389     {
390         Some(pos) => {
391             items[0] = items.remove(pos);
392             true
393         }
394         None => false,
395     }
396 }