1use std::cmp::Ordering;
10
11use rustc_ast::{ast, attr};
12use rustc_span::{Span, symbol::sym};
13
14use crate::config::{Config, GroupImportsTactic};
15use crate::imports::{UseSegmentKind, UseTree, normalize_use_trees_with_granularity};
16use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod};
17use crate::lists::{ListFormatting, ListItem, itemize_list, write_list};
18use crate::rewrite::{RewriteContext, RewriteErrorExt};
19use crate::shape::Shape;
20use crate::source_map::LineRangeUtils;
21use crate::spanned::Spanned;
22use crate::utils::{contains_skip, mk_sp};
23use crate::visitor::FmtVisitor;
24
25fn compare_items(a: &ast::Item, b: &ast::Item) -> Ordering {
27 match (&a.kind, &b.kind) {
28 (&ast::ItemKind::Mod(_, a_ident, _), &ast::ItemKind::Mod(_, b_ident, _)) => {
29 a_ident.as_str().cmp(b_ident.as_str())
30 }
31 (
32 &ast::ItemKind::ExternCrate(ref a_name, a_ident),
33 &ast::ItemKind::ExternCrate(ref b_name, b_ident),
34 ) => {
35 let a_orig_name = a_name.unwrap_or(a_ident.name);
38 let b_orig_name = b_name.unwrap_or(b_ident.name);
39 let result = a_orig_name.as_str().cmp(b_orig_name.as_str());
40 if result != Ordering::Equal {
41 return result;
42 }
43
44 match (a_name, b_name) {
47 (Some(..), None) => Ordering::Greater,
48 (None, Some(..)) => Ordering::Less,
49 (None, None) => Ordering::Equal,
50 (Some(..), Some(..)) => a_ident.as_str().cmp(b_ident.as_str()),
51 }
52 }
53 _ => unreachable!(),
54 }
55}
56
57fn wrap_reorderable_items(
58 context: &RewriteContext<'_>,
59 list_items: &[ListItem],
60 shape: Shape,
61) -> Option<String> {
62 let fmt = ListFormatting::new(shape, context.config)
63 .separator("")
64 .align_comments(false);
65 write_list(list_items, &fmt).ok()
66}
67
68fn rewrite_reorderable_item(
69 context: &RewriteContext<'_>,
70 item: &ast::Item,
71 shape: Shape,
72) -> Option<String> {
73 match item.kind {
74 ast::ItemKind::ExternCrate(..) => rewrite_extern_crate(context, item, shape),
75 ast::ItemKind::Mod(_, ident, _) => rewrite_mod(context, item, ident, shape),
76 _ => None,
77 }
78}
79
80fn rewrite_reorderable_or_regroupable_items(
84 context: &RewriteContext<'_>,
85 reorderable_items: &[&ast::Item],
86 shape: Shape,
87 span: Span,
88) -> Option<String> {
89 match reorderable_items[0].kind {
90 ast::ItemKind::Use(..) => {
92 let mut normalized_items: Vec<_> = reorderable_items
93 .iter()
94 .filter_map(|item| UseTree::from_ast_with_normalization(context, item))
95 .collect();
96 let cloned = normalized_items.clone();
97 let list_items = itemize_list(
99 context.snippet_provider,
100 cloned.iter(),
101 "",
102 ";",
103 |item| item.span().lo(),
104 |item| item.span().hi(),
105 |_item| Ok("".to_owned()),
106 span.lo(),
107 span.hi(),
108 false,
109 );
110 for (item, list_item) in normalized_items.iter_mut().zip(list_items) {
111 item.list_item = Some(list_item.clone());
112 }
113 normalized_items = normalize_use_trees_with_granularity(
114 normalized_items,
115 context.config.imports_granularity(),
116 );
117
118 let mut regrouped_items = match context.config.group_imports() {
119 GroupImportsTactic::Preserve | GroupImportsTactic::One => {
120 vec![normalized_items]
121 }
122 GroupImportsTactic::StdExternalCrate => group_imports(normalized_items),
123 };
124
125 if context.config.reorder_imports() {
126 regrouped_items.iter_mut().for_each(|items| items.sort())
127 }
128
129 let nested_shape = shape.offset_left(4)?.sub_width(1)?;
131 let item_vec: Vec<_> = regrouped_items
132 .into_iter()
133 .filter(|use_group| !use_group.is_empty())
134 .map(|use_group| {
135 let item_vec: Vec<_> = use_group
136 .into_iter()
137 .map(|use_tree| {
138 let item = use_tree.rewrite_top_level(context, nested_shape);
139 if let Some(list_item) = use_tree.list_item {
140 ListItem {
141 item: item,
142 ..list_item
143 }
144 } else {
145 ListItem::from_item(item)
146 }
147 })
148 .collect();
149 wrap_reorderable_items(context, &item_vec, nested_shape)
150 })
151 .collect::<Option<Vec<_>>>()?;
152
153 let join_string = format!("\n\n{}", shape.indent.to_string(context.config));
154 Some(item_vec.join(&join_string))
155 }
156 _ => {
157 let list_items = itemize_list(
158 context.snippet_provider,
159 reorderable_items.iter(),
160 "",
161 ";",
162 |item| item.span().lo(),
163 |item| item.span().hi(),
164 |item| rewrite_reorderable_item(context, item, shape).unknown_error(),
165 span.lo(),
166 span.hi(),
167 false,
168 );
169
170 let mut item_pair_vec: Vec<_> = list_items.zip(reorderable_items.iter()).collect();
171 item_pair_vec.sort_by(|a, b| compare_items(a.1, b.1));
172 let item_vec: Vec<_> = item_pair_vec.into_iter().map(|pair| pair.0).collect();
173
174 wrap_reorderable_items(context, &item_vec, shape)
175 }
176 }
177}
178
179fn contains_macro_use_attr(item: &ast::Item) -> bool {
180 attr::contains_name(&item.attrs, sym::macro_use)
181}
182
183fn group_imports(uts: Vec<UseTree>) -> Vec<Vec<UseTree>> {
186 let mut std_imports = Vec::new();
187 let mut external_imports = Vec::new();
188 let mut local_imports = Vec::new();
189
190 for ut in uts.into_iter() {
191 if ut.path.is_empty() {
192 external_imports.push(ut);
193 continue;
194 }
195 match &ut.path[0].kind {
196 UseSegmentKind::Ident(id, _) => match id.as_ref() {
197 "std" | "alloc" | "core" => std_imports.push(ut),
198 _ => external_imports.push(ut),
199 },
200 UseSegmentKind::Slf(_) | UseSegmentKind::Super(_) | UseSegmentKind::Crate(_) => {
201 local_imports.push(ut)
202 }
203 UseSegmentKind::Glob | UseSegmentKind::List(_) => external_imports.push(ut),
205 }
206 }
207
208 vec![std_imports, external_imports, local_imports]
209}
210
211#[derive(Debug, PartialEq, Eq, Copy, Clone)]
213enum ReorderableItemKind {
214 ExternCrate,
215 Mod,
216 Use,
217 Other,
220}
221
222impl ReorderableItemKind {
223 fn from(item: &ast::Item) -> Self {
224 match item.kind {
225 _ if contains_macro_use_attr(item) | contains_skip(&item.attrs) => {
226 ReorderableItemKind::Other
227 }
228 ast::ItemKind::ExternCrate(..) => ReorderableItemKind::ExternCrate,
229 ast::ItemKind::Mod(..) if is_mod_decl(item) => ReorderableItemKind::Mod,
230 ast::ItemKind::Use(..) => ReorderableItemKind::Use,
231 _ => ReorderableItemKind::Other,
232 }
233 }
234
235 fn is_same_item_kind(self, item: &ast::Item) -> bool {
236 ReorderableItemKind::from(item) == self
237 }
238
239 fn is_reorderable(self, config: &Config) -> bool {
240 match self {
241 ReorderableItemKind::ExternCrate => config.reorder_imports(),
242 ReorderableItemKind::Mod => config.reorder_modules(),
243 ReorderableItemKind::Use => config.reorder_imports(),
244 ReorderableItemKind::Other => false,
245 }
246 }
247
248 fn is_regroupable(self, config: &Config) -> bool {
249 match self {
250 ReorderableItemKind::ExternCrate
251 | ReorderableItemKind::Mod
252 | ReorderableItemKind::Other => false,
253 ReorderableItemKind::Use => config.group_imports() != GroupImportsTactic::Preserve,
254 }
255 }
256
257 fn in_group(self, config: &Config) -> bool {
258 match self {
259 ReorderableItemKind::ExternCrate | ReorderableItemKind::Mod => true,
260 ReorderableItemKind::Use => config.group_imports() == GroupImportsTactic::Preserve,
261 ReorderableItemKind::Other => false,
262 }
263 }
264}
265
266impl<'b, 'a: 'b> FmtVisitor<'a> {
267 fn walk_reorderable_or_regroupable_items(
271 &mut self,
272 items: &[&ast::Item],
273 item_kind: ReorderableItemKind,
274 in_group: bool,
275 ) -> usize {
276 let mut last = self.psess.lookup_line_range(items[0].span());
277 let item_length = items
278 .iter()
279 .take_while(|ppi| {
280 item_kind.is_same_item_kind(&***ppi)
281 && (!in_group || {
282 let current = self.psess.lookup_line_range(ppi.span());
283 let in_same_group = current.lo < last.hi + 2;
284 last = current;
285 in_same_group
286 })
287 })
288 .count();
289 let items = &items[..item_length];
290
291 let at_least_one_in_file_lines = items
292 .iter()
293 .any(|item| !out_of_file_lines_range!(self, item.span));
294
295 if at_least_one_in_file_lines && !items.is_empty() {
296 let lo = items.first().unwrap().span().lo();
297 let hi = items.last().unwrap().span().hi();
298 let span = mk_sp(lo, hi);
299 let rw = rewrite_reorderable_or_regroupable_items(
300 &self.get_context(),
301 items,
302 self.shape(),
303 span,
304 );
305 self.push_rewrite(span, rw);
306 } else {
307 for item in items {
308 self.push_rewrite(item.span, None);
309 }
310 }
311
312 item_length
313 }
314
315 pub(crate) fn visit_items_with_reordering(&mut self, mut items: &[&ast::Item]) {
318 while !items.is_empty() {
319 let item_kind = ReorderableItemKind::from(items[0]);
323 if item_kind.is_reorderable(self.config) || item_kind.is_regroupable(self.config) {
324 let visited_items_num = self.walk_reorderable_or_regroupable_items(
325 items,
326 item_kind,
327 item_kind.in_group(self.config),
328 );
329 let (_, rest) = items.split_at(visited_items_num);
330 items = rest;
331 } else {
332 let (item, rest) = items.split_first().unwrap();
335 self.visit_item(item);
336 items = rest;
337 }
338 }
339 }
340}