Add Version information to UseSegment

There are some proposed import sorting changes for raw identifier `r#`.
These changes constitute a breaking change, and need to be version
gagted. Before version gating those changes we add the version
information to the `UseSegment`.
This commit is contained in:
Yacin Tmimi 2022-06-13 10:53:53 -04:00 committed by Caleb Cartwright
parent 5ae94cc6b8
commit 795efb2068
2 changed files with 206 additions and 115 deletions

View file

@ -15,7 +15,7 @@ use rustc_span::{
use crate::comment::combine_strs_with_missing_comments;
use crate::config::lists::*;
use crate::config::ImportGranularity;
use crate::config::{Edition, IndentStyle};
use crate::config::{Edition, IndentStyle, Version};
use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
};
@ -92,7 +92,7 @@ impl<'a> FmtVisitor<'a> {
// FIXME we do a lot of allocation to make our own representation.
#[derive(Clone, Eq, Hash, PartialEq)]
pub(crate) enum UseSegment {
pub(crate) enum UseSegmentKind {
Ident(String, Option<String>),
Slf(Option<String>),
Super(Option<String>),
@ -101,6 +101,12 @@ pub(crate) enum UseSegment {
List(Vec<UseTree>),
}
#[derive(Clone, Eq, PartialEq)]
pub(crate) struct UseSegment {
pub(crate) kind: UseSegmentKind,
pub(crate) version: Version,
}
#[derive(Clone)]
pub(crate) struct UseTree {
pub(crate) path: Vec<UseSegment>,
@ -134,34 +140,38 @@ impl Spanned for UseTree {
impl UseSegment {
// Clone a version of self with any top-level alias removed.
fn remove_alias(&self) -> UseSegment {
match *self {
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(),
let kind = match self.kind {
UseSegmentKind::Ident(ref s, _) => UseSegmentKind::Ident(s.clone(), None),
UseSegmentKind::Slf(_) => UseSegmentKind::Slf(None),
UseSegmentKind::Super(_) => UseSegmentKind::Super(None),
UseSegmentKind::Crate(_) => UseSegmentKind::Crate(None),
_ => return self.clone(),
};
UseSegment {
kind,
version: self.version,
}
}
// Check if self == other with their aliases removed.
fn equal_except_alias(&self, other: &Self) -> bool {
match (self, other) {
(UseSegment::Ident(ref s1, _), UseSegment::Ident(ref s2, _)) => s1 == s2,
(UseSegment::Slf(_), UseSegment::Slf(_))
| (UseSegment::Super(_), UseSegment::Super(_))
| (UseSegment::Crate(_), UseSegment::Crate(_))
| (UseSegment::Glob, UseSegment::Glob) => true,
(UseSegment::List(ref list1), UseSegment::List(ref list2)) => list1 == list2,
match (&self.kind, &other.kind) {
(UseSegmentKind::Ident(ref s1, _), UseSegmentKind::Ident(ref s2, _)) => s1 == s2,
(UseSegmentKind::Slf(_), UseSegmentKind::Slf(_))
| (UseSegmentKind::Super(_), UseSegmentKind::Super(_))
| (UseSegmentKind::Crate(_), UseSegmentKind::Crate(_))
| (UseSegmentKind::Glob, UseSegmentKind::Glob) => true,
(UseSegmentKind::List(ref list1), UseSegmentKind::List(ref list2)) => list1 == list2,
_ => false,
}
}
fn get_alias(&self) -> Option<&str> {
match self {
UseSegment::Ident(_, a)
| UseSegment::Slf(a)
| UseSegment::Super(a)
| UseSegment::Crate(a) => a.as_deref(),
match &self.kind {
UseSegmentKind::Ident(_, a)
| UseSegmentKind::Slf(a)
| UseSegmentKind::Super(a)
| UseSegmentKind::Crate(a) => a.as_deref(),
_ => None,
}
}
@ -175,19 +185,24 @@ impl UseSegment {
if name.is_empty() || name == "{{root}}" {
return None;
}
Some(match name {
"self" => UseSegment::Slf(None),
"super" => UseSegment::Super(None),
"crate" => UseSegment::Crate(None),
let kind = match name {
"self" => UseSegmentKind::Slf(None),
"super" => UseSegmentKind::Super(None),
"crate" => UseSegmentKind::Crate(None),
_ => {
let mod_sep = if modsep { "::" } else { "" };
UseSegment::Ident(format!("{}{}", mod_sep, name), None)
UseSegmentKind::Ident(format!("{}{}", mod_sep, name), None)
}
};
Some(UseSegment {
kind,
version: context.config.version(),
})
}
fn contains_comment(&self) -> bool {
if let UseSegment::List(list) = self {
if let UseSegmentKind::List(list) = &self.kind {
list.iter().any(|subtree| subtree.contains_comment())
} else {
false
@ -254,20 +269,38 @@ impl fmt::Debug for UseTree {
impl fmt::Debug for UseSegment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
fmt::Display::fmt(&self.kind, f)
}
}
impl fmt::Display for UseSegment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.kind, f)
}
}
impl Hash for UseSegment {
fn hash<H: Hasher>(&self, state: &mut H) {
self.kind.hash(state);
}
}
impl fmt::Debug for UseSegmentKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for UseSegmentKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
UseSegment::Glob => write!(f, "*"),
UseSegment::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias),
UseSegment::Ident(ref s, None) => write!(f, "{}", s),
UseSegment::Slf(..) => write!(f, "self"),
UseSegment::Super(..) => write!(f, "super"),
UseSegment::Crate(..) => write!(f, "crate"),
UseSegment::List(ref list) => {
UseSegmentKind::Glob => write!(f, "*"),
UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias),
UseSegmentKind::Ident(ref s, None) => write!(f, "{}", s),
UseSegmentKind::Slf(..) => write!(f, "self"),
UseSegmentKind::Super(..) => write!(f, "super"),
UseSegmentKind::Crate(..) => write!(f, "crate"),
UseSegmentKind::List(ref list) => {
write!(f, "{{")?;
for (i, item) in list.iter().enumerate() {
if i != 0 {
@ -411,13 +444,19 @@ impl UseTree {
}
}
let version = context.config.version();
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));
let kind = UseSegmentKind::Ident("".to_owned(), None);
result.path.push(UseSegment { kind, version });
}
result.path.push(UseSegment::Glob);
result.path.push(UseSegment {
kind: UseSegmentKind::Glob,
version,
});
}
UseTreeKind::Nested(ref list) => {
// Extract comments between nested use items.
@ -438,16 +477,18 @@ impl UseTree {
// 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));
let kind = UseSegmentKind::Ident("".to_owned(), None);
result.path.push(UseSegment { kind, version });
}
result.path.push(UseSegment::List(
let kind = UseSegmentKind::List(
list.iter()
.zip(items)
.map(|(t, list_item)| {
Self::from_ast(context, &t.0, Some(list_item), None, None, None)
})
.collect(),
));
);
result.path.push(UseSegment { kind, version });
}
UseTreeKind::Simple(ref rename, ..) => {
// If the path has leading double colons and is composed of only 2 segments, then we
@ -469,13 +510,15 @@ impl UseTree {
Some(rewrite_ident(context, ident).to_owned())
}
});
let segment = match name.as_ref() {
"self" => UseSegment::Slf(alias),
"super" => UseSegment::Super(alias),
"crate" => UseSegment::Crate(alias),
_ => UseSegment::Ident(name, alias),
let kind = match name.as_ref() {
"self" => UseSegmentKind::Slf(alias),
"super" => UseSegmentKind::Super(alias),
"crate" => UseSegmentKind::Crate(alias),
_ => UseSegmentKind::Ident(name, alias),
};
let segment = UseSegment { kind, version };
// `name` is already in result.
result.path.pop();
result.path.push(segment);
@ -492,13 +535,13 @@ impl UseTree {
let mut aliased_self = false;
// Remove foo::{} or self without attributes.
match last {
match last.kind {
_ if self.attrs.is_some() => (),
UseSegment::List(ref list) if list.is_empty() => {
UseSegmentKind::List(ref list) if list.is_empty() => {
self.path = vec![];
return self;
}
UseSegment::Slf(None) if self.path.is_empty() && self.visibility.is_some() => {
UseSegmentKind::Slf(None) if self.path.is_empty() && self.visibility.is_some() => {
self.path = vec![];
return self;
}
@ -506,15 +549,19 @@ impl UseTree {
}
// Normalise foo::self -> foo.
if let UseSegment::Slf(None) = last {
if let UseSegmentKind::Slf(None) = last.kind {
if !self.path.is_empty() {
return self;
}
}
// Normalise foo::self as bar -> foo as bar.
if let UseSegment::Slf(_) = last {
if let Some(UseSegment::Ident(_, None)) = self.path.last() {
if let UseSegmentKind::Slf(_) = last.kind {
if let Some(UseSegment {
kind: UseSegmentKind::Ident(_, None),
..
}) = self.path.last()
{
aliased_self = true;
}
}
@ -522,9 +569,12 @@ impl UseTree {
let mut done = false;
if aliased_self {
match self.path.last_mut() {
Some(UseSegment::Ident(_, ref mut old_rename)) => {
Some(UseSegment {
kind: UseSegmentKind::Ident(_, ref mut old_rename),
..
}) => {
assert!(old_rename.is_none());
if let UseSegment::Slf(Some(rename)) = last.clone() {
if let UseSegmentKind::Slf(Some(rename)) = last.clone().kind {
*old_rename = Some(rename);
done = true;
}
@ -538,15 +588,15 @@ impl UseTree {
}
// Normalise foo::{bar} -> foo::bar
if let UseSegment::List(ref list) = last {
if let UseSegmentKind::List(ref list) = last.kind {
if list.len() == 1 && list[0].to_string() != "self" {
normalize_sole_list = true;
}
}
if normalize_sole_list {
match last {
UseSegment::List(list) => {
match last.kind {
UseSegmentKind::List(list) => {
for seg in &list[0].path {
self.path.push(seg.clone());
}
@ -557,10 +607,13 @@ impl UseTree {
}
// Recursively normalize elements of a list use (including sorting the list).
if let UseSegment::List(list) = last {
if let UseSegmentKind::List(list) = last.kind {
let mut list = list.into_iter().map(UseTree::normalize).collect::<Vec<_>>();
list.sort();
last = UseSegment::List(list);
last = UseSegment {
kind: UseSegmentKind::List(list),
version: last.version,
};
}
self.path.push(last);
@ -620,10 +673,10 @@ impl UseTree {
if self.path.is_empty() || self.contains_comment() {
return vec![self];
}
match self.path.clone().last().unwrap() {
UseSegment::List(list) => {
match &self.path.clone().last().unwrap().kind {
UseSegmentKind::List(list) => {
if list.len() == 1 && list[0].path.len() == 1 {
if let UseSegment::Slf(..) = list[0].path[0] {
if let UseSegmentKind::Slf(..) = list[0].path[0].kind {
return vec![self];
};
}
@ -671,12 +724,15 @@ impl UseTree {
/// If this tree ends in `::self`, rewrite it to `::{self}`.
fn nest_trailing_self(mut self) -> UseTree {
if let Some(UseSegment::Slf(..)) = self.path.last() {
if let Some(UseSegment {
kind: UseSegmentKind::Slf(..),
..
}) = self.path.last()
{
let self_segment = self.path.pop().unwrap();
self.path.push(UseSegment::List(vec![UseTree::from_path(
vec![self_segment],
DUMMY_SP,
)]));
let version = self_segment.version;
let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]);
self.path.push(UseSegment { kind, version });
}
self
}
@ -692,7 +748,8 @@ fn merge_rest(
return None;
}
if a.len() != len && b.len() != len {
if let UseSegment::List(ref list) = a[len] {
let version = a[len].version;
if let UseSegmentKind::List(ref list) = a[len].kind {
let mut list = list.clone();
merge_use_trees_inner(
&mut list,
@ -700,7 +757,8 @@ fn merge_rest(
merge_by,
);
let mut new_path = b[..len].to_vec();
new_path.push(UseSegment::List(list));
let kind = UseSegmentKind::List(list);
new_path.push(UseSegment { kind, version });
return Some(new_path);
}
} else if len == 1 {
@ -709,15 +767,28 @@ fn merge_rest(
} else {
(&b[0], &a[1..])
};
let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string));
let version = a[0].version;
let mut list = vec![UseTree::from_path(
vec![UseSegment::Slf(common.get_alias().map(ToString::to_string))],
vec![UseSegment { kind, version }],
DUMMY_SP,
)];
match rest {
[UseSegment::List(rest_list)] => list.extend(rest_list.clone()),
[
UseSegment {
kind: UseSegmentKind::List(rest_list),
..
},
] => list.extend(rest_list.clone()),
_ => list.push(UseTree::from_path(rest.to_vec(), DUMMY_SP)),
}
return Some(vec![b[0].clone(), UseSegment::List(list)]);
return Some(vec![
b[0].clone(),
UseSegment {
kind: UseSegmentKind::List(list),
version,
},
]);
} else {
len -= 1;
}
@ -727,7 +798,9 @@ fn merge_rest(
];
list.sort();
let mut new_path = b[..len].to_vec();
new_path.push(UseSegment::List(list));
let kind = UseSegmentKind::List(list);
let version = a[0].version;
new_path.push(UseSegment { kind, version });
Some(new_path)
}
@ -805,24 +878,24 @@ impl PartialOrd for UseTree {
}
impl Ord for UseSegment {
fn cmp(&self, other: &UseSegment) -> Ordering {
use self::UseSegment::*;
use self::UseSegmentKind::*;
fn is_upper_snake_case(s: &str) -> bool {
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))
| (&Crate(ref a), &Crate(ref b)) => match (a, b) {
match (&self.kind, &other.kind) {
(Slf(ref a), Slf(ref b))
| (Super(ref a), Super(ref b))
| (Crate(ref a), Crate(ref b)) => match (a, b) {
(Some(sa), Some(sb)) => {
sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#"))
}
(_, _) => a.cmp(b),
},
(&Glob, &Glob) => Ordering::Equal,
(&Ident(ref pia, ref aa), &Ident(ref pib, ref ab)) => {
(Glob, Glob) => Ordering::Equal,
(Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => {
let ia = pia.trim_start_matches("r#");
let ib = pib.trim_start_matches("r#");
// snake_case < CamelCase < UPPER_SNAKE_CASE
@ -851,7 +924,7 @@ impl Ord for UseSegment {
(None, None) => Ordering::Equal,
}
}
(&List(ref a), &List(ref b)) => {
(List(ref a), List(ref b)) => {
for (a, b) in a.iter().zip(b.iter()) {
let ord = a.cmp(b);
if ord != Ordering::Equal {
@ -861,16 +934,16 @@ impl Ord for UseSegment {
a.len().cmp(&b.len())
}
(&Slf(_), _) => Ordering::Less,
(_, &Slf(_)) => Ordering::Greater,
(&Super(_), _) => Ordering::Less,
(_, &Super(_)) => Ordering::Greater,
(&Crate(_), _) => Ordering::Less,
(_, &Crate(_)) => Ordering::Greater,
(&Ident(..), _) => Ordering::Less,
(_, &Ident(..)) => Ordering::Greater,
(&Glob, _) => Ordering::Less,
(_, &Glob) => Ordering::Greater,
(Slf(_), _) => Ordering::Less,
(_, Slf(_)) => Ordering::Greater,
(Super(_), _) => Ordering::Less,
(_, Super(_)) => Ordering::Greater,
(Crate(_), _) => Ordering::Less,
(_, Crate(_)) => Ordering::Greater,
(Ident(..), _) => Ordering::Less,
(_, Ident(..)) => Ordering::Greater,
(Glob, _) => Ordering::Less,
(_, Glob) => Ordering::Greater,
}
}
}
@ -914,7 +987,7 @@ fn rewrite_nested_use_tree(
}
let has_nested_list = use_tree_list.iter().any(|use_segment| {
use_segment.path.last().map_or(false, |last_segment| {
matches!(last_segment, UseSegment::List(..))
matches!(last_segment.kind, UseSegmentKind::List(..))
})
});
@ -965,17 +1038,19 @@ fn rewrite_nested_use_tree(
impl Rewrite for UseSegment {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
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(
Some(match self.kind {
UseSegmentKind::Ident(ref ident, Some(ref rename)) => {
format!("{} as {}", ident, rename)
}
UseSegmentKind::Ident(ref ident, None) => ident.clone(),
UseSegmentKind::Slf(Some(ref rename)) => format!("self as {}", rename),
UseSegmentKind::Slf(None) => "self".to_owned(),
UseSegmentKind::Super(Some(ref rename)) => format!("super as {}", rename),
UseSegmentKind::Super(None) => "super".to_owned(),
UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {}", rename),
UseSegmentKind::Crate(None) => "crate".to_owned(),
UseSegmentKind::Glob => "*".to_owned(),
UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree(
context,
use_tree_list,
// 1 = "{" and "}"
@ -1024,6 +1099,7 @@ mod test {
struct Parser<'a> {
input: Peekable<Chars<'a>>,
version: Version,
}
impl<'a> Parser<'a> {
@ -1036,34 +1112,40 @@ mod test {
}
fn push_segment(
&self,
result: &mut Vec<UseSegment>,
buf: &mut String,
alias_buf: &mut Option<String>,
) {
let version = self.version;
if !buf.is_empty() {
let mut alias = None;
swap(alias_buf, &mut alias);
match buf.as_ref() {
"self" => {
result.push(UseSegment::Slf(alias));
let kind = UseSegmentKind::Slf(alias);
result.push(UseSegment { kind, version });
*buf = String::new();
*alias_buf = None;
}
"super" => {
result.push(UseSegment::Super(alias));
let kind = UseSegmentKind::Super(alias);
result.push(UseSegment { kind, version });
*buf = String::new();
*alias_buf = None;
}
"crate" => {
result.push(UseSegment::Crate(alias));
let kind = UseSegmentKind::Crate(alias);
result.push(UseSegment { kind, version });
*buf = String::new();
*alias_buf = None;
}
_ => {
let mut name = String::new();
swap(buf, &mut name);
result.push(UseSegment::Ident(name, alias));
let kind = UseSegmentKind::Ident(name, alias);
result.push(UseSegment { kind, version });
}
}
}
@ -1078,21 +1160,29 @@ mod test {
'{' => {
assert!(buf.is_empty());
self.bump();
result.push(UseSegment::List(self.parse_list()));
let kind = UseSegmentKind::List(self.parse_list());
result.push(UseSegment {
kind,
version: self.version,
});
self.eat('}');
}
'*' => {
assert!(buf.is_empty());
self.bump();
result.push(UseSegment::Glob);
let kind = UseSegmentKind::Glob;
result.push(UseSegment {
kind,
version: self.version,
});
}
':' => {
self.bump();
self.eat(':');
Self::push_segment(&mut result, &mut buf, &mut alias_buf);
self.push_segment(&mut result, &mut buf, &mut alias_buf);
}
'}' | ',' => {
Self::push_segment(&mut result, &mut buf, &mut alias_buf);
self.push_segment(&mut result, &mut buf, &mut alias_buf);
return UseTree {
path: result,
span: DUMMY_SP,
@ -1118,7 +1208,7 @@ mod test {
}
}
}
Self::push_segment(&mut result, &mut buf, &mut alias_buf);
self.push_segment(&mut result, &mut buf, &mut alias_buf);
UseTree {
path: result,
span: DUMMY_SP,
@ -1144,6 +1234,7 @@ mod test {
let mut parser = Parser {
input: s.chars().peekable(),
version: Version::One,
};
parser.parse_in_list()
}

View file

@ -12,7 +12,7 @@ use rustc_ast::ast;
use rustc_span::{symbol::sym, Span};
use crate::config::{Config, GroupImportsTactic};
use crate::imports::{normalize_use_trees_with_granularity, UseSegment, UseTree};
use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree};
use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod};
use crate::lists::{itemize_list, write_list, ListFormatting, ListItem};
use crate::rewrite::RewriteContext;
@ -182,16 +182,16 @@ fn group_imports(uts: Vec<UseTree>) -> Vec<Vec<UseTree>> {
external_imports.push(ut);
continue;
}
match &ut.path[0] {
UseSegment::Ident(id, _) => match id.as_ref() {
match &ut.path[0].kind {
UseSegmentKind::Ident(id, _) => match id.as_ref() {
"std" | "alloc" | "core" => std_imports.push(ut),
_ => external_imports.push(ut),
},
UseSegment::Slf(_) | UseSegment::Super(_) | UseSegment::Crate(_) => {
UseSegmentKind::Slf(_) | UseSegmentKind::Super(_) | UseSegmentKind::Crate(_) => {
local_imports.push(ut)
}
// These are probably illegal here
UseSegment::Glob | UseSegment::List(_) => external_imports.push(ut),
UseSegmentKind::Glob | UseSegmentKind::List(_) => external_imports.push(ut),
}
}