]> git.proxmox.com Git - rustc.git/blob - src/tools/tidy/src/features.rs
0b989d92b3d1d0df4cdb104efda8186671f7c44a
[rustc.git] / src / tools / tidy / src / features.rs
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();
49 if !filename.ends_with(".rs") || filename == "features.rs" {
50 return
51 }
52
53 contents.truncate(0);
54 t!(t!(File::open(file)).read_to_string(&mut contents));
55
56 for (i, line) in contents.lines().enumerate() {
57 let mut err = |msg: &str| {
58 println!("{}:{}: {}", file.display(), i + 1, msg);
59 *bad = true;
60 };
61 let level = if line.contains("[unstable(") {
62 "unstable"
63 } else if line.contains("[stable(") {
64 "stable"
65 } else {
66 continue
67 };
68 let feature_name = match find_attr_val(line, "feature") {
69 Some(name) => name,
70 None => {
71 err("malformed stability attribute");
72 continue
73 }
74 };
75 let since = match find_attr_val(line, "since") {
76 Some(name) => name,
77 None if level == "stable" => {
78 err("malformed stability attribute");
79 continue
80 }
81 None => "None",
82 };
83
84 if features.iter().any(|f| f.name == feature_name) {
85 err("duplicating a lang feature");
86 }
87 if let Some(ref s) = lib_features.get(feature_name) {
88 if s.level != level {
89 err("different stability level than before");
90 }
91 if s.since != since {
92 err("different `since` than before");
93 }
94 continue
95 }
96 lib_features.insert(feature_name.to_owned(), LibFeature {
97 level: level.to_owned(),
98 since: since.to_owned(),
99 });
100 }
101 });
102
103 if *bad {
104 return
105 }
106
107 let mut lines = Vec::new();
108 for feature in features {
109 lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
110 feature.name, "lang", feature.status, feature.since));
111 }
112 for (name, feature) in lib_features {
113 lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
114 name, "lib", feature.level, feature.since));
115 }
116
117 lines.sort();
118 for line in lines {
119 println!("* {}", line);
120 }
121 }
122
123 fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
124 line.find(attr).and_then(|i| {
125 line[i..].find("\"").map(|j| i + j + 1)
126 }).and_then(|i| {
127 line[i..].find("\"").map(|j| (i, i + j))
128 }).map(|(i, j)| {
129 &line[i..j]
130 })
131 }
132
133 fn collect_lang_features(path: &Path) -> Vec<Feature> {
134 let mut contents = String::new();
135 t!(t!(File::open(path)).read_to_string(&mut contents));
136
137 let mut features = Vec::new();
138 for line in contents.lines().map(|l| l.trim()) {
139 if !STATUSES.iter().any(|s| line.starts_with(&format!("({}", s))) {
140 continue
141 }
142 let mut parts = line.split(",");
143 let status = match &parts.next().unwrap().trim().replace("(", "")[..] {
144 "active" => "unstable",
145 "removed" => "unstable",
146 "accepted" => "stable",
147 s => panic!("unknown status: {}", s),
148 };
149 let name = parts.next().unwrap().trim().to_owned();
150 let since = parts.next().unwrap().trim().replace("\"", "");
151
152 features.push(Feature {
153 name: name,
154 since: since,
155 status: status.to_owned(),
156 });
157 }
158 return features
159 }