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).
This commit is contained in:
Nilstrieb 2022-10-04 22:34:07 +02:00 committed by Dylan DPC
parent e6ce5627a9
commit 0e3867338d
3 changed files with 101 additions and 0 deletions

View File

@ -0,0 +1,98 @@
//! Checks that a list of items is in alphabetical order
//!
//! To use, use the following annotation in the code:
//! ```rust
//! // tidy-alphabetical-start
//! fn aaa() {}
//! fn eee() {}
//! fn z() {}
//! // tidy-alphabetical-end
//! ```
//!
//! The following lines are ignored:
//! - Lines that are indented with more or less spaces than the first line
//! - Lines starting with `//`, `#[`, `)`, `]`, `}` if the comment has the same indentation as
//! the first line
//!
//! If a line ends with an opening bracket, the line is ignored and the next line will have
//! its extra indentation ignored.
use std::{fmt::Display, path::Path};
use crate::walk::{filter_dirs, walk};
fn indentation(line: &str) -> usize {
line.find(|c| c != ' ').unwrap_or(0)
}
fn is_close_bracket(c: char) -> bool {
matches!(c, ')' | ']' | '}')
}
fn check_section<'a>(
file: impl Display,
lines: impl Iterator<Item = (usize, &'a str)>,
bad: &mut bool,
) {
let content_lines = lines.take_while(|(_, line)| !line.contains("// tidy-alphabetical-end"));
let mut prev_line = String::new();
let mut first_indent = None;
let mut in_split_line = None;
for (line_idx, line) in content_lines {
let indent = first_indent.unwrap_or_else(|| {
let indent = indentation(line);
first_indent = Some(indent);
indent
});
let line = if let Some(prev_split_line) = in_split_line {
in_split_line = None;
format!("{prev_split_line}{}", line.trim_start())
} else {
line.to_string()
};
if indentation(&line) != indent {
continue;
}
let trimmed_line = line.trim_start_matches(' ');
if trimmed_line.starts_with("//")
|| trimmed_line.starts_with("#[")
|| trimmed_line.starts_with(is_close_bracket)
{
continue;
}
if line.trim_end().ends_with('(') {
in_split_line = Some(line);
continue;
}
let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' ').to_lowercase();
if trimmed_line.to_lowercase() < prev_line_trimmed_lowercase {
tidy_error!(bad, "{file}:{}: line not in alphabetical order", line_idx + 1,);
}
prev_line = line;
}
}
const START_COMMENT: &str = "// tidy-alphabetical-start";
pub fn check(path: &Path, bad: &mut bool) {
walk(path, &mut filter_dirs, &mut |entry, contents| {
let file = &entry.path().display();
let mut lines = contents.lines().enumerate();
while let Some((_, line)) = lines.next() {
if line.contains(START_COMMENT) {
check_section(file, &mut lines, bad);
}
}
});
}

View File

@ -38,6 +38,7 @@ macro_rules! tidy_error {
});
}
pub mod alphabetical;
pub mod bins;
pub mod debug_artifacts;
pub mod deps;

View File

@ -90,6 +90,8 @@ fn main() {
check!(edition, &compiler_path);
check!(edition, &library_path);
check!(alphabetical, &compiler_path);
let collected = {
while handles.len() >= concurrency.get() {
handles.pop_front().unwrap().join().unwrap();