Skip to content

Commit b7f55ca

Browse files
committed
Assign group and parse since for Feature
1 parent 8b82f68 commit b7f55ca

File tree

2 files changed

+117
-11
lines changed

2 files changed

+117
-11
lines changed

src/tools/tidy/src/features.rs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ use std::path::Path;
1616

1717
use regex::{Regex, escape};
1818

19+
mod version;
20+
use self::version::Version;
21+
22+
const FEATURE_GROUP_START_PREFIX: &str = "// feature group start:";
23+
const FEATURE_GROUP_END_PREFIX: &str = "// feature group end";
24+
1925
#[derive(Debug, PartialEq, Clone)]
2026
pub enum Status {
2127
Stable,
@@ -37,9 +43,10 @@ impl fmt::Display for Status {
3743
#[derive(Debug, Clone)]
3844
pub struct Feature {
3945
pub level: Status,
40-
pub since: String,
46+
pub since: Option<Version>,
4147
pub has_gate_test: bool,
4248
pub tracking_issue: Option<u32>,
49+
pub group: Option<String>,
4350
}
4451

4552
pub type Features = HashMap<String, Feature>;
@@ -136,14 +143,16 @@ pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
136143
name,
137144
"lang",
138145
feature.level,
139-
feature.since));
146+
feature.since.as_ref().map_or("None".to_owned(),
147+
|since| since.to_string())));
140148
}
141149
for (name, feature) in lib_features {
142150
lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
143151
name,
144152
"lib",
145153
feature.level,
146-
feature.since));
154+
feature.since.as_ref().map_or("None".to_owned(),
155+
|since| since.to_string())));
147156
}
148157

149158
lines.sort();
@@ -188,6 +197,8 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
188197
// without one inside `// no tracking issue START` and `// no tracking issue END`.
189198
let mut next_feature_omits_tracking_issue = false;
190199

200+
let mut next_feature_group = None;
201+
191202
contents.lines().zip(1..)
192203
.filter_map(|(line, line_number)| {
193204
let line = line.trim();
@@ -205,6 +216,15 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
205216
_ => {}
206217
}
207218

219+
if line.starts_with(FEATURE_GROUP_START_PREFIX) {
220+
let group = line.trim_start_matches(FEATURE_GROUP_START_PREFIX).trim();
221+
next_feature_group = Some(group.to_owned());
222+
return None;
223+
} else if line.starts_with(FEATURE_GROUP_END_PREFIX) {
224+
next_feature_group = None;
225+
return None;
226+
}
227+
208228
let mut parts = line.split(',');
209229
let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {
210230
Some("active") => Status::Unstable,
@@ -213,7 +233,20 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
213233
_ => return None,
214234
};
215235
let name = parts.next().unwrap().trim();
216-
let since = parts.next().unwrap().trim().trim_matches('"');
236+
let since_str = parts.next().unwrap().trim().trim_matches('"');
237+
let since = match since_str.parse() {
238+
Ok(since) => Some(since),
239+
Err(err) => {
240+
tidy_error!(
241+
bad,
242+
"libsyntax/feature_gate.rs:{}: failed to parse since: {} ({})",
243+
line_number,
244+
since_str,
245+
err,
246+
);
247+
None
248+
}
249+
};
217250
let issue_str = parts.next().unwrap().trim();
218251
let tracking_issue = if issue_str.starts_with("None") {
219252
if level == Status::Unstable && !next_feature_omits_tracking_issue {
@@ -233,9 +266,10 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
233266
Some((name.to_owned(),
234267
Feature {
235268
level,
236-
since: since.to_owned(),
269+
since,
237270
has_gate_test: false,
238271
tracking_issue,
272+
group: next_feature_group.clone(),
239273
}))
240274
})
241275
.collect()
@@ -250,9 +284,10 @@ pub fn collect_lib_features(base_src_path: &Path) -> Features {
250284
// add it to the set of known library features so we can still generate docs.
251285
lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
252286
level: Status::Unstable,
253-
since: String::new(),
287+
since: None,
254288
has_gate_test: false,
255289
tracking_issue: None,
290+
group: None,
256291
});
257292

258293
map_lib_features(base_src_path,
@@ -351,12 +386,13 @@ fn map_lib_features(base_src_path: &Path,
351386
};
352387
let feature = Feature {
353388
level: Status::Unstable,
354-
since: "None".to_owned(),
389+
since: None,
355390
has_gate_test: false,
356391
// FIXME(#57563): #57563 is now used as a common tracking issue,
357392
// although we would like to have specific tracking issues for each
358393
// `rustc_const_unstable` in the future.
359394
tracking_issue: Some(57563),
395+
group: None,
360396
};
361397
mf(Ok((feature_name, feature)), file, i + 1);
362398
continue;
@@ -372,20 +408,24 @@ fn map_lib_features(base_src_path: &Path,
372408
Some(name) => name,
373409
None => err!("malformed stability attribute"),
374410
};
375-
let since = match find_attr_val(line, "since") {
376-
Some(name) => name,
411+
let since = match find_attr_val(line, "since").map(|x| x.parse()) {
412+
Some(Ok(since)) => Some(since),
413+
Some(Err(_err)) => {
414+
err!("malformed since attribute");
415+
},
377416
None if level == Status::Stable => {
378417
err!("malformed stability attribute");
379418
}
380-
None => "None",
419+
None => None,
381420
};
382421
let tracking_issue = find_attr_val(line, "issue").map(|s| s.parse().unwrap());
383422

384423
let feature = Feature {
385424
level,
386-
since: since.to_owned(),
425+
since,
387426
has_gate_test: false,
388427
tracking_issue,
428+
group: None,
389429
};
390430
if line.contains(']') {
391431
mf(Ok((feature_name, feature)), file, i + 1);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use std::str::FromStr;
2+
use std::num::ParseIntError;
3+
use std::fmt;
4+
5+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
6+
pub struct Version {
7+
parts: Vec<u32>,
8+
}
9+
10+
impl fmt::Display for Version {
11+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12+
let x = self.parts.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(".");
13+
f.pad(&x)
14+
}
15+
}
16+
17+
impl FromStr for Version {
18+
type Err = ParseIntError;
19+
20+
fn from_str(s: &str) -> Result<Self, Self::Err> {
21+
let parts = s.split('.').map(|part| part.parse()).collect::<Result<_, _>>()?;
22+
Ok(Version { parts })
23+
}
24+
}
25+
26+
#[cfg(test)]
27+
mod test {
28+
use super::Version;
29+
30+
#[test]
31+
fn test_try_from_invalid_version() {
32+
assert!("".parse::<Version>().is_err());
33+
assert!("hello".parse::<Version>().is_err());
34+
assert!("1.32.hi".parse::<Version>().is_err());
35+
assert!("1.32..1".parse::<Version>().is_err());
36+
}
37+
38+
#[test]
39+
fn test_try_from_single() {
40+
assert_eq!("1.32.0".parse(), Ok(Version { parts: vec![1, 32, 0] }));
41+
assert_eq!("1.0.0".parse(), Ok(Version { parts: vec![1, 0, 0] }));
42+
}
43+
44+
#[test]
45+
fn test_compare() {
46+
let v_1_0_0 = "1.0.0".parse::<Version>().unwrap();
47+
let v_1_32 = "1.32".parse::<Version>().unwrap();
48+
let v_1_32_1 = "1.32.1".parse::<Version>().unwrap();
49+
assert!(v_1_0_0 < v_1_32_1);
50+
assert!(v_1_0_0 < v_1_32);
51+
assert!(v_1_32 < v_1_32_1);
52+
}
53+
54+
#[test]
55+
fn test_to_string() {
56+
let v_1_0_0 = "1.0.0".parse::<Version>().unwrap();
57+
let v_1_32 = "1.32".parse::<Version>().unwrap();
58+
let v_1_32_1 = "1.32.1".parse::<Version>().unwrap();
59+
60+
assert_eq!(v_1_0_0.to_string(), "1.0.0");
61+
assert_eq!(v_1_32.to_string(), "1.32");
62+
assert_eq!(v_1_32_1.to_string(), "1.32.1");
63+
assert_eq!(format!("{:<8}", v_1_32_1), "1.32.1 ");
64+
assert_eq!(format!("{:>8}", v_1_32_1), " 1.32.1");
65+
}
66+
}

0 commit comments

Comments
 (0)