Skip to content

Commit ed2aad8

Browse files
committed
Add lint groups; define built-in lint groups bad_style and unused
This adds support for lint groups to the compiler. Lint groups are a way of grouping a number of lints together under one name. For example, this also defines a default lint for naming conventions, named `bad_style`. Writing `#[allow(bad_style)]` is equivalent to writing `#[allow(non_camel_case_types, non_snake_case, non_uppercase_statics)]`. These lint groups can also be defined as a compiler plugin using the new `Registry::register_lint_group` method. This also adds two built-in lint groups, `bad_style` and `unused`. The contents of these groups can be seen by running `rustc -W help`.
1 parent de7abd8 commit ed2aad8

File tree

16 files changed

+318
-50
lines changed

16 files changed

+318
-50
lines changed

src/libcollections/hash/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ macro_rules! impl_hash_tuple(
157157

158158
( $($name:ident)+) => (
159159
impl<S: Writer, $($name: Hash<S>),*> Hash<S> for ($($name,)*) {
160-
#[allow(uppercase_variables)]
161160
#[inline]
162161
#[allow(non_snake_case)]
163162
fn hash(&self, state: &mut S) {

src/libcore/ops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ macro_rules! def_fn_mut(
775775
FnMut<($($args,)*),Result>
776776
for extern "Rust" fn($($args: $args,)*) -> Result {
777777
#[rust_call_abi_hack]
778-
#[allow(uppercase_variables)]
778+
#[allow(non_snake_case)]
779779
fn call_mut(&mut self, args: ($($args,)*)) -> Result {
780780
let ($($args,)*) = args;
781781
(*self)($($args,)*)

src/librustc/driver/driver.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,17 @@ pub fn phase_2_configure_and_expand(sess: &Session,
242242
}
243243
});
244244

245-
let Registry { syntax_exts, lint_passes, .. } = registry;
245+
let Registry { syntax_exts, lint_passes, lint_groups, .. } = registry;
246246

247247
{
248248
let mut ls = sess.lint_store.borrow_mut();
249249
for pass in lint_passes.move_iter() {
250250
ls.register_pass(Some(sess), true, pass);
251251
}
252+
253+
for (name, to) in lint_groups.move_iter() {
254+
ls.register_group(Some(sess), true, name, to);
255+
}
252256
}
253257

254258
// Lint plugins are registered; now we can process command line flags.

src/librustc/driver/mod.rs

+56-12
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,26 @@ Available lint options:
180180
lints
181181
}
182182

183+
fn sort_lint_groups(lints: Vec<(&'static str, Vec<lint::LintId>, bool)>)
184+
-> Vec<(&'static str, Vec<lint::LintId>)> {
185+
let mut lints: Vec<_> = lints.move_iter().map(|(x, y, _)| (x, y)).collect();
186+
lints.sort_by(|&(x, _): &(&'static str, Vec<lint::LintId>),
187+
&(y, _): &(&'static str, Vec<lint::LintId>)| {
188+
x.cmp(&y)
189+
});
190+
lints
191+
}
192+
183193
let (plugin, builtin) = lint_store.get_lints().partitioned(|&(_, p)| p);
184194
let plugin = sort_lints(plugin);
185195
let builtin = sort_lints(builtin);
186196

187-
// FIXME (#7043): We should use the width in character cells rather than
188-
// the number of codepoints.
197+
let (plugin_groups, builtin_groups) = lint_store.get_lint_groups().partitioned(|&(_, _, p)| p);
198+
let plugin_groups = sort_lint_groups(plugin_groups);
199+
let builtin_groups = sort_lint_groups(builtin_groups);
200+
189201
let max_name_len = plugin.iter().chain(builtin.iter())
190-
.map(|&s| s.name.char_len())
202+
.map(|&s| s.name.width(true))
191203
.max().unwrap_or(0);
192204
let padded = |x: &str| {
193205
" ".repeat(max_name_len - x.char_len()).append(x)
@@ -208,16 +220,48 @@ Available lint options:
208220

209221
print_lints(builtin);
210222

211-
match (loaded_plugins, plugin.len()) {
212-
(false, 0) => {
213-
println!("Compiler plugins can provide additional lints. To see a listing of these, \
214-
re-run `rustc -W help` with a crate filename.");
223+
224+
225+
let max_name_len = plugin_groups.iter().chain(builtin_groups.iter())
226+
.map(|&(s, _)| s.width(true))
227+
.max().unwrap_or(0);
228+
let padded = |x: &str| {
229+
" ".repeat(max_name_len - x.char_len()).append(x)
230+
};
231+
232+
println!("Lint groups provided by rustc:\n");
233+
println!(" {} {}", padded("name"), "sub-lints");
234+
println!(" {} {}", padded("----"), "---------");
235+
236+
let print_lint_groups = |lints: Vec<(&'static str, Vec<lint::LintId>)>| {
237+
for (name, to) in lints.move_iter() {
238+
let name = name.chars().map(|x| x.to_lowercase())
239+
.collect::<String>().replace("_", "-");
240+
let desc = to.move_iter().map(|x| x.as_str()).collect::<Vec<String>>().connect(", ");
241+
println!(" {} {}",
242+
padded(name.as_slice()), desc);
215243
}
216-
(false, _) => fail!("didn't load lint plugins but got them anyway!"),
217-
(true, 0) => println!("This crate does not load any lint plugins."),
218-
(true, _) => {
219-
println!("Lint checks provided by plugins loaded by this crate:\n");
220-
print_lints(plugin);
244+
println!("\n");
245+
};
246+
247+
print_lint_groups(builtin_groups);
248+
249+
match (loaded_plugins, plugin.len(), plugin_groups.len()) {
250+
(false, 0, _) | (false, _, 0) => {
251+
println!("Compiler plugins can provide additional lints and lint groups. To see a \
252+
listing of these, re-run `rustc -W help` with a crate filename.");
253+
}
254+
(false, _, _) => fail!("didn't load lint plugins but got them anyway!"),
255+
(true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
256+
(true, l, g) => {
257+
if l > 0 {
258+
println!("Lint checks provided by plugins loaded by this crate:\n");
259+
print_lints(plugin);
260+
}
261+
if g > 0 {
262+
println!("Lint groups provided by plugins loaded by this crate:\n");
263+
print_lint_groups(plugin_groups);
264+
}
221265
}
222266
}
223267
}

src/librustc/lint/builtin.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ impl LintPass for UnusedResult {
736736
}
737737
}
738738

739-
declare_lint!(NON_CAMEL_CASE_TYPES, Warn,
739+
declare_lint!(pub NON_CAMEL_CASE_TYPES, Warn,
740740
"types, variants, traits and type parameters should have camel case names")
741741

742742
pub struct NonCamelCaseTypes;
@@ -844,7 +844,7 @@ fn method_context(cx: &Context, m: &ast::Method) -> MethodContext {
844844
}
845845
}
846846

847-
declare_lint!(NON_SNAKE_CASE, Warn,
847+
declare_lint!(pub NON_SNAKE_CASE, Warn,
848848
"methods, functions, lifetime parameters and modules should have snake case names")
849849

850850
pub struct NonSnakeCase;
@@ -930,8 +930,8 @@ impl LintPass for NonSnakeCase {
930930
self.check_snake_case(cx, "trait method", t.ident, t.span);
931931
}
932932

933-
fn check_lifetime_decl(&mut self, cx: &Context, t: &ast::Lifetime) {
934-
self.check_snake_case(cx, "lifetime", t.name.ident(), t.span);
933+
fn check_lifetime_decl(&mut self, cx: &Context, t: &ast::LifetimeDef) {
934+
self.check_snake_case(cx, "lifetime", t.lifetime.name.ident(), t.lifetime.span);
935935
}
936936

937937
fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
@@ -962,7 +962,7 @@ impl LintPass for NonSnakeCase {
962962
}
963963
}
964964

965-
declare_lint!(NON_UPPERCASE_STATICS, Allow,
965+
declare_lint!(pub NON_UPPERCASE_STATICS, Allow,
966966
"static constants should have uppercase identifiers")
967967

968968
pub struct NonUppercaseStatics;
@@ -1143,7 +1143,7 @@ impl LintPass for UnsafeBlock {
11431143
}
11441144
}
11451145

1146-
declare_lint!(UNUSED_MUT, Warn,
1146+
declare_lint!(pub UNUSED_MUT, Warn,
11471147
"detect mut variables which don't need to be mutable")
11481148

11491149
pub struct UnusedMut;

src/librustc/lint/context.rs

+82-20
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ pub struct LintStore {
6666

6767
/// Current levels of each lint, and where they were set.
6868
levels: HashMap<LintId, LevelSource>,
69+
70+
/// Map of registered lint groups to what lints they expand to. The bool
71+
/// is true if the lint group was added by a plugin.
72+
lint_groups: HashMap<&'static str, (Vec<LintId>, bool)>,
6973
}
7074

7175
impl LintStore {
@@ -90,13 +94,18 @@ impl LintStore {
9094
passes: Some(vec!()),
9195
by_name: HashMap::new(),
9296
levels: HashMap::new(),
97+
lint_groups: HashMap::new(),
9398
}
9499
}
95100

96101
pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] {
97102
self.lints.as_slice()
98103
}
99104

105+
pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
106+
self.lint_groups.iter().map(|(k, &(ref v, b))| (*k, v.clone(), b)).collect()
107+
}
108+
100109
pub fn register_pass(&mut self, sess: Option<&Session>,
101110
from_plugin: bool, pass: LintPassObject) {
102111
for &lint in pass.get_lints().iter() {
@@ -123,6 +132,25 @@ impl LintStore {
123132
self.passes.get_mut_ref().push(pass);
124133
}
125134

135+
pub fn register_group(&mut self, sess: Option<&Session>,
136+
from_plugin: bool, name: &'static str,
137+
to: Vec<LintId>) {
138+
let new = self.lint_groups.insert(name, (to, from_plugin));
139+
140+
if !new {
141+
let msg = format!("duplicate specification of lint group {}", name);
142+
match (sess, from_plugin) {
143+
// We load builtin lints first, so a duplicate is a compiler bug.
144+
// Use early_error when handling -W help with no crate.
145+
(None, _) => early_error(msg.as_slice()),
146+
(Some(sess), false) => sess.bug(msg.as_slice()),
147+
148+
// A duplicate name from a plugin is a user error.
149+
(Some(sess), true) => sess.err(msg.as_slice()),
150+
}
151+
}
152+
}
153+
126154
pub fn register_builtin(&mut self, sess: Option<&Session>) {
127155
macro_rules! add_builtin ( ( $sess:ident, $($name:ident),*, ) => (
128156
{$(
@@ -136,6 +164,10 @@ impl LintStore {
136164
)*}
137165
))
138166

167+
macro_rules! add_lint_group ( ( $sess:ident, $name:expr, $($lint:ident),* ) => (
168+
self.register_group($sess, false, $name, vec![$(LintId::of(builtin::$lint)),*]);
169+
))
170+
139171
add_builtin!(sess,
140172
HardwiredLints,
141173
WhileTrue,
@@ -162,6 +194,13 @@ impl LintStore {
162194
MissingDoc,
163195
)
164196

197+
add_lint_group!(sess, "bad_style",
198+
NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPERCASE_STATICS)
199+
200+
add_lint_group!(sess, "unused",
201+
UNUSED_IMPORTS, UNUSED_VARIABLE, DEAD_ASSIGNMENT, DEAD_CODE,
202+
UNUSED_MUT, UNREACHABLE_CODE)
203+
165204
// We have one lint pass defined in this module.
166205
self.register_pass(sess, false, box GatherNodeLevels as LintPassObject);
167206
}
@@ -170,8 +209,20 @@ impl LintStore {
170209
for &(ref lint_name, level) in sess.opts.lint_opts.iter() {
171210
match self.by_name.find_equiv(&lint_name.as_slice()) {
172211
Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)),
173-
None => sess.err(format!("unknown {} flag: {}",
174-
level.as_str(), lint_name).as_slice()),
212+
None => {
213+
match self.lint_groups.iter().map(|(&x, &(ref y, _))| (x, y.clone()))
214+
.collect::<HashMap<&'static str, Vec<LintId>>>()
215+
.find_equiv(&lint_name.as_slice()) {
216+
Some(v) => {
217+
v.iter()
218+
.map(|lint_id: &LintId|
219+
self.set_level(*lint_id, (level, CommandLine)))
220+
.collect::<Vec<()>>();
221+
}
222+
None => sess.err(format!("unknown {} flag: {}",
223+
level.as_str(), lint_name).as_slice()),
224+
}
225+
}
175226
}
176227
}
177228
}
@@ -305,7 +356,7 @@ impl<'a> Context<'a> {
305356
krate: krate,
306357
exported_items: exported_items,
307358
lints: lint_store,
308-
level_stack: vec!(),
359+
level_stack: vec![],
309360
node_levels: RefCell::new(HashMap::new()),
310361
}
311362
}
@@ -359,35 +410,46 @@ impl<'a> Context<'a> {
359410
let mut pushed = 0u;
360411

361412
for result in gather_attrs(attrs).move_iter() {
362-
let (lint_id, level, span) = match result {
413+
let v = match result {
363414
Err(span) => {
364415
self.tcx.sess.span_err(span, "malformed lint attribute");
365416
continue;
366417
}
367418
Ok((lint_name, level, span)) => {
368419
match self.lints.by_name.find_equiv(&lint_name.get()) {
369-
Some(&lint_id) => (lint_id, level, span),
420+
Some(&lint_id) => vec![(lint_id, level, span)],
370421
None => {
371-
self.span_lint(builtin::UNRECOGNIZED_LINT, span,
372-
format!("unknown `{}` attribute: `{}`",
373-
level.as_str(), lint_name).as_slice());
374-
continue;
422+
match self.lints.lint_groups.find_equiv(&lint_name.get()) {
423+
Some(&(ref v, _)) => v.iter()
424+
.map(|lint_id: &LintId|
425+
(*lint_id, level, span))
426+
.collect(),
427+
None => {
428+
self.span_lint(builtin::UNRECOGNIZED_LINT, span,
429+
format!("unknown `{}` attribute: `{}`",
430+
level.as_str(), lint_name).as_slice());
431+
continue;
432+
}
433+
}
375434
}
376435
}
377436
}
378437
};
379438

380-
let now = self.lints.get_level_source(lint_id).val0();
381-
if now == Forbid && level != Forbid {
382-
let lint_name = lint_id.as_str();
383-
self.tcx.sess.span_err(span,
384-
format!("{}({}) overruled by outer forbid({})",
385-
level.as_str(), lint_name, lint_name).as_slice());
386-
} else if now != level {
387-
let src = self.lints.get_level_source(lint_id).val1();
388-
self.level_stack.push((lint_id, (now, src)));
389-
pushed += 1;
390-
self.lints.set_level(lint_id, (level, Node(span)));
439+
for (lint_id, level, span) in v.move_iter() {
440+
let now = self.lints.get_level_source(lint_id).val0();
441+
if now == Forbid && level != Forbid {
442+
let lint_name = lint_id.as_str();
443+
self.tcx.sess.span_err(span,
444+
format!("{}({}) overruled by outer forbid({})",
445+
level.as_str(), lint_name,
446+
lint_name).as_slice());
447+
} else if now != level {
448+
let src = self.lints.get_level_source(lint_id).val1();
449+
self.level_stack.push((lint_id, (now, src)));
450+
pushed += 1;
451+
self.lints.set_level(lint_id, (level, Node(span)));
452+
}
391453
}
392454
}
393455

src/librustc/middle/typeck/infer/equate.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub struct Equate<'f> {
2727
fields: CombineFields<'f>
2828
}
2929

30-
#[allow(non_snake_case_functions)]
30+
#[allow(non_snake_case)]
3131
pub fn Equate<'f>(cf: CombineFields<'f>) -> Equate<'f> {
3232
Equate { fields: cf }
3333
}

src/librustc/middle/typeck/infer/glb.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub struct Glb<'f> {
3434
fields: CombineFields<'f>
3535
}
3636

37-
#[allow(non_snake_case_functions)]
37+
#[allow(non_snake_case)]
3838
pub fn Glb<'f>(cf: CombineFields<'f>) -> Glb<'f> {
3939
Glb { fields: cf }
4040
}

src/librustc/middle/typeck/infer/lub.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub struct Lub<'f> {
3333
fields: CombineFields<'f>
3434
}
3535

36-
#[allow(non_snake_case_functions)]
36+
#[allow(non_snake_case)]
3737
pub fn Lub<'f>(cf: CombineFields<'f>) -> Lub<'f> {
3838
Lub { fields: cf }
3939
}

src/librustc/middle/typeck/infer/sub.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub struct Sub<'f> {
3232
fields: CombineFields<'f>
3333
}
3434

35-
#[allow(non_snake_case_functions)]
35+
#[allow(non_snake_case)]
3636
pub fn Sub<'f>(cf: CombineFields<'f>) -> Sub<'f> {
3737
Sub { fields: cf }
3838
}

0 commit comments

Comments
 (0)