]> git.proxmox.com Git - rustc.git/blame - src/tools/tidy/src/features.rs
New upstream version 1.14.0+dfsg1
[rustc.git] / src / tools / tidy / src / features.rs
CommitLineData
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
20use std::collections::HashMap;
c30ab7b3 21use std::fmt;
a7813a04
XL
22use std::fs::File;
23use std::io::prelude::*;
24use std::path::Path;
25
c30ab7b3
SL
26#[derive(PartialEq)]
27enum Status {
28 Stable,
29 Unstable,
30}
31
32impl fmt::Display for Status {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 let as_str = match *self {
35 Status::Stable => "stable",
36 Status::Unstable => "unstable",
37 };
38 fmt::Display::fmt(as_str, f)
39 }
40}
41
a7813a04
XL
42
43struct Feature {
44 name: String,
c30ab7b3 45 level: Status,
a7813a04 46 since: String,
a7813a04
XL
47}
48
49struct LibFeature {
c30ab7b3 50 level: Status,
a7813a04
XL
51 since: String,
52}
53
54pub fn check(path: &Path, bad: &mut bool) {
55 let features = collect_lang_features(&path.join("libsyntax/feature_gate.rs"));
c30ab7b3 56 assert!(!features.is_empty());
a7813a04
XL
57 let mut lib_features = HashMap::<String, LibFeature>::new();
58
59 let mut contents = String::new();
60 super::walk(path,
61 &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
62 &mut |file| {
63 let filename = file.file_name().unwrap().to_string_lossy();
5bcae85e
SL
64 if !filename.ends_with(".rs") || filename == "features.rs" ||
65 filename == "diagnostic_list.rs" {
c30ab7b3 66 return;
a7813a04
XL
67 }
68
69 contents.truncate(0);
70 t!(t!(File::open(file)).read_to_string(&mut contents));
71
72 for (i, line) in contents.lines().enumerate() {
73 let mut err = |msg: &str| {
74 println!("{}:{}: {}", file.display(), i + 1, msg);
75 *bad = true;
76 };
77 let level = if line.contains("[unstable(") {
c30ab7b3 78 Status::Unstable
a7813a04 79 } else if line.contains("[stable(") {
c30ab7b3 80 Status::Stable
a7813a04 81 } else {
c30ab7b3 82 continue;
a7813a04
XL
83 };
84 let feature_name = match find_attr_val(line, "feature") {
85 Some(name) => name,
86 None => {
87 err("malformed stability attribute");
c30ab7b3 88 continue;
a7813a04
XL
89 }
90 };
91 let since = match find_attr_val(line, "since") {
92 Some(name) => name,
c30ab7b3 93 None if level == Status::Stable => {
a7813a04 94 err("malformed stability attribute");
c30ab7b3 95 continue;
a7813a04
XL
96 }
97 None => "None",
98 };
99
100 if features.iter().any(|f| f.name == feature_name) {
101 err("duplicating a lang feature");
102 }
103 if let Some(ref s) = lib_features.get(feature_name) {
104 if s.level != level {
105 err("different stability level than before");
106 }
107 if s.since != since {
108 err("different `since` than before");
109 }
c30ab7b3 110 continue;
a7813a04 111 }
c30ab7b3
SL
112 lib_features.insert(feature_name.to_owned(),
113 LibFeature {
114 level: level,
115 since: since.to_owned(),
116 });
a7813a04
XL
117 }
118 });
119
120 if *bad {
c30ab7b3 121 return;
a7813a04
XL
122 }
123
124 let mut lines = Vec::new();
125 for feature in features {
126 lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
c30ab7b3
SL
127 feature.name,
128 "lang",
129 feature.level,
130 feature.since));
a7813a04
XL
131 }
132 for (name, feature) in lib_features {
133 lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
c30ab7b3
SL
134 name,
135 "lib",
136 feature.level,
137 feature.since));
a7813a04
XL
138 }
139
140 lines.sort();
141 for line in lines {
142 println!("* {}", line);
143 }
144}
145
146fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
c30ab7b3
SL
147 line.find(attr)
148 .and_then(|i| line[i..].find('"').map(|j| i + j + 1))
149 .and_then(|i| line[i..].find('"').map(|j| (i, i + j)))
150 .map(|(i, j)| &line[i..j])
a7813a04
XL
151}
152
153fn collect_lang_features(path: &Path) -> Vec<Feature> {
154 let mut contents = String::new();
155 t!(t!(File::open(path)).read_to_string(&mut contents));
156
c30ab7b3
SL
157 contents.lines()
158 .filter_map(|line| {
159 let mut parts = line.trim().split(",");
160 let level = match parts.next().map(|l| l.trim().trim_left_matches('(')) {
161 Some("active") => Status::Unstable,
162 Some("removed") => Status::Unstable,
163 Some("accepted") => Status::Stable,
164 _ => return None,
165 };
166 let name = parts.next().unwrap().trim();
167 let since = parts.next().unwrap().trim().trim_matches('"');
168 Some(Feature {
169 name: name.to_owned(),
170 level: level,
171 since: since.to_owned(),
172 })
173 })
174 .collect()
a7813a04 175}