From 0e3867338d66a016d6e6214a18537fbb715ed752 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 4 Oct 2022 22:34:07 +0200 Subject: [PATCH] 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). --- src/tools/tidy/src/alphabetical.rs | 98 ++++++++++++++++++++++++++++++ src/tools/tidy/src/lib.rs | 1 + src/tools/tidy/src/main.rs | 2 + 3 files changed, 101 insertions(+) create mode 100644 src/tools/tidy/src/alphabetical.rs diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs new file mode 100644 index 00000000000..e02b2d98f42 --- /dev/null +++ b/src/tools/tidy/src/alphabetical.rs @@ -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, + 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); + } + } + }); +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index e82cca402e2..fc0bce58572 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -38,6 +38,7 @@ macro_rules! tidy_error { }); } +pub mod alphabetical; pub mod bins; pub mod debug_artifacts; pub mod deps; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index c1ce94f4705..8fe361c45a2 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -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();