Skip to content

Commit 7130bf7

Browse files
committed
Add #[rustfmt::sort] for enum variants
1 parent 06e1644 commit 7130bf7

File tree

6 files changed

+94
-11
lines changed

6 files changed

+94
-11
lines changed

src/items.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Formatting top-level items - functions, structs, enums, traits, impls.
22

3-
use std::borrow::Cow;
4-
use std::cmp::{Ordering, max, min};
5-
3+
use itertools::Itertools;
64
use regex::Regex;
7-
use rustc_ast::visit;
5+
use rustc_ast::{AttrVec, visit};
86
use rustc_ast::{ast, ptr};
97
use rustc_span::{BytePos, DUMMY_SP, Span, symbol};
8+
use std::borrow::Cow;
9+
use std::cmp::{Ordering, max, min};
1010

1111
use crate::attr::filter_inline_attrs;
1212
use crate::comment::{
@@ -536,6 +536,7 @@ impl<'a> FmtVisitor<'a> {
536536
enum_def: &ast::EnumDef,
537537
generics: &ast::Generics,
538538
span: Span,
539+
attrs: &AttrVec,
539540
) {
540541
let enum_header =
541542
format_header(&self.get_context(), "enum ", ident, vis, self.block_indent);
@@ -563,7 +564,7 @@ impl<'a> FmtVisitor<'a> {
563564

564565
self.last_pos = body_start;
565566

566-
match self.format_variant_list(enum_def, body_start, span.hi()) {
567+
match self.format_variant_list(enum_def, body_start, span.hi(), attrs) {
567568
Some(ref s) if enum_def.variants.is_empty() => self.push_str(s),
568569
rw => {
569570
self.push_rewrite(mk_sp(body_start, span.hi()), rw);
@@ -578,6 +579,7 @@ impl<'a> FmtVisitor<'a> {
578579
enum_def: &ast::EnumDef,
579580
body_lo: BytePos,
580581
body_hi: BytePos,
582+
attrs: &AttrVec,
581583
) -> Option<String> {
582584
if enum_def.variants.is_empty() {
583585
let mut buffer = String::with_capacity(128);
@@ -615,7 +617,7 @@ impl<'a> FmtVisitor<'a> {
615617
.unwrap_or(&0);
616618

617619
let itemize_list_with = |one_line_width: usize| {
618-
itemize_list(
620+
let iter = itemize_list(
619621
self.snippet_provider,
620622
enum_def.variants.iter(),
621623
"}",
@@ -635,8 +637,16 @@ impl<'a> FmtVisitor<'a> {
635637
body_lo,
636638
body_hi,
637639
false,
638-
)
639-
.collect()
640+
);
641+
if contains_sort(attrs) {
642+
// sort the items by their name as this enum has the rustfmt::sort attr
643+
iter.enumerate()
644+
.sorted_by_key(|&(i, _)| enum_def.variants[i].ident.name.as_str())
645+
.map(|(_, item)| item)
646+
.collect()
647+
} else {
648+
iter.collect()
649+
}
640650
};
641651
let mut items: Vec<_> = itemize_list_with(self.config.struct_variant_width());
642652

src/skip.rs

+11
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ impl SkipNameContext {
8585

8686
static RUSTFMT: &str = "rustfmt";
8787
static SKIP: &str = "skip";
88+
static SORT: &str = "sort";
8889

8990
/// Say if you're playing with `rustfmt`'s skip attribute
9091
pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool {
@@ -103,6 +104,16 @@ pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool {
103104
}
104105
}
105106

107+
pub(crate) fn is_sort_attr(segments: &[ast::PathSegment]) -> bool {
108+
if segments.len() < 2 || segments[0].ident.to_string() != RUSTFMT {
109+
return false;
110+
}
111+
match segments.len() {
112+
2 => segments[1].ident.to_string() == SORT,
113+
_ => false,
114+
}
115+
}
116+
106117
fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec<String> {
107118
let mut skip_names = vec![];
108119
let path = format!("{RUSTFMT}::{SKIP}::{kind}");

src/utils.rs

+34
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ pub(crate) fn skip_annotation() -> Symbol {
2424
Symbol::intern("rustfmt::skip")
2525
}
2626

27+
#[inline]
28+
pub(crate) fn sort_annotation() -> Symbol {
29+
Symbol::intern("rustfmt::sort")
30+
}
31+
2732
pub(crate) fn rewrite_ident<'a>(context: &'a RewriteContext<'_>, ident: symbol::Ident) -> &'a str {
2833
context.snippet(ident.span)
2934
}
@@ -271,6 +276,35 @@ pub(crate) fn contains_skip(attrs: &[Attribute]) -> bool {
271276
.any(|a| a.meta().map_or(false, |a| is_skip(&a)))
272277
}
273278

279+
#[inline]
280+
pub(crate) fn contains_sort(attrs: &[Attribute]) -> bool {
281+
attrs
282+
.iter()
283+
.any(|a| a.meta().map_or(false, |a| is_sort(&a)))
284+
}
285+
286+
#[inline]
287+
fn is_sort(meta_item: &MetaItem) -> bool {
288+
match meta_item.kind {
289+
MetaItemKind::Word => {
290+
let path_str = pprust::path_to_string(&meta_item.path);
291+
path_str == sort_annotation().as_str()
292+
}
293+
MetaItemKind::List(ref l) => {
294+
meta_item.has_name(sym::cfg_attr) && l.len() == 2 && crate::utils::is_sort_nested(&l[1])
295+
}
296+
_ => false,
297+
}
298+
}
299+
300+
#[inline]
301+
fn is_sort_nested(meta_item: &NestedMetaItem) -> bool {
302+
match meta_item {
303+
NestedMetaItem::MetaItem(ref mi) => crate::utils::is_sort(mi),
304+
NestedMetaItem::Lit(_) => false,
305+
}
306+
}
307+
274308
#[inline]
275309
pub(crate) fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr) -> bool {
276310
// Never try to insert semicolons on expressions when we're inside

src/visitor.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::modules::Module;
1818
use crate::parse::session::ParseSess;
1919
use crate::rewrite::{Rewrite, RewriteContext};
2020
use crate::shape::{Indent, Shape};
21-
use crate::skip::{SkipContext, is_skip_attr};
21+
use crate::skip::{SkipContext, is_skip_attr, is_sort_attr};
2222
use crate::source_map::{LineRangeUtils, SpanUtils};
2323
use crate::spanned::Spanned;
2424
use crate::stmt::Stmt;
@@ -515,7 +515,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
515515
}
516516
ast::ItemKind::Enum(ref def, ref generics) => {
517517
self.format_missing_with_indent(source!(self, item.span).lo());
518-
self.visit_enum(item.ident, &item.vis, def, generics, item.span);
518+
self.visit_enum(item.ident, &item.vis, def, generics, item.span, &item.attrs);
519519
self.last_pos = source!(self, item.span).hi();
520520
}
521521
ast::ItemKind::Mod(safety, ref mod_kind) => {
@@ -858,7 +858,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
858858
if segments[0].ident.to_string() != "rustfmt" {
859859
return false;
860860
}
861-
!is_skip_attr(segments)
861+
!(is_skip_attr(segments) | is_sort_attr(segments))
862862
}
863863

864864
fn walk_mod_items(&mut self, items: &[rustc_ast::ptr::P<ast::Item>]) {

tests/source/enum.rs

+14
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,17 @@ pub enum E {
210210
A { a: u32 } = 0x100,
211211
B { field1: u32, field2: u8, field3: m::M } = 0x300 // comment
212212
}
213+
214+
// #3422
215+
#[rustfmt::sort]
216+
enum SortE {
217+
E,
218+
// something
219+
D(),
220+
C(),
221+
/// Comment for B
222+
B,
223+
/// Comment for A
224+
#[rustfmt::skip]
225+
A,
226+
}

tests/target/enum.rs

+14
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,17 @@ pub enum E {
287287
field3: m::M,
288288
} = 0x300, // comment
289289
}
290+
291+
// #3422
292+
#[rustfmt::sort]
293+
enum SortE {
294+
/// Comment for A
295+
#[rustfmt::skip]
296+
A,
297+
/// Comment for B
298+
B,
299+
C(),
300+
// something
301+
D(),
302+
E,
303+
}

0 commit comments

Comments
 (0)