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
31 use std::env;
32 use std::fs;
33 use std::io::prelude::*;
34 use std::path::Path;
35
36 macro_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
main()45 fn 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
walk(path: &Path, err: &mut Errors)58 fn 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
82 struct Errors {
83 errs: bool,
84 }
85
86 #[derive(Clone, Copy, PartialEq)]
87 enum State {
88 Start,
89 Imports,
90 Typedefs,
91 Structs,
92 Constants,
93 FunctionDefinitions,
94 Functions,
95 Modules,
96 }
97
check_style(file: &str, path: &Path, err: &mut Errors)98 fn 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
187 impl State {
desc(&self) -> &str188 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
202 impl Errors {
error(&mut self, path: &Path, line: usize, msg: &str)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