xref: /third_party/rust/crates/libc/ci/style.rs (revision 2add0d91)
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