]> git.proxmox.com Git - rustc.git/blame - src/tools/tidy/src/pal.rs
New upstream version 1.66.0+dfsg1
[rustc.git] / src / tools / tidy / src / pal.rs
CommitLineData
9fa01778 1//! Tidy check to enforce rules about platform-specific code in std.
c30ab7b3
SL
2//!
3//! This is intended to maintain existing standards of code
4//! organization in hopes that the standard library will continue to
5//! be refactored to isolate platform-specific bits, making porting
6//! easier; where "standard library" roughly means "all the
7//! dependencies of the std and test crates".
8//!
9//! This generally means placing restrictions on where `cfg(unix)`,
10//! `cfg(windows)`, `cfg(target_os)` and `cfg(target_env)` may appear,
11//! the basic objective being to isolate platform-specific code to the
12//! platform-specific `std::sys` modules, and to the allocation,
13//! unwinding, and libc crates.
14//!
15//! Following are the basic rules, though there are currently
16//! exceptions:
17//!
9fa01778
XL
18//! - core may not have platform-specific code.
19//! - libpanic_abort may have platform-specific code.
20//! - libpanic_unwind may have platform-specific code.
21//! - libunwind may have platform-specific code.
22//! - other crates in the std facade may not.
23//! - std may have platform-specific code in the following places:
17df50a5 24//! - `sys/`
9fa01778 25//! - `os/`
c30ab7b3
SL
26//!
27//! `std/sys_common` should _not_ contain platform-specific code.
28//! Finally, because std contains tests with platform-specific
29//! `ignore` attributes, once the parser encounters `mod tests`,
30//! platform-specific cfgs are allowed. Not sure yet how to deal with
31//! this in the long term.
32
2b03887a 33use crate::walk::{filter_dirs, walk};
c30ab7b3 34use std::iter::Iterator;
dfeec247 35use std::path::Path;
c30ab7b3 36
9fa01778 37// Paths that may contain platform-specific code.
b7449926 38const EXCEPTION_PATHS: &[&str] = &[
3dfed10e
XL
39 "library/panic_abort",
40 "library/panic_unwind",
41 "library/unwind",
17df50a5
XL
42 "library/rtstartup", // Not sure what to do about this. magic stuff for mingw
43 "library/term", // Not sure how to make this crate portable, but test crate needs it.
44 "library/test", // Probably should defer to unstable `std::sys` APIs.
a1dfa0c6
XL
45 // The `VaList` implementation must have platform specific code.
46 // The Windows implementation of a `va_list` is always a character
47 // pointer regardless of the target architecture. As a result,
48 // we must use `#[cfg(windows)]` to conditionally compile the
49 // correct `VaList` structure for windows.
5e7ed085 50 "library/core/src/ffi/mod.rs",
17df50a5
XL
51 "library/std/src/sys/", // Platform-specific code for std lives here.
52 "library/std/src/os", // Platform-specific public interfaces
53 // Temporary `std` exceptions
54 // FIXME: platform-specific code should be moved to `sys`
55 "library/std/src/io/copy.rs",
56 "library/std/src/io/stdio.rs",
57 "library/std/src/f32.rs",
58 "library/std/src/f64.rs",
59 "library/std/src/path.rs",
17df50a5
XL
60 "library/std/src/sys_common", // Should only contain abstractions over platforms
61 "library/std/src/net/test.rs", // Utility helpers for tests
5099ac24 62 "library/std/src/panic.rs", // fuchsia-specific panic backtrace handling
f2b60f7d
FG
63 "library/std/src/personality.rs",
64 "library/std/src/personality/",
c30ab7b3
SL
65];
66
67pub fn check(path: &Path, bad: &mut bool) {
9fa01778 68 // Sanity check that the complex parsing here works.
b7449926
XL
69 let mut saw_target_arch = false;
70 let mut saw_cfg_bang = false;
2b03887a 71 walk(path, &mut filter_dirs, &mut |entry, contents| {
dc9dc135 72 let file = entry.path();
c30ab7b3 73 let filestr = file.to_string_lossy().replace("\\", "/");
dfeec247
XL
74 if !filestr.ends_with(".rs") {
75 return;
76 }
c30ab7b3
SL
77
78 let is_exception_path = EXCEPTION_PATHS.iter().any(|s| filestr.contains(&**s));
dfeec247
XL
79 if is_exception_path {
80 return;
81 }
c30ab7b3 82
17df50a5
XL
83 // exclude tests and benchmarks as some platforms do not support all tests
84 if filestr.contains("tests") || filestr.contains("benches") {
85 return;
86 }
87
dc9dc135 88 check_cfgs(contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
c30ab7b3
SL
89 });
90
b7449926
XL
91 assert!(saw_target_arch);
92 assert!(saw_cfg_bang);
c30ab7b3
SL
93}
94
dfeec247
XL
95fn check_cfgs(
96 contents: &str,
97 file: &Path,
98 bad: &mut bool,
99 saw_target_arch: &mut bool,
100 saw_cfg_bang: &mut bool,
101) {
9fa01778 102 // Pull out all `cfg(...)` and `cfg!(...)` strings.
c30ab7b3
SL
103 let cfgs = parse_cfgs(contents);
104
105 let mut line_numbers: Option<Vec<usize>> = None;
106 let mut err = |idx: usize, cfg: &str| {
107 if line_numbers.is_none() {
108 line_numbers = Some(contents.match_indices('\n').map(|(i, _)| i).collect());
109 }
110 let line_numbers = line_numbers.as_ref().expect("");
111 let line = match line_numbers.binary_search(&idx) {
112 Ok(_) => unreachable!(),
dfeec247 113 Err(i) => i + 1,
c30ab7b3 114 };
cc61c64b 115 tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg);
c30ab7b3
SL
116 };
117
b7449926 118 for (idx, cfg) in cfgs {
9fa01778 119 // Sanity check that the parsing here works.
dfeec247
XL
120 if !*saw_target_arch && cfg.contains("target_arch") {
121 *saw_target_arch = true
122 }
123 if !*saw_cfg_bang && cfg.contains("cfg!") {
124 *saw_cfg_bang = true
125 }
c30ab7b3 126
dfeec247 127 let contains_platform_specific_cfg = cfg.contains("target_os")
c30ab7b3 128 || cfg.contains("target_env")
136023e0 129 || cfg.contains("target_abi")
c30ab7b3
SL
130 || cfg.contains("target_vendor")
131 || cfg.contains("unix")
132 || cfg.contains("windows");
133
dfeec247
XL
134 if !contains_platform_specific_cfg {
135 continue;
136 }
c30ab7b3 137
5e7ed085 138 let preceded_by_doc_comment = {
c30ab7b3
SL
139 let pre_contents = &contents[..idx];
140 let pre_newline = pre_contents.rfind('\n');
141 let pre_doc_comment = pre_contents.rfind("///");
142 match (pre_newline, pre_doc_comment) {
143 (Some(n), Some(c)) => n < c,
144 (None, Some(_)) => true,
145 (_, None) => false,
146 }
147 };
148
5e7ed085 149 if preceded_by_doc_comment {
dfeec247
XL
150 continue;
151 }
c30ab7b3 152
17df50a5
XL
153 // exclude tests as some platforms do not support all tests
154 if cfg.contains("test") {
155 continue;
c30ab7b3 156 }
17df50a5
XL
157
158 err(idx, cfg);
c30ab7b3
SL
159 }
160}
161
17df50a5 162fn parse_cfgs(contents: &str) -> Vec<(usize, &str)> {
c30ab7b3
SL
163 let candidate_cfgs = contents.match_indices("cfg");
164 let candidate_cfg_idxs = candidate_cfgs.map(|(i, _)| i);
165 // This is puling out the indexes of all "cfg" strings
9fa01778 166 // that appear to be tokens followed by a parenthesis.
c30ab7b3 167 let cfgs = candidate_cfg_idxs.filter(|i| {
17df50a5 168 let pre_idx = i.saturating_sub(1);
dfeec247
XL
169 let succeeds_non_ident = !contents
170 .as_bytes()
171 .get(pre_idx)
c30ab7b3
SL
172 .cloned()
173 .map(char::from)
174 .map(char::is_alphanumeric)
175 .unwrap_or(false);
176 let contents_after = &contents[*i..];
177 let first_paren = contents_after.find('(');
178 let paren_idx = first_paren.map(|ip| i + ip);
dfeec247
XL
179 let preceeds_whitespace_and_paren = paren_idx
180 .map(|ip| {
181 let maybe_space = &contents[*i + "cfg".len()..ip];
182 maybe_space.chars().all(|c| char::is_whitespace(c) || c == '!')
183 })
184 .unwrap_or(false);
c30ab7b3
SL
185
186 succeeds_non_ident && preceeds_whitespace_and_paren
187 });
188
dc9dc135 189 cfgs.flat_map(|i| {
c30ab7b3
SL
190 let mut depth = 0;
191 let contents_from = &contents[i..];
192 for (j, byte) in contents_from.bytes().enumerate() {
193 match byte {
194 b'(' => {
195 depth += 1;
196 }
197 b')' => {
198 depth -= 1;
199 if depth == 0 {
dc9dc135 200 return Some((i, &contents_from[..=j]));
c30ab7b3
SL
201 }
202 }
dfeec247 203 _ => {}
c30ab7b3
SL
204 }
205 }
206
dc9dc135
XL
207 // if the parentheses are unbalanced just ignore this cfg -- it'll be caught when attempting
208 // to run the compiler, and there's no real reason to lint it separately here
209 None
dfeec247
XL
210 })
211 .collect()
c30ab7b3 212}