Skip to content

Commit 0e38673

Browse files
NoratriebDylan-DPC
authored andcommitted
Add tidy directoy tidy-alphabetical
It can be used to ensure that a list of things is sorted alphabetically. It goes off lines, but contains several heuristics to work with normal Rust code (looking at indentation, ignoring comments and attributes).
1 parent e6ce562 commit 0e38673

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed

src/tools/tidy/src/alphabetical.rs

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! Checks that a list of items is in alphabetical order
2+
//!
3+
//! To use, use the following annotation in the code:
4+
//! ```rust
5+
//! // tidy-alphabetical-start
6+
//! fn aaa() {}
7+
//! fn eee() {}
8+
//! fn z() {}
9+
//! // tidy-alphabetical-end
10+
//! ```
11+
//!
12+
//! The following lines are ignored:
13+
//! - Lines that are indented with more or less spaces than the first line
14+
//! - Lines starting with `//`, `#[`, `)`, `]`, `}` if the comment has the same indentation as
15+
//! the first line
16+
//!
17+
//! If a line ends with an opening bracket, the line is ignored and the next line will have
18+
//! its extra indentation ignored.
19+
20+
use std::{fmt::Display, path::Path};
21+
22+
use crate::walk::{filter_dirs, walk};
23+
24+
fn indentation(line: &str) -> usize {
25+
line.find(|c| c != ' ').unwrap_or(0)
26+
}
27+
28+
fn is_close_bracket(c: char) -> bool {
29+
matches!(c, ')' | ']' | '}')
30+
}
31+
32+
fn check_section<'a>(
33+
file: impl Display,
34+
lines: impl Iterator<Item = (usize, &'a str)>,
35+
bad: &mut bool,
36+
) {
37+
let content_lines = lines.take_while(|(_, line)| !line.contains("// tidy-alphabetical-end"));
38+
39+
let mut prev_line = String::new();
40+
let mut first_indent = None;
41+
let mut in_split_line = None;
42+
43+
for (line_idx, line) in content_lines {
44+
let indent = first_indent.unwrap_or_else(|| {
45+
let indent = indentation(line);
46+
first_indent = Some(indent);
47+
indent
48+
});
49+
50+
let line = if let Some(prev_split_line) = in_split_line {
51+
in_split_line = None;
52+
format!("{prev_split_line}{}", line.trim_start())
53+
} else {
54+
line.to_string()
55+
};
56+
57+
if indentation(&line) != indent {
58+
continue;
59+
}
60+
61+
let trimmed_line = line.trim_start_matches(' ');
62+
63+
if trimmed_line.starts_with("//")
64+
|| trimmed_line.starts_with("#[")
65+
|| trimmed_line.starts_with(is_close_bracket)
66+
{
67+
continue;
68+
}
69+
70+
if line.trim_end().ends_with('(') {
71+
in_split_line = Some(line);
72+
continue;
73+
}
74+
75+
let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' ').to_lowercase();
76+
77+
if trimmed_line.to_lowercase() < prev_line_trimmed_lowercase {
78+
tidy_error!(bad, "{file}:{}: line not in alphabetical order", line_idx + 1,);
79+
}
80+
81+
prev_line = line;
82+
}
83+
}
84+
85+
const START_COMMENT: &str = "// tidy-alphabetical-start";
86+
87+
pub fn check(path: &Path, bad: &mut bool) {
88+
walk(path, &mut filter_dirs, &mut |entry, contents| {
89+
let file = &entry.path().display();
90+
91+
let mut lines = contents.lines().enumerate();
92+
while let Some((_, line)) = lines.next() {
93+
if line.contains(START_COMMENT) {
94+
check_section(file, &mut lines, bad);
95+
}
96+
}
97+
});
98+
}

src/tools/tidy/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ macro_rules! tidy_error {
3838
});
3939
}
4040

41+
pub mod alphabetical;
4142
pub mod bins;
4243
pub mod debug_artifacts;
4344
pub mod deps;

src/tools/tidy/src/main.rs

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ fn main() {
9090
check!(edition, &compiler_path);
9191
check!(edition, &library_path);
9292

93+
check!(alphabetical, &compiler_path);
94+
9395
let collected = {
9496
while handles.len() >= concurrency.get() {
9597
handles.pop_front().unwrap().join().unwrap();

0 commit comments

Comments
 (0)