X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fimports.rs;h=0f635fe1ccb3584210a21debfaa5f423384f556a;hb=1e2258ffa949bf4d273259e78d326857c0b2bb3d;hp=79d0641504ef23a57accc813abb4a6e31821dc49;hpb=d40ed146a5eec39b277514b1d5d48d927934768a;p=rust.git diff --git a/src/imports.rs b/src/imports.rs index 79d0641504e..0f635fe1ccb 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -1,40 +1,34 @@ -// Copyright 2015 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 std::cmp::Ordering; - -use config::lists::*; -use syntax::ast::{self, UseTreeKind}; -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}; -use shape::Shape; -use spanned::Spanned; -use utils::{is_same_visibility, mk_sp, rewrite_ident}; -use visitor::FmtVisitor; - use std::borrow::Cow; +use std::cmp::Ordering; 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`. -pub fn path_to_imported_ident(path: &ast::Path) -> ast::Ident { +use rustc_ast::ast::{self, UseTreeKind}; +use rustc_span::{ + symbol::{self, sym}, + BytePos, Span, DUMMY_SP, +}; + +use crate::comment::combine_strs_with_missing_comments; +use crate::config::lists::*; +use crate::config::{Edition, IndentStyle}; +use crate::lists::{ + definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, +}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::spanned::Spanned; +use crate::utils::{is_same_visibility, mk_sp, rewrite_ident}; +use crate::visitor::FmtVisitor; + +/// Returns a name imported by a `use` declaration. +/// E.g., returns `Ordering` for `std::cmp::Ordering` and `self` for `std::cmp::self`. +pub(crate) fn path_to_imported_ident(path: &ast::Path) -> symbol::Ident { path.segments.last().unwrap().ident } impl<'a> FmtVisitor<'a> { - pub fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) { + pub(crate) fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) { let span = item.span(); let shape = self.shape(); let rw = UseTree::from_ast( @@ -44,12 +38,13 @@ pub fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) { Some(item.vis.clone()), Some(item.span.lo()), Some(item.attrs.clone()), - ).rewrite_top_level(&self.get_context(), shape); + ) + .rewrite_top_level(&self.get_context(), shape); match rw { Some(ref s) if s.is_empty() => { // Format up to last newline let prev_span = mk_sp(self.last_pos, source!(self, span).lo()); - let trimmed_snippet = self.snippet(prev_span).trim_right(); + let trimmed_snippet = self.snippet(prev_span).trim_end(); let span_end = self.last_pos + BytePos(trimmed_snippet.len() as u32); self.format_missing(span_end); // We have an excessive newline from the removed import. @@ -85,27 +80,28 @@ pub fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) { // when ordering unless the imports are identical except for the alias (rare in // practice). -// FIXME(#2531) - we should unify the comparison code here with the formatting +// FIXME(#2531): we should unify the comparison code here with the formatting // code elsewhere since we are essentially string-ifying twice. Furthermore, by // parsing to our own format on comparison, we repeat a lot of work when // sorting. // FIXME we do a lot of allocation to make our own representation. #[derive(Clone, Eq, PartialEq)] -pub enum UseSegment { +pub(crate) enum UseSegment { Ident(String, Option), Slf(Option), Super(Option), + Crate(Option), Glob, List(Vec), } #[derive(Clone)] -pub struct UseTree { - pub path: Vec, - pub span: Span, +pub(crate) struct UseTree { + pub(crate) path: Vec, + pub(crate) span: Span, // Comment information within nested use tree. - pub list_item: Option, + pub(crate) 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, @@ -137,29 +133,33 @@ fn remove_alias(&self) -> UseSegment { UseSegment::Ident(ref s, _) => UseSegment::Ident(s.clone(), None), UseSegment::Slf(_) => UseSegment::Slf(None), UseSegment::Super(_) => UseSegment::Super(None), + UseSegment::Crate(_) => UseSegment::Crate(None), _ => self.clone(), } } fn from_path_segment( - context: &RewriteContext, + context: &RewriteContext<'_>, path_seg: &ast::PathSegment, + modsep: bool, ) -> Option { let name = rewrite_ident(context, path_seg.ident); if name.is_empty() || name == "{{root}}" { return None; } - Some(if name == "self" { - UseSegment::Slf(None) - } else if name == "super" { - UseSegment::Super(None) - } else { - UseSegment::Ident((*name).to_owned(), None) + Some(match name { + "self" => UseSegment::Slf(None), + "super" => UseSegment::Super(None), + "crate" => UseSegment::Crate(None), + _ => { + let mod_sep = if modsep { "::" } else { "" }; + UseSegment::Ident(format!("{}{}", mod_sep, name), None) + } }) } } -pub fn merge_use_trees(use_trees: Vec) -> Vec { +pub(crate) fn merge_use_trees(use_trees: Vec, merge_by: SharedPrefix) -> 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() { @@ -168,50 +168,64 @@ pub fn merge_use_trees(use_trees: Vec) -> Vec { } for flattened in use_tree.flatten() { - merge_use_trees_inner(&mut result, flattened); + if let Some(tree) = result + .iter_mut() + .find(|tree| tree.share_prefix(&flattened, merge_by)) + { + tree.merge(&flattened, merge_by); + } else { + result.push(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); +pub(crate) fn flatten_use_trees(use_trees: Vec) -> Vec { + use_trees + .into_iter() + .flat_map(UseTree::flatten) + .map(|mut tree| { + // If a path ends in `::self`, rewrite it to `::{self}`. + if let Some(UseSegment::Slf(..)) = tree.path.last() { + let self_segment = tree.path.pop().unwrap(); + tree.path.push(UseSegment::List(vec![UseTree::from_path( + vec![self_segment], + DUMMY_SP, + )])); + } + tree + }) + .collect() } impl fmt::Debug for UseTree { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + 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 { + 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 { + 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::Crate(..) => write!(f, "crate"), 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 { + if i != 0 { write!(f, ", ")?; } + write!(f, "{}", item)?; } write!(f, "}}") } @@ -219,47 +233,67 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { } } impl fmt::Display for UseTree { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + 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 { + if i != 0 { write!(f, "::")?; } + write!(f, "{}", segment)?; } - write!(f, "") + Ok(()) } } impl UseTree { // Rewrite use tree with `use ` and a trailing `;`. - pub fn rewrite_top_level(&self, context: &RewriteContext, shape: Shape) -> Option { + pub(crate) fn rewrite_top_level( + &self, + context: &RewriteContext<'_>, + shape: Shape, + ) -> Option { let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| { - ::utils::format_visibility(context, &vis) + crate::utils::format_visibility(context, &vis) }); let use_str = self .rewrite(context, shape.offset_left(vis.len())?) .map(|s| { if s.is_empty() { - s.to_owned() + s } else { format!("{}use {};", vis, s) } })?; - 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) + match self.attrs { + Some(ref attrs) if !attrs.is_empty() => { + 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); + + let allow_extend = if attrs.len() == 1 { + let line_len = attr_str.len() + 1 + use_str.len(); + !attrs.first().unwrap().is_doc_comment() + && context.config.inline_attribute_width() >= line_len + } else { + false + }; + + combine_strs_with_missing_comments( + context, + &attr_str, + &use_str, + span, + shape, + allow_extend, + ) + } + _ => 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 + // 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 { @@ -272,11 +306,11 @@ fn from_path(path: Vec, span: Span) -> UseTree { } } - pub fn from_ast_with_normalization( - context: &RewriteContext, + pub(crate) fn from_ast_with_normalization( + context: &RewriteContext<'_>, item: &ast::Item, ) -> Option { - match item.node { + match item.kind { ast::ItemKind::Use(ref use_tree) => Some( UseTree::from_ast( context, @@ -289,14 +323,15 @@ pub fn from_ast_with_normalization( } else { Some(item.attrs.clone()) }, - ).normalize(), + ) + .normalize(), ), _ => None, } } fn from_ast( - context: &RewriteContext, + context: &RewriteContext<'_>, a: &ast::UseTree, list_item: Option, visibility: Option, @@ -315,13 +350,25 @@ fn from_ast( visibility, attrs, }; + + let leading_modsep = + context.config.edition() >= Edition::Edition2018 && a.prefix.is_global(); + + let mut modsep = leading_modsep; + for p in &a.prefix.segments { - if let Some(use_segment) = UseSegment::from_path_segment(context, p) { + if let Some(use_segment) = UseSegment::from_path_segment(context, p, modsep) { result.path.push(use_segment); + modsep = false; } } + match a.kind { UseTreeKind::Glob => { + // in case of a global path and the glob starts at the root, e.g., "::*" + if a.prefix.segments.len() == 1 && leading_modsep { + result.path.push(UseSegment::Ident("".to_owned(), None)); + } result.path.push(UseSegment::Glob); } UseTreeKind::Nested(ref list) => { @@ -338,7 +385,13 @@ fn from_ast( context.snippet_provider.span_after(a.span, "{"), a.span.hi(), false, - ).collect(); + ) + .collect(); + // in case of a global path and the nested list starts at the root, + // e.g., "::{foo, bar}" + if a.prefix.segments.len() == 1 && leading_modsep { + result.path.push(UseSegment::Ident("".to_owned(), None)); + } result.path.push(UseSegment::List( list.iter() .zip(items.into_iter()) @@ -349,21 +402,30 @@ fn from_ast( )); } UseTreeKind::Simple(ref rename, ..) => { - let mut name = rewrite_ident(context, path_to_imported_ident(&a.prefix)).to_owned(); + // If the path has leading double colons and is composed of only 2 segments, then we + // bypass the call to path_to_imported_ident which would get only the ident and + // lose the path root, e.g., `that` in `::that`. + // The span of `a.prefix` contains the leading colons. + let name = if a.prefix.segments.len() == 2 && leading_modsep { + context.snippet(a.prefix.span).to_owned() + } else { + rewrite_ident(context, path_to_imported_ident(&a.prefix)).to_owned() + }; let alias = rename.and_then(|ident| { - if ident == path_to_imported_ident(&a.prefix) { + if ident.name == sym::underscore_imports { + // for impl-only-use + Some("_".to_owned()) + } else if ident == path_to_imported_ident(&a.prefix) { None } else { Some(rewrite_ident(context, ident).to_owned()) } }); - - let segment = if &name == "self" { - UseSegment::Slf(alias) - } else if &name == "super" { - UseSegment::Super(alias) - } else { - UseSegment::Ident(name, alias) + let segment = match name.as_ref() { + "self" => UseSegment::Slf(alias), + "super" => UseSegment::Super(alias), + "crate" => UseSegment::Crate(alias), + _ => UseSegment::Ident(name, alias), }; // `name` is already in result. @@ -375,7 +437,7 @@ fn from_ast( } // Do the adjustments that rustfmt does elsewhere to use paths. - pub fn normalize(mut self) -> UseTree { + pub(crate) 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; @@ -405,11 +467,10 @@ pub fn normalize(mut self) -> UseTree { // Normalise foo::self as bar -> foo as bar. if let UseSegment::Slf(_) = last { match self.path.last() { - None => {} Some(UseSegment::Ident(_, None)) => { aliased_self = true; } - _ => unreachable!(), + _ => {} } } @@ -433,7 +494,7 @@ pub fn normalize(mut self) -> UseTree { // Normalise foo::{bar} -> foo::bar if let UseSegment::List(ref list) = last { - if list.len() == 1 { + if list.len() == 1 && list[0].to_string() != "self" { normalize_sole_list = true; } } @@ -452,10 +513,7 @@ pub fn normalize(mut self) -> 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()) - .collect::>(); + let mut list = list.into_iter().map(UseTree::normalize).collect::>(); list.sort(); last = UseSegment::List(list); } @@ -471,16 +529,16 @@ fn has_comment(&self) -> bool { fn same_visibility(&self, other: &UseTree) -> bool { match (&self.visibility, &other.visibility) { ( - Some(codemap::Spanned { - node: ast::VisibilityKind::Inherited, + Some(ast::Visibility { + kind: ast::VisibilityKind::Inherited, .. }), None, ) | ( None, - Some(codemap::Spanned { - node: ast::VisibilityKind::Inherited, + Some(ast::Visibility { + kind: ast::VisibilityKind::Inherited, .. }), ) @@ -490,7 +548,7 @@ fn same_visibility(&self, other: &UseTree) -> bool { } } - fn share_prefix(&self, other: &UseTree) -> bool { + fn share_prefix(&self, other: &UseTree, shared_prefix: SharedPrefix) -> bool { if self.path.is_empty() || other.path.is_empty() || self.attrs.is_some() @@ -498,7 +556,12 @@ fn share_prefix(&self, other: &UseTree) -> bool { { false } else { - self.path[0] == other.path[0] + match shared_prefix { + SharedPrefix::Crate => self.path[0] == other.path[0], + SharedPrefix::Module => { + self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1] + } + } } } @@ -508,10 +571,16 @@ fn flatten(self) -> Vec { } match self.path.clone().last().unwrap() { UseSegment::List(list) => { + if list.len() == 1 && list[0].path.len() == 1 { + match list[0].path[0] { + UseSegment::Slf(..) => return vec![self], + _ => (), + }; + } 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() { + for flattend in &mut nested_use_tree.clone().flatten() { let mut new_path = prefix.to_vec(); new_path.append(&mut flattend.path); result.push(UseTree { @@ -530,57 +599,83 @@ fn flatten(self) -> Vec { } } - 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); + fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) { + let mut prefix = 0; + for (a, b) in self.path.iter().zip(other.path.iter()) { + if *a == *b { + prefix += 1; } else { break; } } - if let Some(merged) = merge_rest(&self.path, &other.path, new_path.len()) { - new_path.push(merged); + if let Some(new_path) = merge_rest(&self.path, &other.path, prefix, merge_by) { + self.path = new_path; 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() { +fn merge_rest( + a: &[UseSegment], + b: &[UseSegment], + mut len: usize, + merge_by: SharedPrefix, +) -> Option> { + if a.len() == len && b.len() == len { 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())); + if a.len() != len && b.len() != len { + if let UseSegment::List(ref list) = a[len] { + let mut list = list.clone(); + merge_use_trees_inner( + &mut list, + UseTree::from_path(b[len..].to_vec(), DUMMY_SP), + merge_by, + ); + let mut new_path = b[..len].to_vec(); + new_path.push(UseSegment::List(list)); + return Some(new_path); + } + } else if len == 1 { + let rest = if a.len() == len { &b[1..] } else { &a[1..] }; + return Some(vec![ + b[0].clone(), + UseSegment::List(vec![ + UseTree::from_path(vec![UseSegment::Slf(None)], DUMMY_SP), + UseTree::from_path(rest.to_vec(), DUMMY_SP), + ]), + ]); + } else { + len -= 1; } let mut list = vec![ - UseTree::from_path(a_rest.to_vec(), DUMMY_SP), - UseTree::from_path(b_rest.to_vec(), DUMMY_SP), + UseTree::from_path(a[len..].to_vec(), DUMMY_SP), + UseTree::from_path(b[len..].to_vec(), DUMMY_SP), ]; list.sort(); - Some(UseSegment::List(list)) + let mut new_path = b[..len].to_vec(); + new_path.push(UseSegment::List(list)); + Some(new_path) +} + +fn merge_use_trees_inner(trees: &mut Vec, use_tree: UseTree, merge_by: SharedPrefix) { + let similar_trees = trees + .iter_mut() + .filter(|tree| tree.share_prefix(&use_tree, merge_by)); + if use_tree.path.len() == 1 && merge_by == SharedPrefix::Crate { + if let Some(tree) = similar_trees.min_by_key(|tree| tree.path.len()) { + if tree.path.len() == 1 { + return; + } + } + } else if let Some(tree) = similar_trees.max_by_key(|tree| tree.path.len()) { + if tree.path.len() > 1 { + tree.merge(&use_tree, merge_by); + return; + } + } + trees.push(use_tree); + trees.sort(); } impl PartialOrd for UseSegment { @@ -598,11 +693,14 @@ fn cmp(&self, other: &UseSegment) -> Ordering { use self::UseSegment::*; fn is_upper_snake_case(s: &str) -> bool { - s.chars().all(|c| c.is_uppercase() || c == '_') + s.chars() + .all(|c| c.is_uppercase() || c == '_' || c.is_numeric()) } match (self, other) { - (&Slf(ref a), &Slf(ref b)) | (&Super(ref a), &Super(ref b)) => a.cmp(b), + (&Slf(ref a), &Slf(ref b)) + | (&Super(ref a), &Super(ref b)) + | (&Crate(ref a), &Crate(ref b)) => a.cmp(b), (&Glob, &Glob) => Ordering::Equal, (&Ident(ref ia, ref aa), &Ident(ref ib, ref ab)) => { // snake_case < CamelCase < UPPER_SNAKE_CASE @@ -644,6 +742,8 @@ fn is_upper_snake_case(s: &str) -> bool { (_, &Slf(_)) => Ordering::Greater, (&Super(_), _) => Ordering::Less, (_, &Super(_)) => Ordering::Greater, + (&Crate(_), _) => Ordering::Less, + (_, &Crate(_)) => Ordering::Greater, (&Ident(..), _) => Ordering::Less, (_, &Ident(..)) => Ordering::Greater, (&Glob, _) => Ordering::Less, @@ -669,7 +769,7 @@ fn cmp(&self, other: &UseTree) -> Ordering { } fn rewrite_nested_use_tree( - context: &RewriteContext, + context: &RewriteContext<'_>, use_tree_list: &[UseTree], shape: Shape, ) -> Option { @@ -714,21 +814,17 @@ fn rewrite_nested_use_tree( let ends_with_newline = context.config.imports_indent() == IndentStyle::Block && tactic != DefinitiveListTactic::Horizontal; - let fmt = ListFormatting { - tactic, - separator: ",", - trailing_separator: if ends_with_newline { - context.config.trailing_comma() - } else { - SeparatorTactic::Never - }, - separator_place: SeparatorPlace::Back, - shape: nested_shape, - ends_with_newline, - preserve_newline: true, - nested: has_nested_list, - config: context.config, + let trailing_separator = if ends_with_newline { + context.config.trailing_comma() + } else { + SeparatorTactic::Never }; + let fmt = ListFormatting::new(nested_shape, context.config) + .tactic(tactic) + .trailing_separator(trailing_separator) + .ends_with_newline(ends_with_newline) + .preserve_newline(true) + .nested(has_nested_list); let list_str = write_list(&list_items, &fmt)?; @@ -749,14 +845,16 @@ fn rewrite_nested_use_tree( } impl Rewrite for UseSegment { - fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { - Some(match *self { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + Some(match self { UseSegment::Ident(ref ident, Some(ref rename)) => format!("{} as {}", ident, rename), UseSegment::Ident(ref ident, None) => ident.clone(), UseSegment::Slf(Some(ref rename)) => format!("self as {}", rename), UseSegment::Slf(None) => "self".to_owned(), UseSegment::Super(Some(ref rename)) => format!("super as {}", rename), UseSegment::Super(None) => "super".to_owned(), + UseSegment::Crate(Some(ref rename)) => format!("crate as {}", rename), + UseSegment::Crate(None) => "crate".to_owned(), UseSegment::Glob => "*".to_owned(), UseSegment::List(ref use_tree_list) => rewrite_nested_use_tree( context, @@ -770,7 +868,7 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { impl Rewrite for UseTree { // This does NOT format attributes and visibility or add a trailing `;`. - fn rewrite(&self, context: &RewriteContext, mut shape: Shape) -> Option { + fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option { let mut result = String::with_capacity(256); let mut iter = self.path.iter().peekable(); while let Some(ref segment) = iter.next() { @@ -786,10 +884,16 @@ fn rewrite(&self, context: &RewriteContext, mut shape: Shape) -> Option } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum SharedPrefix { + Crate, + Module, +} + #[cfg(test)] mod test { use super::*; - use syntax::codemap::DUMMY_SP; + use rustc_span::DUMMY_SP; // Parse the path part of an import. This parser is not robust and is only // suitable for use in a test harness. @@ -819,18 +923,28 @@ fn push_segment( if !buf.is_empty() { let mut alias = None; swap(alias_buf, &mut alias); - if buf == "self" { - result.push(UseSegment::Slf(alias)); - *buf = String::new(); - *alias_buf = None; - } else if buf == "super" { - result.push(UseSegment::Super(alias)); - *buf = String::new(); - *alias_buf = None; - } else { - let mut name = String::new(); - swap(buf, &mut name); - result.push(UseSegment::Ident(name, alias)); + + match buf.as_ref() { + "self" => { + result.push(UseSegment::Slf(alias)); + *buf = String::new(); + *alias_buf = None; + } + "super" => { + result.push(UseSegment::Super(alias)); + *buf = String::new(); + *alias_buf = None; + } + "crate" => { + result.push(UseSegment::Crate(alias)); + *buf = String::new(); + *alias_buf = None; + } + _ => { + let mut name = String::new(); + swap(buf, &mut name); + result.push(UseSegment::Ident(name, alias)); + } } } } @@ -914,37 +1028,97 @@ fn parse_list(&mut self) -> Vec { parser.parse_in_list() } - macro parse_use_trees($($s:expr),* $(,)*) { - vec![ - $(parse_use_tree($s),)* - ] + macro_rules! parse_use_trees { + ($($s:expr),* $(,)*) => { + vec![ + $(parse_use_tree($s),)* + ] + } } - #[test] - fn test_use_tree_merge() { - macro test_merge([$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) { + macro_rules! test_merge { + ($by:ident, [$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) => { assert_eq!( - merge_use_trees(parse_use_trees!($($input,)*)), + merge_use_trees(parse_use_trees!($($input,)*), SharedPrefix::$by), 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] + fn test_use_tree_merge_crate() { + test_merge!( + Crate, + ["a::b::{c, d}", "a::b::{e, f}"], + ["a::b::{c, d, e, f}"] + ); + test_merge!(Crate, ["a::b::c", "a::b"], ["a::{b, b::c}"]); + test_merge!(Crate, ["a::b", "a::b"], ["a::b"]); + test_merge!(Crate, ["a", "a::b", "a::b::c"], ["a::{self, b, b::c}"]); + test_merge!( + Crate, + ["a", "a::b", "a::b::c", "a::b::c::d"], + ["a::{self, b, b::{c, c::d}}"] + ); + test_merge!( + Crate, + ["a", "a::b", "a::b::c", "a::b"], + ["a::{self, b, b::c}"] + ); test_merge!( + Crate, ["a::{b::{self, c}, d::e}", "a::d::f"], ["a::{b::{self, c}, d::{e, f}}"] ); test_merge!( + Crate, ["a::d::f", "a::{b::{self, c}, d::e}"], ["a::{b::{self, c}, d::{e, f}}"] ); test_merge!( + Crate, ["a::{c, d, b}", "a::{d, e, b, a, f}", "a::{f, g, c}"], ["a::{a, b, c, d, e, f, g}"] ); + test_merge!( + Crate, + ["a::{self}", "b::{self as foo}"], + ["a::{self}", "b::{self as foo}"] + ); + } + + #[test] + fn test_use_tree_merge_module() { + test_merge!( + Module, + ["foo::b", "foo::{a, c, d::e}"], + ["foo::{a, b, c}", "foo::d::e"] + ); + + test_merge!( + Module, + ["foo::{a::b, a::c, d::e, d::f}"], + ["foo::a::{b, c}", "foo::d::{e, f}"] + ); + } + + #[test] + fn test_flatten_use_trees() { + assert_eq!( + flatten_use_trees(parse_use_trees!["foo::{a::{b, c}, d::e}"]), + parse_use_trees!["foo::a::b", "foo::a::c", "foo::d::e"] + ); + + assert_eq!( + flatten_use_trees(parse_use_trees!["foo::{self, a, b::{c, d}, e::*}"]), + parse_use_trees![ + "foo::{self}", + "foo::a", + "foo::b::c", + "foo::b::d", + "foo::e::*" + ] + ); } #[test] @@ -975,7 +1149,10 @@ fn test_use_tree_normalize() { 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::{self}").normalize(), + parse_use_tree("a::{self}") + ); assert_eq!(parse_use_tree("a::{b}").normalize(), parse_use_tree("a::b")); assert_eq!( parse_use_tree("a::{b, c::self}").normalize(), @@ -1010,8 +1187,8 @@ fn test_use_tree_ord() { ); assert!( - parse_use_tree("foo::{self as bar}").normalize() - < parse_use_tree("foo::{qux as bar}").normalize() + parse_use_tree("foo::{qux as bar}").normalize() + < parse_use_tree("foo::{self as bar}").normalize() ); assert!( parse_use_tree("foo::{qux as bar}").normalize()