• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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