Files
Florian Bartels b93e8844ea style: Enforce order of any s_*! macro call
Before this change, only the order of `s!` was checked.
After, it also checks `s_no_extra_traits!` and `s_paren!`.
Only the order is checked, not the number of calls. This is
required because multiple calls have to be allowed.
2022-10-28 10:18:13 +02:00

208 lines
5.3 KiB
Rust

//! Simple script to verify the coding style of this library
//!
//! ## How to run
//!
//! The first argument to this script is the directory to run on, so running
//! this script should be as simple as:
//!
//! ```notrust
//! rustc ci/style.rs
//! ./style src
//! ```
//!
//! ## Guidelines
//!
//! The current style is:
//!
//! * Specific module layout:
//! 1. use directives
//! 2. typedefs
//! 3. structs
//! 4. constants
//! 5. f! { ... } functions
//! 6. extern functions
//! 7. modules + pub use
//!
//! Things not verified:
//!
//! * alignment
//! * leading colons on paths
use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
}
fn main() {
let arg = env::args().skip(1).next().unwrap_or(".".to_string());
let mut errors = Errors { errs: false };
walk(Path::new(&arg), &mut errors);
if errors.errs {
panic!("found some lint errors");
} else {
println!("good style!");
}
}
fn walk(path: &Path, err: &mut Errors) {
for entry in t!(path.read_dir()).map(|e| t!(e)) {
let path = entry.path();
if t!(entry.file_type()).is_dir() {
walk(&path, err);
continue;
}
let name = entry.file_name().into_string().unwrap();
match &name[..] {
n if !n.ends_with(".rs") => continue,
"lib.rs" | "macros.rs" => continue,
_ => {}
}
let mut contents = String::new();
t!(t!(fs::File::open(&path)).read_to_string(&mut contents));
check_style(&contents, &path, err);
}
}
struct Errors {
errs: bool,
}
#[derive(Clone, Copy, PartialEq)]
enum State {
Start,
Imports,
Typedefs,
Structs,
Constants,
FunctionDefinitions,
Functions,
Modules,
}
fn check_style(file: &str, path: &Path, err: &mut Errors) {
let mut state = State::Start;
let mut s_macros = 0;
let mut f_macros = 0;
let mut in_impl = false;
for (i, line) in file.lines().enumerate() {
if line.contains("#[cfg(")
&& line.contains(']')
&& !line.contains(" if ")
&& !(line.contains("target_endian") || line.contains("target_arch"))
{
if state != State::Structs {
err.error(path, i, "use cfg_if! and submodules instead of #[cfg]");
}
}
if line.contains("#[derive(") && (line.contains("Copy") || line.contains("Clone")) {
err.error(path, i, "impl ::Copy and ::Clone manually");
}
if line.contains("impl") {
in_impl = true;
}
if in_impl && line.starts_with('}') {
in_impl = false;
}
let orig_line = line;
let line = line.trim_start();
let is_pub = line.starts_with("pub ");
let line = if is_pub { &line[4..] } else { line };
let line_state = if line.starts_with("use ") {
if line.contains("c_void") {
continue;
}
if is_pub {
State::Modules
} else {
State::Imports
}
} else if line.starts_with("const ") {
State::Constants
} else if line.starts_with("type ") && !in_impl {
State::Typedefs
} else if line.starts_with("s! {") {
s_macros += 1;
State::Structs
} else if line.starts_with("s_no_extra_traits! {") {
// multiple macros of this type are allowed
State::Structs
} else if line.starts_with("s_paren! {") {
// multiple macros of this type are allowed
State::Structs
} else if line.starts_with("f! {") {
f_macros += 1;
State::FunctionDefinitions
} else if line.starts_with("extern ") && !orig_line.starts_with(" ") {
State::Functions
} else if line.starts_with("mod ") {
State::Modules
} else {
continue;
};
if state as usize > line_state as usize {
err.error(
path,
i,
&format!(
"{} found after {} when it belongs before",
line_state.desc(),
state.desc()
),
);
}
if f_macros == 2 {
f_macros += 1;
err.error(path, i, "multiple f! macros in one module");
}
if s_macros == 2 {
s_macros += 1;
err.error(path, i, "multiple s! macros in one module");
}
state = line_state;
}
}
impl State {
fn desc(&self) -> &str {
match *self {
State::Start => "start",
State::Imports => "import",
State::Typedefs => "typedef",
State::Structs => "struct",
State::Constants => "constant",
State::FunctionDefinitions => "function definition",
State::Functions => "extern function",
State::Modules => "module",
}
}
}
impl Errors {
fn error(&mut self, path: &Path, line: usize, msg: &str) {
self.errs = true;
println!("{}:{}: {}", path.display(), line + 1, msg);
}
}