]> git.proxmox.com Git - rustc.git/blame - src/etc/featureck.py
Imported Upstream version 1.2.0+dfsg1
[rustc.git] / src / etc / featureck.py
CommitLineData
85aaf69f
SL
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# This script does a tree-wide sanity checks against stability
12# attributes, currently:
13# * For all feature_name/level pairs the 'since' field is the same
14# * That no features are both stable and unstable.
15# * That lib features don't have the same name as lang features
16# unless they are on the 'joint_features' whitelist
17# * That features that exist in both lang and lib and are stable
18# since the same version
19# * Prints information about features
20
62682a34
SL
21import sys
22import os
23import re
24import codecs
85aaf69f
SL
25
26if len(sys.argv) < 2:
62682a34 27 print("usage: featureck.py <src-dir>")
85aaf69f
SL
28 sys.exit(1)
29
30src_dir = sys.argv[1]
31
32# Features that are allowed to exist in both the language and the library
33joint_features = [ ]
34
35# Grab the list of language features from the compiler
36language_gate_statuses = [ "Active", "Deprecated", "Removed", "Accepted" ]
37feature_gate_source = os.path.join(src_dir, "libsyntax", "feature_gate.rs")
38language_features = []
39language_feature_names = []
40with open(feature_gate_source, 'r') as f:
41 for line in f:
42 original_line = line
43 line = line.strip()
44 is_feature_line = False
45 for status in language_gate_statuses:
46 if status in line and line.startswith("("):
47 is_feature_line = True
48
49 if is_feature_line:
50 line = line.replace("(", "").replace("),", "").replace(")", "")
51 parts = line.split(",")
52 if len(parts) != 3:
62682a34 53 print("error: unexpected number of components in line: " + original_line)
85aaf69f
SL
54 sys.exit(1)
55 feature_name = parts[0].strip().replace('"', "")
56 since = parts[1].strip().replace('"', "")
57 status = parts[2].strip()
58 assert len(feature_name) > 0
59 assert len(since) > 0
60 assert len(status) > 0
61
62 language_feature_names += [feature_name]
63 language_features += [(feature_name, since, status)]
64
65assert len(language_features) > 0
66
67errors = False
68
69lib_features = { }
70lib_features_and_level = { }
71for (dirpath, dirnames, filenames) in os.walk(src_dir):
72 # Don't look for feature names in tests
73 if "src/test" in dirpath:
74 continue
75
76 # Takes a long time to traverse LLVM
77 if "src/llvm" in dirpath:
78 continue
79
80 for filename in filenames:
81 if not filename.endswith(".rs"):
82 continue
83
84 path = os.path.join(dirpath, filename)
62682a34 85 with codecs.open(filename=path, mode='r', encoding="utf-8") as f:
85aaf69f
SL
86 line_num = 0
87 for line in f:
88 line_num += 1
89 level = None
90 if "[unstable(" in line:
91 level = "unstable"
92 elif "[stable(" in line:
93 level = "stable"
94 else:
95 continue
96
97 # This is a stability attribute. For the purposes of this
98 # script we expect both the 'feature' and 'since' attributes on
99 # the same line, e.g.
100 # `#[unstable(feature = "foo", since = "1.0.0")]`
101
102 p = re.compile('(unstable|stable).*feature *= *"(\w*)"')
103 m = p.search(line)
104 if not m is None:
105 feature_name = m.group(2)
106 since = None
107 if re.compile("\[ *stable").search(line) is not None:
108 pp = re.compile('since *= *"([\w\.]*)"')
109 mm = pp.search(line)
110 if not mm is None:
111 since = mm.group(1)
112 else:
62682a34
SL
113 print("error: misformed stability attribute")
114 print("line %d of %:" % (line_num, path))
115 print(line)
85aaf69f
SL
116 errors = True
117
118 lib_features[feature_name] = feature_name
119 if lib_features_and_level.get((feature_name, level)) is None:
120 # Add it to the observed features
121 lib_features_and_level[(feature_name, level)] = \
122 (since, path, line_num, line)
123 else:
124 # Verify that for this combination of feature_name and level the 'since'
125 # attribute matches.
126 (expected_since, source_path, source_line_num, source_line) = \
127 lib_features_and_level.get((feature_name, level))
128 if since != expected_since:
62682a34
SL
129 print("error: mismatch in %s feature '%s'" % (level, feature_name))
130 print("line %d of %s:" % (source_line_num, source_path))
131 print(source_line)
132 print("line %d of %s:" % (line_num, path))
133 print(line)
85aaf69f
SL
134 errors = True
135
136 # Verify that this lib feature doesn't duplicate a lang feature
137 if feature_name in language_feature_names:
62682a34
SL
138 print("error: lib feature '%s' duplicates a lang feature" % (feature_name))
139 print("line %d of %s:" % (line_num, path))
140 print(line)
85aaf69f
SL
141 errors = True
142
143 else:
62682a34
SL
144 print("error: misformed stability attribute")
145 print("line %d of %s:" % (line_num, path))
146 print(line)
85aaf69f
SL
147 errors = True
148
149# Merge data about both lists
150# name, lang, lib, status, stable since
151
152language_feature_stats = {}
153
154for f in language_features:
155 name = f[0]
156 lang = True
157 lib = False
158 status = "unstable"
159 stable_since = None
160
161 if f[2] == "Accepted":
162 status = "stable"
163 if status == "stable":
164 stable_since = f[1]
165
166 language_feature_stats[name] = (name, lang, lib, status, stable_since)
167
168lib_feature_stats = {}
169
170for f in lib_features:
171 name = f
172 lang = False
173 lib = True
174 status = "unstable"
175 stable_since = None
176
177 is_stable = lib_features_and_level.get((name, "stable")) is not None
178 is_unstable = lib_features_and_level.get((name, "unstable")) is not None
179
180 if is_stable and is_unstable:
62682a34 181 print("error: feature '%s' is both stable and unstable" % (name))
85aaf69f
SL
182 errors = True
183
184 if is_stable:
185 status = "stable"
186 stable_since = lib_features_and_level[(name, "stable")][0]
187 elif is_unstable:
188 status = "unstable"
189
190 lib_feature_stats[name] = (name, lang, lib, status, stable_since)
191
192# Check for overlap in two sets
193merged_stats = { }
194
195for name in lib_feature_stats:
196 if language_feature_stats.get(name) is not None:
197 if not name in joint_features:
62682a34 198 print("error: feature '%s' is both a lang and lib feature but not whitelisted" % (name))
85aaf69f
SL
199 errors = True
200 lang_status = language_feature_stats[name][3]
201 lib_status = lib_feature_stats[name][3]
202 lang_stable_since = language_feature_stats[name][4]
203 lib_stable_since = lib_feature_stats[name][4]
204
205 if lang_status != lib_status and lib_status != "deprecated":
62682a34
SL
206 print("error: feature '%s' has lang status %s " +
207 "but lib status %s" % (name, lang_status, lib_status))
85aaf69f
SL
208 errors = True
209
210 if lang_stable_since != lib_stable_since:
62682a34
SL
211 print("error: feature '%s' has lang stable since %s " +
212 "but lib stable since %s" % (name, lang_stable_since, lib_stable_since))
85aaf69f
SL
213 errors = True
214
215 merged_stats[name] = (name, True, True, lang_status, lang_stable_since)
216
217 del language_feature_stats[name]
218 del lib_feature_stats[name]
219
220if errors:
221 sys.exit(1)
222
223# Finally, display the stats
224stats = {}
225stats.update(language_feature_stats)
226stats.update(lib_feature_stats)
227stats.update(merged_stats)
228lines = []
229for s in stats:
230 s = stats[s]
231 type_ = "lang"
232 if s[1] and s[2]:
233 type_ = "lang/lib"
234 elif s[2]:
235 type_ = "lib"
236 line = "{: <32}".format(s[0]) + \
237 "{: <8}".format(type_) + \
238 "{: <12}".format(s[3]) + \
239 "{: <8}".format(str(s[4]))
240 lines += [line]
241
242lines.sort()
243
244print
245for line in lines:
62682a34 246 print("* " + line)
85aaf69f 247print