]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Tidy check to ensure that unstable features are all in order | |
12 | //! | |
13 | //! This check will ensure properties like: | |
14 | //! | |
15 | //! * All stability attributes look reasonably well formed | |
16 | //! * The set of library features is disjoint from the set of language features | |
17 | //! * Library features have at most one stability level | |
18 | //! * Library features have at most one `since` value | |
19 | ||
20 | use std::collections::HashMap; | |
21 | use std::fs::File; | |
22 | use std::io::prelude::*; | |
23 | use std::path::Path; | |
24 | ||
25 | const STATUSES: &'static [&'static str] = &[ | |
26 | "Active", "Deprecated", "Removed", "Accepted", | |
27 | ]; | |
28 | ||
29 | struct Feature { | |
30 | name: String, | |
31 | since: String, | |
32 | status: String, | |
33 | } | |
34 | ||
35 | struct LibFeature { | |
36 | level: String, | |
37 | since: String, | |
38 | } | |
39 | ||
40 | pub fn check(path: &Path, bad: &mut bool) { | |
41 | let features = collect_lang_features(&path.join("libsyntax/feature_gate.rs")); | |
42 | let mut lib_features = HashMap::<String, LibFeature>::new(); | |
43 | ||
44 | let mut contents = String::new(); | |
45 | super::walk(path, | |
46 | &mut |path| super::filter_dirs(path) || path.ends_with("src/test"), | |
47 | &mut |file| { | |
48 | let filename = file.file_name().unwrap().to_string_lossy(); | |
5bcae85e SL |
49 | if !filename.ends_with(".rs") || filename == "features.rs" || |
50 | filename == "diagnostic_list.rs" { | |
a7813a04 XL |
51 | return |
52 | } | |
53 | ||
54 | contents.truncate(0); | |
55 | t!(t!(File::open(file)).read_to_string(&mut contents)); | |
56 | ||
57 | for (i, line) in contents.lines().enumerate() { | |
58 | let mut err = |msg: &str| { | |
59 | println!("{}:{}: {}", file.display(), i + 1, msg); | |
60 | *bad = true; | |
61 | }; | |
62 | let level = if line.contains("[unstable(") { | |
63 | "unstable" | |
64 | } else if line.contains("[stable(") { | |
65 | "stable" | |
66 | } else { | |
67 | continue | |
68 | }; | |
69 | let feature_name = match find_attr_val(line, "feature") { | |
70 | Some(name) => name, | |
71 | None => { | |
72 | err("malformed stability attribute"); | |
73 | continue | |
74 | } | |
75 | }; | |
76 | let since = match find_attr_val(line, "since") { | |
77 | Some(name) => name, | |
78 | None if level == "stable" => { | |
79 | err("malformed stability attribute"); | |
80 | continue | |
81 | } | |
82 | None => "None", | |
83 | }; | |
84 | ||
85 | if features.iter().any(|f| f.name == feature_name) { | |
86 | err("duplicating a lang feature"); | |
87 | } | |
88 | if let Some(ref s) = lib_features.get(feature_name) { | |
89 | if s.level != level { | |
90 | err("different stability level than before"); | |
91 | } | |
92 | if s.since != since { | |
93 | err("different `since` than before"); | |
94 | } | |
95 | continue | |
96 | } | |
97 | lib_features.insert(feature_name.to_owned(), LibFeature { | |
98 | level: level.to_owned(), | |
99 | since: since.to_owned(), | |
100 | }); | |
101 | } | |
102 | }); | |
103 | ||
104 | if *bad { | |
105 | return | |
106 | } | |
107 | ||
108 | let mut lines = Vec::new(); | |
109 | for feature in features { | |
110 | lines.push(format!("{:<32} {:<8} {:<12} {:<8}", | |
111 | feature.name, "lang", feature.status, feature.since)); | |
112 | } | |
113 | for (name, feature) in lib_features { | |
114 | lines.push(format!("{:<32} {:<8} {:<12} {:<8}", | |
115 | name, "lib", feature.level, feature.since)); | |
116 | } | |
117 | ||
118 | lines.sort(); | |
119 | for line in lines { | |
120 | println!("* {}", line); | |
121 | } | |
122 | } | |
123 | ||
124 | fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> { | |
125 | line.find(attr).and_then(|i| { | |
126 | line[i..].find("\"").map(|j| i + j + 1) | |
127 | }).and_then(|i| { | |
128 | line[i..].find("\"").map(|j| (i, i + j)) | |
129 | }).map(|(i, j)| { | |
130 | &line[i..j] | |
131 | }) | |
132 | } | |
133 | ||
134 | fn collect_lang_features(path: &Path) -> Vec<Feature> { | |
135 | let mut contents = String::new(); | |
136 | t!(t!(File::open(path)).read_to_string(&mut contents)); | |
137 | ||
138 | let mut features = Vec::new(); | |
139 | for line in contents.lines().map(|l| l.trim()) { | |
140 | if !STATUSES.iter().any(|s| line.starts_with(&format!("({}", s))) { | |
141 | continue | |
142 | } | |
143 | let mut parts = line.split(","); | |
144 | let status = match &parts.next().unwrap().trim().replace("(", "")[..] { | |
145 | "active" => "unstable", | |
146 | "removed" => "unstable", | |
147 | "accepted" => "stable", | |
148 | s => panic!("unknown status: {}", s), | |
149 | }; | |
150 | let name = parts.next().unwrap().trim().to_owned(); | |
151 | let since = parts.next().unwrap().trim().replace("\"", ""); | |
152 | ||
153 | features.push(Feature { | |
154 | name: name, | |
155 | since: since, | |
156 | status: status.to_owned(), | |
157 | }); | |
158 | } | |
159 | return features | |
160 | } |