Skip to content

Imports reordering #4591

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

86 changes: 62 additions & 24 deletions src/formatting/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_span::{
use crate::config::lists::*;
use crate::config::{Edition, IndentStyle};
use crate::formatting::{
comment::combine_strs_with_missing_comments,
comment::{combine_strs_with_missing_comments, contains_comment},
lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator},
reorder::{compare_as_versions, compare_opt_ident_as_versions},
rewrite::{Rewrite, RewriteContext},
Expand Down Expand Up @@ -365,6 +365,19 @@ impl UseTree {
}
}

let rename_to_alias = |rename: &Option<symbol::Ident>| {
rename.and_then(|ident| {
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())
}
})
};

match a.kind {
UseTreeKind::Glob => {
// in case of a global path and the glob starts at the root, e.g., "::*"
Expand All @@ -389,19 +402,53 @@ impl UseTree {
false,
)
.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())
.map(|(t, list_item)| {
Self::from_ast(context, &t.0, Some(list_item), None, None, None)
})
.collect(),
));

// find whether a case of a global path and the nested list starts at the root
// with one item, e.g., "::{foo as bar}", and does not include comments.
let mut first_item = None;
let mut first_alias = None;
if a.prefix.segments.len() == 1 && list.len() == 1 && result.to_string().is_empty()
{
let first = &list[0].0;
match first.kind {
UseTreeKind::Simple(ref rename, ..) => {
// "-1" for the "}"
let snippet = context
.snippet(mk_sp(first.span.lo(), span.hi() - BytePos(1)))
.trim();
// Ensure that indent does not include comments
if !contains_comment(&snippet) {
first_item = Some(first);
first_alias = rename_to_alias(rename);
}
}
_ => {}
}
};

if let Some(first) = first_item {
// in case of a global path and the nested list starts at the root
// with one item, e.g., "::{foo as bar}"
let tree = Self::from_ast(context, first, None, None, None, None);
let mod_sep = if leading_modsep { "::" } else { "" };
let seg = UseSegment::Ident(format!("{}{}", mod_sep, tree), first_alias);
result.path.pop();
result.path.push(seg);
} else {
// 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())
.map(|(t, list_item)| {
Self::from_ast(context, &t.0, Some(list_item), None, None, None)
})
.collect(),
));
};
}
UseTreeKind::Simple(ref rename, ..) => {
// If the path has leading double colons and is composed of only 2 segments, then we
Expand All @@ -413,16 +460,7 @@ impl UseTree {
} else {
rewrite_ident(context, path_to_imported_ident(&a.prefix)).to_owned()
};
let alias = rename.and_then(|ident| {
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 alias = rename_to_alias(rename);
let segment = match name.as_ref() {
"self" => UseSegment::Slf(alias),
"super" => UseSegment::Super(alias),
Expand Down
44 changes: 44 additions & 0 deletions tests/source/issue-3943.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Tests for original #3943 issue
use ::foo;
use ::foo::{Bar1};
use ::foo::{Bar2, Baz2};
use ::{Foo};
use ::{Bar3, Baz3};

use ::foo;
use ::foo::Bar;
use ::foo::{Bar, Baz};
use ::Foo;
use ::{Bar, Baz};

use ::Foo;
use ::foo;
use ::foo::Bar;
use ::foo::{Bar, Baz};
use ::{Bar, Baz};

// Additional tests for signle item `{}` handling
use ::AAAA;
use bbbbb::AAAA;
use ::{BBBB};
use aaaa::{BBBB};
use crate::detect::{Feature, cache};
use super::{auxvec};

// Tests with comments
use a::{/* pre-comment */ item};
use a::{ item /* post-comment */};
use a::{/* pre-comment */ item /* post-comment */ };

// Misc
use ::{Foo};
use ::foo;
use ::{Foo1};
use std;
use dummy;
use Super::foo;
use ::*;
use ::foo::{Foo, foo};
use self::std::fs as self_fs;
use ::foo as bar;
use ::{Foo as baz};
44 changes: 44 additions & 0 deletions tests/target/issue-3943.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Tests for original #3943 issue
use ::Foo;
use ::foo;
use ::foo::Bar1;
use ::foo::{Bar2, Baz2};
use ::{Bar3, Baz3};

use ::Foo;
use ::foo;
use ::foo::Bar;
use ::foo::{Bar, Baz};
use ::{Bar, Baz};

use ::Foo;
use ::foo;
use ::foo::Bar;
use ::foo::{Bar, Baz};
use ::{Bar, Baz};

// Additional tests for signle item `{}` handling
use super::auxvec;
use crate::detect::{cache, Feature};
use ::AAAA;
use ::BBBB;
use aaaa::BBBB;
use bbbbb::AAAA;

// Tests with comments
use a::{/* pre-comment */ item};
use a::{item /* post-comment */};
use a::{/* pre-comment */ item /* post-comment */};

// Misc
use self::std::fs as self_fs;
use ::Foo;
use ::Foo as baz;
use ::Foo1;
use ::foo;
use ::foo as bar;
use ::foo::{foo, Foo};
use dummy;
use std;
use Super::foo;
use ::*;