X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fimports.rs;h=9fd844a2c35b6ceec489b43dec87a990969d7c22;hb=a159b64b0aea1b09ec0d677acebd3f666426e6bc;hp=a66bcbd3e94e7ea89b33dc0e9b87185ed0ccbadd;hpb=45629dbdd60dc152e6a8d7beef92203770535742;p=rust.git diff --git a/src/imports.rs b/src/imports.rs index a66bcbd3e94..9fd844a2c35 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -12,9 +12,10 @@ use config::lists::*; use syntax::ast::{self, UseTreeKind}; -use syntax::codemap::{BytePos, Span}; +use syntax::codemap::{self, BytePos, Span, DUMMY_SP}; use codemap::SpanUtils; +use comment::combine_strs_with_missing_comments; use config::IndentStyle; use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator}; use rewrite::{Rewrite, RewriteContext}; @@ -24,6 +25,7 @@ use visitor::FmtVisitor; use std::borrow::Cow; +use std::fmt; /// Returns a name imported by a `use` declaration. e.g. returns `Ordering` /// for `std::cmp::Ordering` and `self` for `std::cmp::self`. @@ -89,7 +91,7 @@ pub fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) { // sorting. // FIXME we do a lot of allocation to make our own representation. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq)] pub enum UseSegment { Ident(String, Option), Slf(Option), @@ -98,12 +100,12 @@ pub enum UseSegment { List(Vec), } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct UseTree { pub path: Vec, pub span: Span, // Comment information within nested use tree. - list_item: Option, + pub list_item: Option, // Additional fields for top level use items. // Should we have another struct for top-level use items rather than reusing this? visibility: Option, @@ -143,29 +145,116 @@ fn from_path_segment(path_seg: &ast::PathSegment) -> Option { } } -impl UseTree { - // Rewrite use tree with `use ` and a trailing `;`. - pub fn rewrite_top_level(&self, context: &RewriteContext, shape: Shape) -> Option { - let mut result = String::with_capacity(256); - if let Some(ref attrs) = self.attrs { - result.push_str(&attrs.rewrite(context, shape)?); - if !result.is_empty() { - result.push_str(&shape.indent.to_string_with_newline(context.config)); +pub fn merge_use_trees(use_trees: Vec) -> Vec { + let mut result = Vec::with_capacity(use_trees.len()); + for use_tree in use_trees { + if use_tree.has_comment() || use_tree.attrs.is_some() { + result.push(use_tree); + continue; + } + + for flattened in use_tree.flatten() { + merge_use_trees_inner(&mut result, flattened); + } + } + result +} + +fn merge_use_trees_inner(trees: &mut Vec, use_tree: UseTree) { + for tree in trees.iter_mut() { + if tree.share_prefix(&use_tree) { + tree.merge(use_tree); + return; + } + } + + trees.push(use_tree); +} + +impl fmt::Debug for UseTree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Debug for UseSegment { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for UseSegment { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + UseSegment::Glob => write!(f, "*"), + UseSegment::Ident(ref s, _) => write!(f, "{}", s), + UseSegment::Slf(..) => write!(f, "self"), + UseSegment::Super(..) => write!(f, "super"), + UseSegment::List(ref list) => { + write!(f, "{{")?; + for (i, item) in list.iter().enumerate() { + let is_last = i == list.len() - 1; + write!(f, "{}", item)?; + if !is_last { + write!(f, ", ")?; + } + } + write!(f, "}}") } } + } +} +impl fmt::Display for UseTree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for (i, segment) in self.path.iter().enumerate() { + let is_last = i == self.path.len() - 1; + write!(f, "{}", segment)?; + if !is_last { + write!(f, "::")?; + } + } + write!(f, "") + } +} +impl UseTree { + // Rewrite use tree with `use ` and a trailing `;`. + pub fn rewrite_top_level(&self, context: &RewriteContext, shape: Shape) -> Option { let vis = self.visibility .as_ref() .map_or(Cow::from(""), |vis| ::utils::format_visibility(&vis)); - result.push_str(&self.rewrite(context, shape.offset_left(vis.len())?) + let use_str = self.rewrite(context, shape.offset_left(vis.len())?) .map(|s| { if s.is_empty() { s.to_owned() } else { format!("{}use {};", vis, s) } - })?); - Some(result) + })?; + if let Some(ref attrs) = self.attrs { + let attr_str = attrs.rewrite(context, shape)?; + let lo = attrs.last().as_ref()?.span().hi(); + let hi = self.span.lo(); + let span = mk_sp(lo, hi); + combine_strs_with_missing_comments(context, &attr_str, &use_str, span, shape, false) + } else { + Some(use_str) + } + } + + // FIXME: Use correct span? + // The given span is essentially incorrect, since we are reconstructing + // use statements. This should not be a problem, though, since we have + // already tried to extract comment and observed that there are no comment + // around the given use item, and the span will not be used afterward. + fn from_path(path: Vec, span: Span) -> UseTree { + UseTree { + path, + span, + list_item: None, + visibility: None, + attrs: None, + } } pub fn from_ast_with_normalization( @@ -185,7 +274,7 @@ pub fn from_ast_with_normalization( } else { Some(item.attrs.clone()) }, - ).normalize(context.config.reorder_imported_names()), + ).normalize(), ), _ => None, } @@ -271,7 +360,7 @@ fn from_ast( } // Do the adjustments that rustfmt does elsewhere to use paths. - pub fn normalize(mut self, do_sort: bool) -> UseTree { + pub fn normalize(mut self) -> UseTree { let mut last = self.path.pop().expect("Empty use tree?"); // Hack around borrow checker. let mut normalize_sole_list = false; @@ -293,7 +382,7 @@ pub fn normalize(mut self, do_sort: bool) -> UseTree { // Normalise foo::self -> foo. if let UseSegment::Slf(None) = last { - if self.path.len() > 0 { + if !self.path.is_empty() { return self; } } @@ -309,19 +398,24 @@ pub fn normalize(mut self, do_sort: bool) -> UseTree { } } + let mut done = false; if aliased_self { - match self.path.last() { + match self.path.last_mut() { Some(UseSegment::Ident(_, ref mut old_rename)) => { assert!(old_rename.is_none()); - if let UseSegment::Slf(Some(rename)) = last { + if let UseSegment::Slf(Some(rename)) = last.clone() { *old_rename = Some(rename); - return self; + done = true; } } _ => unreachable!(), } } + if done { + return self; + } + // Normalise foo::{bar} -> foo::bar if let UseSegment::List(ref list) = last { if list.len() == 1 { @@ -335,7 +429,7 @@ pub fn normalize(mut self, do_sort: bool) -> UseTree { for seg in &list[0].path { self.path.push(seg.clone()); } - return self.normalize(do_sort); + return self.normalize(); } _ => unreachable!(), } @@ -344,17 +438,133 @@ pub fn normalize(mut self, do_sort: bool) -> UseTree { // Recursively normalize elements of a list use (including sorting the list). if let UseSegment::List(list) = last { let mut list = list.into_iter() - .map(|ut| ut.normalize(do_sort)) + .map(|ut| ut.normalize()) .collect::>(); - if do_sort { - list.sort(); - } + list.sort(); last = UseSegment::List(list); } self.path.push(last); self } + + fn has_comment(&self) -> bool { + self.list_item.as_ref().map_or(false, ListItem::has_comment) + } + + fn same_visibility(&self, other: &UseTree) -> bool { + match (&self.visibility, &other.visibility) { + ( + Some(codemap::Spanned { + node: ast::VisibilityKind::Inherited, + .. + }), + None, + ) + | ( + None, + Some(codemap::Spanned { + node: ast::VisibilityKind::Inherited, + .. + }), + ) + | (None, None) => true, + ( + Some(codemap::Spanned { node: lnode, .. }), + Some(codemap::Spanned { node: rnode, .. }), + ) => lnode == rnode, + _ => false, + } + } + + fn share_prefix(&self, other: &UseTree) -> bool { + if self.path.is_empty() || other.path.is_empty() || self.attrs.is_some() + || !self.same_visibility(other) + { + false + } else { + self.path[0] == other.path[0] + } + } + + fn flatten(self) -> Vec { + if self.path.is_empty() { + return vec![self]; + } + match self.path.clone().last().unwrap() { + UseSegment::List(list) => { + let prefix = &self.path[..self.path.len() - 1]; + let mut result = vec![]; + for nested_use_tree in list { + for mut flattend in &mut nested_use_tree.clone().flatten() { + let mut new_path = prefix.to_vec(); + new_path.append(&mut flattend.path); + result.push(UseTree { + path: new_path, + span: self.span, + list_item: None, + visibility: self.visibility.clone(), + attrs: None, + }); + } + } + + result + } + _ => vec![self], + } + } + + fn merge(&mut self, other: UseTree) { + let mut new_path = vec![]; + for (mut a, b) in self.path + .clone() + .iter_mut() + .zip(other.path.clone().into_iter()) + { + if *a == b { + new_path.push(b); + } else { + break; + } + } + if let Some(merged) = merge_rest(&self.path, &other.path, new_path.len()) { + new_path.push(merged); + self.span = self.span.to(other.span); + } + self.path = new_path; + } +} + +fn merge_rest(a: &[UseSegment], b: &[UseSegment], len: usize) -> Option { + let a_rest = &a[len..]; + let b_rest = &b[len..]; + if a_rest.is_empty() && b_rest.is_empty() { + return None; + } + if a_rest.is_empty() { + return Some(UseSegment::List(vec![ + UseTree::from_path(vec![UseSegment::Slf(None)], DUMMY_SP), + UseTree::from_path(b_rest.to_vec(), DUMMY_SP), + ])); + } + if b_rest.is_empty() { + return Some(UseSegment::List(vec![ + UseTree::from_path(vec![UseSegment::Slf(None)], DUMMY_SP), + UseTree::from_path(a_rest.to_vec(), DUMMY_SP), + ])); + } + if let UseSegment::List(mut list) = a_rest[0].clone() { + merge_use_trees_inner(&mut list, UseTree::from_path(b_rest.to_vec(), DUMMY_SP)); + list.sort(); + return Some(UseSegment::List(list.clone())); + } + let mut list = vec![ + UseTree::from_path(a_rest.to_vec(), DUMMY_SP), + UseTree::from_path(b_rest.to_vec(), DUMMY_SP), + ]; + list.sort(); + Some(UseSegment::List(list)) } impl PartialOrd for UseSegment { @@ -456,11 +666,14 @@ fn rewrite_nested_use_tree( IndentStyle::Visual => shape.visual_indent(0), }; for use_tree in use_tree_list { - let mut list_item = use_tree.list_item.clone()?; - list_item.item = use_tree.rewrite(context, nested_shape); - list_items.push(list_item); + if let Some(mut list_item) = use_tree.list_item.clone() { + list_item.item = use_tree.rewrite(context, nested_shape); + list_items.push(list_item); + } else { + list_items.push(ListItem::from_str(use_tree.rewrite(context, nested_shape)?)); + } } - let (tactic, remaining_width) = if use_tree_list.iter().any(|use_segment| { + let has_nested_list = use_tree_list.iter().any(|use_segment| { use_segment .path .last() @@ -468,7 +681,8 @@ fn rewrite_nested_use_tree( UseSegment::List(..) => true, _ => false, }) - }) { + }); + let (tactic, remaining_width) = if has_nested_list { (DefinitiveListTactic::Vertical, 0) } else { let remaining_width = shape.width.checked_sub(2).unwrap_or(0); @@ -480,6 +694,7 @@ fn rewrite_nested_use_tree( ); (tactic, remaining_width) }; + let ends_with_newline = context.config.imports_indent() == IndentStyle::Block && tactic != DefinitiveListTactic::Horizontal; let fmt = ListFormatting { @@ -678,77 +893,120 @@ fn parse_list(&mut self) -> Vec { parser.parse_in_list() } + macro parse_use_trees($($s:expr),* $(,)*) { + vec![ + $(parse_use_tree($s),)* + ] + } + #[test] - fn test_use_tree_normalize() { - assert_eq!( - parse_use_tree("a::self").normalize(true), - parse_use_tree("a") + fn test_use_tree_merge() { + macro test_merge([$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) { + assert_eq!( + merge_use_trees(parse_use_trees!($($input,)*)), + parse_use_trees!($($output,)*), + ); + } + + test_merge!(["a::b::{c, d}", "a::b::{e, f}"], ["a::b::{c, d, e, f}"]); + test_merge!(["a::b::c", "a::b"], ["a::b::{self, c}"]); + test_merge!(["a::b", "a::b"], ["a::b"]); + test_merge!(["a", "a::b", "a::b::c"], ["a::{self, b::{self, c}}"]); + test_merge!( + ["a::{b::{self, c}, d::e}", "a::d::f"], + ["a::{b::{self, c}, d::{e, f}}"] + ); + test_merge!( + ["a::d::f", "a::{b::{self, c}, d::e}"], + ["a::{b::{self, c}, d::{e, f}}"] + ); + test_merge!( + ["a::{c, d, b}", "a::{d, e, b, a, f}", "a::{f, g, c}"], + ["a::{a, b, c, d, e, f, g}"] ); + } + + #[test] + fn test_use_tree_flatten() { assert_eq!( - parse_use_tree("a::self as foo").normalize(true), - parse_use_tree("a as foo") + parse_use_tree("a::b::{c, d, e, f}").flatten(), + parse_use_trees!("a::b::c", "a::b::d", "a::b::e", "a::b::f",) ); + assert_eq!( - parse_use_tree("a::{self}").normalize(true), - parse_use_tree("a") + parse_use_tree("a::b::{c::{d, e, f}, g, h::{i, j, k}}").flatten(), + parse_use_trees![ + "a::b::c::d", + "a::b::c::e", + "a::b::c::f", + "a::b::g", + "a::b::h::i", + "a::b::h::j", + "a::b::h::k", + ] ); + } + + #[test] + fn test_use_tree_normalize() { + assert_eq!(parse_use_tree("a::self").normalize(), parse_use_tree("a")); assert_eq!( - parse_use_tree("a::{b}").normalize(true), - parse_use_tree("a::b") + parse_use_tree("a::self as foo").normalize(), + parse_use_tree("a as foo") ); + assert_eq!(parse_use_tree("a::{self}").normalize(), parse_use_tree("a")); + assert_eq!(parse_use_tree("a::{b}").normalize(), parse_use_tree("a::b")); assert_eq!( - parse_use_tree("a::{b, c::self}").normalize(true), + parse_use_tree("a::{b, c::self}").normalize(), parse_use_tree("a::{b, c}") ); assert_eq!( - parse_use_tree("a::{b as bar, c::self}").normalize(true), + parse_use_tree("a::{b as bar, c::self}").normalize(), parse_use_tree("a::{b as bar, c}") ); } #[test] fn test_use_tree_ord() { - assert!(parse_use_tree("a").normalize(true) < parse_use_tree("aa").normalize(true)); - assert!(parse_use_tree("a").normalize(true) < parse_use_tree("a::a").normalize(true)); - assert!(parse_use_tree("a").normalize(true) < parse_use_tree("*").normalize(true)); - assert!(parse_use_tree("a").normalize(true) < parse_use_tree("{a, b}").normalize(true)); - assert!(parse_use_tree("*").normalize(true) < parse_use_tree("{a, b}").normalize(true)); + assert!(parse_use_tree("a").normalize() < parse_use_tree("aa").normalize()); + assert!(parse_use_tree("a").normalize() < parse_use_tree("a::a").normalize()); + assert!(parse_use_tree("a").normalize() < parse_use_tree("*").normalize()); + assert!(parse_use_tree("a").normalize() < parse_use_tree("{a, b}").normalize()); + assert!(parse_use_tree("*").normalize() < parse_use_tree("{a, b}").normalize()); assert!( - parse_use_tree("aaaaaaaaaaaaaaa::{bb, cc, dddddddd}").normalize(true) - < parse_use_tree("aaaaaaaaaaaaaaa::{bb, cc, ddddddddd}").normalize(true) - ); - assert!( - parse_use_tree("serde::de::{Deserialize}").normalize(true) - < parse_use_tree("serde_json").normalize(true) + parse_use_tree("aaaaaaaaaaaaaaa::{bb, cc, dddddddd}").normalize() + < parse_use_tree("aaaaaaaaaaaaaaa::{bb, cc, ddddddddd}").normalize() ); assert!( - parse_use_tree("a::b::c").normalize(true) < parse_use_tree("a::b::*").normalize(true) + parse_use_tree("serde::de::{Deserialize}").normalize() + < parse_use_tree("serde_json").normalize() ); + assert!(parse_use_tree("a::b::c").normalize() < parse_use_tree("a::b::*").normalize()); assert!( - parse_use_tree("foo::{Bar, Baz}").normalize(true) - < parse_use_tree("{Bar, Baz}").normalize(true) + parse_use_tree("foo::{Bar, Baz}").normalize() + < parse_use_tree("{Bar, Baz}").normalize() ); assert!( - parse_use_tree("foo::{self as bar}").normalize(true) - < parse_use_tree("foo::{qux as bar}").normalize(true) + parse_use_tree("foo::{self as bar}").normalize() + < parse_use_tree("foo::{qux as bar}").normalize() ); assert!( - parse_use_tree("foo::{qux as bar}").normalize(true) - < parse_use_tree("foo::{baz, qux as bar}").normalize(true) + parse_use_tree("foo::{qux as bar}").normalize() + < parse_use_tree("foo::{baz, qux as bar}").normalize() ); assert!( - parse_use_tree("foo::{self as bar, baz}").normalize(true) - < parse_use_tree("foo::{baz, qux as bar}").normalize(true) + parse_use_tree("foo::{self as bar, baz}").normalize() + < parse_use_tree("foo::{baz, qux as bar}").normalize() ); - assert!(parse_use_tree("foo").normalize(true) < parse_use_tree("Foo").normalize(true)); - assert!(parse_use_tree("foo").normalize(true) < parse_use_tree("foo::Bar").normalize(true)); + assert!(parse_use_tree("foo").normalize() < parse_use_tree("Foo").normalize()); + assert!(parse_use_tree("foo").normalize() < parse_use_tree("foo::Bar").normalize()); assert!( - parse_use_tree("std::cmp::{d, c, b, a}").normalize(true) - < parse_use_tree("std::cmp::{b, e, g, f}").normalize(true) + parse_use_tree("std::cmp::{d, c, b, a}").normalize() + < parse_use_tree("std::cmp::{b, e, g, f}").normalize() ); } }