1//! Simple script to verify the coding style of this library 2//! 3//! ## How to run 4//! 5//! The first argument to this script is the directory to run on, so running 6//! this script should be as simple as: 7//! 8//! ```notrust 9//! rustc ci/style.rs 10//! ./style src 11//! ``` 12//! 13//! ## Guidelines 14//! 15//! The current style is: 16//! 17//! * Specific module layout: 18//! 1. use directives 19//! 2. typedefs 20//! 3. structs 21//! 4. constants 22//! 5. f! { ... } functions 23//! 6. extern functions 24//! 7. modules + pub use 25//! 26//! Things not verified: 27//! 28//! * alignment 29//! * leading colons on paths 30 31use std::env; 32use std::fs; 33use std::io::prelude::*; 34use std::path::Path; 35 36macro_rules! t { 37 ($e:expr) => { 38 match $e { 39 Ok(e) => e, 40 Err(e) => panic!("{} failed with {}", stringify!($e), e), 41 } 42 }; 43} 44 45fn main() { 46 let arg = env::args().skip(1).next().unwrap_or(".".to_string()); 47 48 let mut errors = Errors { errs: false }; 49 walk(Path::new(&arg), &mut errors); 50 51 if errors.errs { 52 panic!("found some lint errors"); 53 } else { 54 println!("good style!"); 55 } 56} 57 58fn walk(path: &Path, err: &mut Errors) { 59 for entry in t!(path.read_dir()).map(|e| t!(e)) { 60 let path = entry.path(); 61 if t!(entry.file_type()).is_dir() { 62 walk(&path, err); 63 continue; 64 } 65 66 let name = entry.file_name().into_string().unwrap(); 67 match &name[..] { 68 n if !n.ends_with(".rs") => continue, 69 70 "lib.rs" | "macros.rs" => continue, 71 72 _ => {} 73 } 74 75 let mut contents = String::new(); 76 t!(t!(fs::File::open(&path)).read_to_string(&mut contents)); 77 78 check_style(&contents, &path, err); 79 } 80} 81 82struct Errors { 83 errs: bool, 84} 85 86#[derive(Clone, Copy, PartialEq)] 87enum State { 88 Start, 89 Imports, 90 Typedefs, 91 Structs, 92 Constants, 93 FunctionDefinitions, 94 Functions, 95 Modules, 96} 97 98fn check_style(file: &str, path: &Path, err: &mut Errors) { 99 let mut state = State::Start; 100 let mut s_macros = 0; 101 let mut f_macros = 0; 102 let mut in_impl = false; 103 104 for (i, line) in file.lines().enumerate() { 105 if line.contains("#[cfg(") 106 && line.contains(']') 107 && !line.contains(" if ") 108 && !(line.contains("target_endian") || line.contains("target_arch")) 109 { 110 if state != State::Structs { 111 err.error(path, i, "use cfg_if! and submodules instead of #[cfg]"); 112 } 113 } 114 if line.contains("#[derive(") && (line.contains("Copy") || line.contains("Clone")) { 115 err.error(path, i, "impl ::Copy and ::Clone manually"); 116 } 117 if line.contains("impl") { 118 in_impl = true; 119 } 120 if in_impl && line.starts_with('}') { 121 in_impl = false; 122 } 123 124 let orig_line = line; 125 let line = line.trim_start(); 126 let is_pub = line.starts_with("pub "); 127 let line = if is_pub { &line[4..] } else { line }; 128 129 let line_state = if line.starts_with("use ") { 130 if line.contains("c_void") { 131 continue; 132 } 133 if is_pub { 134 State::Modules 135 } else { 136 State::Imports 137 } 138 } else if line.starts_with("const ") { 139 State::Constants 140 } else if line.starts_with("type ") && !in_impl { 141 State::Typedefs 142 } else if line.starts_with("s! {") { 143 s_macros += 1; 144 State::Structs 145 } else if line.starts_with("s_no_extra_traits! {") { 146 // multiple macros of this type are allowed 147 State::Structs 148 } else if line.starts_with("s_paren! {") { 149 // multiple macros of this type are allowed 150 State::Structs 151 } else if line.starts_with("f! {") { 152 f_macros += 1; 153 State::FunctionDefinitions 154 } else if line.starts_with("extern ") && !orig_line.starts_with(" ") { 155 State::Functions 156 } else if line.starts_with("mod ") { 157 State::Modules 158 } else { 159 continue; 160 }; 161 162 if state as usize > line_state as usize { 163 err.error( 164 path, 165 i, 166 &format!( 167 "{} found after {} when it belongs before", 168 line_state.desc(), 169 state.desc() 170 ), 171 ); 172 } 173 174 if f_macros == 2 { 175 f_macros += 1; 176 err.error(path, i, "multiple f! macros in one module"); 177 } 178 if s_macros == 2 { 179 s_macros += 1; 180 err.error(path, i, "multiple s! macros in one module"); 181 } 182 183 state = line_state; 184 } 185} 186 187impl State { 188 fn desc(&self) -> &str { 189 match *self { 190 State::Start => "start", 191 State::Imports => "import", 192 State::Typedefs => "typedef", 193 State::Structs => "struct", 194 State::Constants => "constant", 195 State::FunctionDefinitions => "function definition", 196 State::Functions => "extern function", 197 State::Modules => "module", 198 } 199 } 200} 201 202impl Errors { 203 fn error(&mut self, path: &Path, line: usize, msg: &str) { 204 self.errs = true; 205 println!("{}:{}: {}", path.display(), line + 1, msg); 206 } 207} 208