diff --git a/src/libcollections/hash/mod.rs b/src/libcollections/hash/mod.rs index a264acf5d03a3..c59de19e822cc 100644 --- a/src/libcollections/hash/mod.rs +++ b/src/libcollections/hash/mod.rs @@ -157,7 +157,6 @@ macro_rules! impl_hash_tuple( ( $($name:ident)+) => ( impl),*> Hash for ($($name,)*) { - #[allow(uppercase_variables)] #[inline] #[allow(non_snake_case)] fn hash(&self, state: &mut S) { diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 23577dd29e0af..836285bc3135a 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -775,7 +775,7 @@ macro_rules! def_fn_mut( FnMut<($($args,)*),Result> for extern "Rust" fn($($args: $args,)*) -> Result { #[rust_call_abi_hack] - #[allow(uppercase_variables)] + #[allow(non_snake_case)] fn call_mut(&mut self, args: ($($args,)*)) -> Result { let ($($args,)*) = args; (*self)($($args,)*) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index da20c945cb04a..5ba54a7bfc19f 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -242,13 +242,17 @@ pub fn phase_2_configure_and_expand(sess: &Session, } }); - let Registry { syntax_exts, lint_passes, .. } = registry; + let Registry { syntax_exts, lint_passes, lint_groups, .. } = registry; { let mut ls = sess.lint_store.borrow_mut(); for pass in lint_passes.move_iter() { ls.register_pass(Some(sess), true, pass); } + + for (name, to) in lint_groups.move_iter() { + ls.register_group(Some(sess), true, name, to); + } } // Lint plugins are registered; now we can process command line flags. diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs index dc600e0da3ec0..e6bdf9badc87d 100644 --- a/src/librustc/driver/mod.rs +++ b/src/librustc/driver/mod.rs @@ -180,14 +180,26 @@ Available lint options: lints } + fn sort_lint_groups(lints: Vec<(&'static str, Vec, bool)>) + -> Vec<(&'static str, Vec)> { + let mut lints: Vec<_> = lints.move_iter().map(|(x, y, _)| (x, y)).collect(); + lints.sort_by(|&(x, _): &(&'static str, Vec), + &(y, _): &(&'static str, Vec)| { + x.cmp(&y) + }); + lints + } + let (plugin, builtin) = lint_store.get_lints().partitioned(|&(_, p)| p); let plugin = sort_lints(plugin); let builtin = sort_lints(builtin); - // FIXME (#7043): We should use the width in character cells rather than - // the number of codepoints. + let (plugin_groups, builtin_groups) = lint_store.get_lint_groups().partitioned(|&(_, _, p)| p); + let plugin_groups = sort_lint_groups(plugin_groups); + let builtin_groups = sort_lint_groups(builtin_groups); + let max_name_len = plugin.iter().chain(builtin.iter()) - .map(|&s| s.name.char_len()) + .map(|&s| s.name.width(true)) .max().unwrap_or(0); let padded = |x: &str| { " ".repeat(max_name_len - x.char_len()).append(x) @@ -208,16 +220,48 @@ Available lint options: print_lints(builtin); - match (loaded_plugins, plugin.len()) { - (false, 0) => { - println!("Compiler plugins can provide additional lints. To see a listing of these, \ - re-run `rustc -W help` with a crate filename."); + + + let max_name_len = plugin_groups.iter().chain(builtin_groups.iter()) + .map(|&(s, _)| s.width(true)) + .max().unwrap_or(0); + let padded = |x: &str| { + " ".repeat(max_name_len - x.char_len()).append(x) + }; + + println!("Lint groups provided by rustc:\n"); + println!(" {} {}", padded("name"), "sub-lints"); + println!(" {} {}", padded("----"), "---------"); + + let print_lint_groups = |lints: Vec<(&'static str, Vec)>| { + for (name, to) in lints.move_iter() { + let name = name.chars().map(|x| x.to_lowercase()) + .collect::().replace("_", "-"); + let desc = to.move_iter().map(|x| x.as_str()).collect::>().connect(", "); + println!(" {} {}", + padded(name.as_slice()), desc); } - (false, _) => fail!("didn't load lint plugins but got them anyway!"), - (true, 0) => println!("This crate does not load any lint plugins."), - (true, _) => { - println!("Lint checks provided by plugins loaded by this crate:\n"); - print_lints(plugin); + println!("\n"); + }; + + print_lint_groups(builtin_groups); + + match (loaded_plugins, plugin.len(), plugin_groups.len()) { + (false, 0, _) | (false, _, 0) => { + println!("Compiler plugins can provide additional lints and lint groups. To see a \ + listing of these, re-run `rustc -W help` with a crate filename."); + } + (false, _, _) => fail!("didn't load lint plugins but got them anyway!"), + (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."), + (true, l, g) => { + if l > 0 { + println!("Lint checks provided by plugins loaded by this crate:\n"); + print_lints(plugin); + } + if g > 0 { + println!("Lint groups provided by plugins loaded by this crate:\n"); + print_lint_groups(plugin_groups); + } } } } diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 71e966d77867e..4f4824a221995 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -736,7 +736,7 @@ impl LintPass for UnusedResult { } } -declare_lint!(NON_CAMEL_CASE_TYPES, Warn, +declare_lint!(pub NON_CAMEL_CASE_TYPES, Warn, "types, variants, traits and type parameters should have camel case names") pub struct NonCamelCaseTypes; @@ -844,7 +844,7 @@ fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { } } -declare_lint!(NON_SNAKE_CASE, Warn, +declare_lint!(pub NON_SNAKE_CASE, Warn, "methods, functions, lifetime parameters and modules should have snake case names") pub struct NonSnakeCase; @@ -930,8 +930,8 @@ impl LintPass for NonSnakeCase { self.check_snake_case(cx, "trait method", t.ident, t.span); } - fn check_lifetime_decl(&mut self, cx: &Context, t: &ast::Lifetime) { - self.check_snake_case(cx, "lifetime", t.name.ident(), t.span); + fn check_lifetime_decl(&mut self, cx: &Context, t: &ast::LifetimeDef) { + self.check_snake_case(cx, "lifetime", t.lifetime.name.ident(), t.lifetime.span); } fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { @@ -962,7 +962,7 @@ impl LintPass for NonSnakeCase { } } -declare_lint!(NON_UPPERCASE_STATICS, Allow, +declare_lint!(pub NON_UPPERCASE_STATICS, Allow, "static constants should have uppercase identifiers") pub struct NonUppercaseStatics; @@ -1143,7 +1143,7 @@ impl LintPass for UnsafeBlock { } } -declare_lint!(UNUSED_MUT, Warn, +declare_lint!(pub UNUSED_MUT, Warn, "detect mut variables which don't need to be mutable") pub struct UnusedMut; diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 28e4e1a564ef9..b40916dcc30aa 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -66,6 +66,10 @@ pub struct LintStore { /// Current levels of each lint, and where they were set. levels: HashMap, + + /// Map of registered lint groups to what lints they expand to. The bool + /// is true if the lint group was added by a plugin. + lint_groups: HashMap<&'static str, (Vec, bool)>, } impl LintStore { @@ -90,6 +94,7 @@ impl LintStore { passes: Some(vec!()), by_name: HashMap::new(), levels: HashMap::new(), + lint_groups: HashMap::new(), } } @@ -97,6 +102,10 @@ impl LintStore { self.lints.as_slice() } + pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec, bool)> { + self.lint_groups.iter().map(|(k, &(ref v, b))| (*k, v.clone(), b)).collect() + } + pub fn register_pass(&mut self, sess: Option<&Session>, from_plugin: bool, pass: LintPassObject) { for &lint in pass.get_lints().iter() { @@ -123,6 +132,25 @@ impl LintStore { self.passes.get_mut_ref().push(pass); } + pub fn register_group(&mut self, sess: Option<&Session>, + from_plugin: bool, name: &'static str, + to: Vec) { + let new = self.lint_groups.insert(name, (to, from_plugin)); + + if !new { + let msg = format!("duplicate specification of lint group {}", name); + match (sess, from_plugin) { + // We load builtin lints first, so a duplicate is a compiler bug. + // Use early_error when handling -W help with no crate. + (None, _) => early_error(msg.as_slice()), + (Some(sess), false) => sess.bug(msg.as_slice()), + + // A duplicate name from a plugin is a user error. + (Some(sess), true) => sess.err(msg.as_slice()), + } + } + } + pub fn register_builtin(&mut self, sess: Option<&Session>) { macro_rules! add_builtin ( ( $sess:ident, $($name:ident),*, ) => ( {$( @@ -136,6 +164,10 @@ impl LintStore { )*} )) + macro_rules! add_lint_group ( ( $sess:ident, $name:expr, $($lint:ident),* ) => ( + self.register_group($sess, false, $name, vec![$(LintId::of(builtin::$lint)),*]); + )) + add_builtin!(sess, HardwiredLints, WhileTrue, @@ -162,6 +194,13 @@ impl LintStore { MissingDoc, ) + add_lint_group!(sess, "bad_style", + NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPERCASE_STATICS) + + add_lint_group!(sess, "unused", + UNUSED_IMPORTS, UNUSED_VARIABLE, DEAD_ASSIGNMENT, DEAD_CODE, + UNUSED_MUT, UNREACHABLE_CODE) + // We have one lint pass defined in this module. self.register_pass(sess, false, box GatherNodeLevels as LintPassObject); } @@ -170,8 +209,20 @@ impl LintStore { for &(ref lint_name, level) in sess.opts.lint_opts.iter() { match self.by_name.find_equiv(&lint_name.as_slice()) { Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)), - None => sess.err(format!("unknown {} flag: {}", - level.as_str(), lint_name).as_slice()), + None => { + match self.lint_groups.iter().map(|(&x, &(ref y, _))| (x, y.clone())) + .collect::>>() + .find_equiv(&lint_name.as_slice()) { + Some(v) => { + v.iter() + .map(|lint_id: &LintId| + self.set_level(*lint_id, (level, CommandLine))) + .collect::>(); + } + None => sess.err(format!("unknown {} flag: {}", + level.as_str(), lint_name).as_slice()), + } + } } } } @@ -305,7 +356,7 @@ impl<'a> Context<'a> { krate: krate, exported_items: exported_items, lints: lint_store, - level_stack: vec!(), + level_stack: vec![], node_levels: RefCell::new(HashMap::new()), } } @@ -359,35 +410,46 @@ impl<'a> Context<'a> { let mut pushed = 0u; for result in gather_attrs(attrs).move_iter() { - let (lint_id, level, span) = match result { + let v = match result { Err(span) => { self.tcx.sess.span_err(span, "malformed lint attribute"); continue; } Ok((lint_name, level, span)) => { match self.lints.by_name.find_equiv(&lint_name.get()) { - Some(&lint_id) => (lint_id, level, span), + Some(&lint_id) => vec![(lint_id, level, span)], None => { - self.span_lint(builtin::UNRECOGNIZED_LINT, span, - format!("unknown `{}` attribute: `{}`", - level.as_str(), lint_name).as_slice()); - continue; + match self.lints.lint_groups.find_equiv(&lint_name.get()) { + Some(&(ref v, _)) => v.iter() + .map(|lint_id: &LintId| + (*lint_id, level, span)) + .collect(), + None => { + self.span_lint(builtin::UNRECOGNIZED_LINT, span, + format!("unknown `{}` attribute: `{}`", + level.as_str(), lint_name).as_slice()); + continue; + } + } } } } }; - let now = self.lints.get_level_source(lint_id).val0(); - if now == Forbid && level != Forbid { - let lint_name = lint_id.as_str(); - self.tcx.sess.span_err(span, - format!("{}({}) overruled by outer forbid({})", - level.as_str(), lint_name, lint_name).as_slice()); - } else if now != level { - let src = self.lints.get_level_source(lint_id).val1(); - self.level_stack.push((lint_id, (now, src))); - pushed += 1; - self.lints.set_level(lint_id, (level, Node(span))); + for (lint_id, level, span) in v.move_iter() { + let now = self.lints.get_level_source(lint_id).val0(); + if now == Forbid && level != Forbid { + let lint_name = lint_id.as_str(); + self.tcx.sess.span_err(span, + format!("{}({}) overruled by outer forbid({})", + level.as_str(), lint_name, + lint_name).as_slice()); + } else if now != level { + let src = self.lints.get_level_source(lint_id).val1(); + self.level_stack.push((lint_id, (now, src))); + pushed += 1; + self.lints.set_level(lint_id, (level, Node(span))); + } } } diff --git a/src/librustc/middle/typeck/infer/equate.rs b/src/librustc/middle/typeck/infer/equate.rs index 391027f9c4bf9..223d37ee1ea62 100644 --- a/src/librustc/middle/typeck/infer/equate.rs +++ b/src/librustc/middle/typeck/infer/equate.rs @@ -27,7 +27,7 @@ pub struct Equate<'f> { fields: CombineFields<'f> } -#[allow(non_snake_case_functions)] +#[allow(non_snake_case)] pub fn Equate<'f>(cf: CombineFields<'f>) -> Equate<'f> { Equate { fields: cf } } diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index 68b8031a04b68..8ae141ea19cc2 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -34,7 +34,7 @@ pub struct Glb<'f> { fields: CombineFields<'f> } -#[allow(non_snake_case_functions)] +#[allow(non_snake_case)] pub fn Glb<'f>(cf: CombineFields<'f>) -> Glb<'f> { Glb { fields: cf } } diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index 9c6c0763ad45e..49760ac92bf7d 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -33,7 +33,7 @@ pub struct Lub<'f> { fields: CombineFields<'f> } -#[allow(non_snake_case_functions)] +#[allow(non_snake_case)] pub fn Lub<'f>(cf: CombineFields<'f>) -> Lub<'f> { Lub { fields: cf } } diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index cc3abc279bf34..be4abb1ad8261 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -32,7 +32,7 @@ pub struct Sub<'f> { fields: CombineFields<'f> } -#[allow(non_snake_case_functions)] +#[allow(non_snake_case)] pub fn Sub<'f>(cf: CombineFields<'f>) -> Sub<'f> { Sub { fields: cf } } diff --git a/src/librustc/plugin/registry.rs b/src/librustc/plugin/registry.rs index 2581ba51c2e10..7fa3ee0ac63ed 100644 --- a/src/librustc/plugin/registry.rs +++ b/src/librustc/plugin/registry.rs @@ -10,7 +10,7 @@ //! Used by plugin crates to tell `rustc` about the plugins they provide. -use lint::LintPassObject; +use lint::{LintPassObject, LintId, Lint}; use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT}; use syntax::ext::base::{IdentTT, LetSyntaxTT, ItemDecorator, ItemModifier, BasicMacroExpander}; @@ -19,6 +19,8 @@ use syntax::codemap::Span; use syntax::parse::token; use syntax::ast; +use std::collections::HashMap; + /// Structure used to register plugins. /// /// A plugin registrar function takes an `&mut Registry` and should call @@ -36,6 +38,9 @@ pub struct Registry { #[doc(hidden)] pub lint_passes: Vec, + + #[doc(hidden)] + pub lint_groups: HashMap<&'static str, Vec>, } impl Registry { @@ -45,6 +50,7 @@ impl Registry { krate_span: krate.span, syntax_exts: vec!(), lint_passes: vec!(), + lint_groups: HashMap::new(), } } @@ -80,4 +86,9 @@ impl Registry { pub fn register_lint_pass(&mut self, lint_pass: LintPassObject) { self.lint_passes.push(lint_pass); } + + /// Register a lint group. + pub fn register_lint_group(&mut self, name: &'static str, to: Vec<&'static Lint>) { + self.lint_groups.insert(name, to.move_iter().map(|x| LintId::of(x)).collect()); + } } diff --git a/src/librustrt/local_data.rs b/src/librustrt/local_data.rs index fedea3c31e0b1..70dc03d06d699 100644 --- a/src/librustrt/local_data.rs +++ b/src/librustrt/local_data.rs @@ -218,10 +218,10 @@ impl KeyValue { // Do nothing. None } - (0, Some(newValue)) => { + (0, Some(new_value)) => { // The current value is uninitialized and we're storing a new value. unsafe { - ptr::write(&mut (*value_box).value, newValue); + ptr::write(&mut (*value_box).value, new_value); *(*value_box).refcount.get() = 1; None } @@ -234,10 +234,10 @@ impl KeyValue { Some(ret) } } - (1, Some(newValue)) => { + (1, Some(new_value)) => { // We have an initialized value and we're replacing it. let value_ref = unsafe { &mut (*value_box).value }; - let ret = mem::replace(value_ref, newValue); + let ret = mem::replace(value_ref, new_value); // Refcount is already 1, leave it as that. Some(ret) } diff --git a/src/test/auxiliary/lint_group_plugin_test.rs b/src/test/auxiliary/lint_group_plugin_test.rs new file mode 100644 index 0000000000000..4790ae11b2107 --- /dev/null +++ b/src/test/auxiliary/lint_group_plugin_test.rs @@ -0,0 +1,53 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// force-host + +#![feature(phase, plugin_registrar)] + +extern crate syntax; + +// Load rustc as a plugin to get macros +#[phase(plugin, link)] +extern crate rustc; + +use syntax::ast; +use syntax::parse::token; +use rustc::lint::{Context, LintPass, LintPassObject, LintArray}; +use rustc::plugin::Registry; + +declare_lint!(TEST_LINT, Warn, + "Warn about items named 'lintme'") + +declare_lint!(PLEASE_LINT, Warn, + "Warn about items named 'pleaselintme'") + +struct Pass; + +impl LintPass for Pass { + fn get_lints(&self) -> LintArray { + lint_array!(TEST_LINT, PLEASE_LINT) + } + + fn check_item(&mut self, cx: &Context, it: &ast::Item) { + let name = token::get_ident(it.ident); + if name.get() == "lintme" { + cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"); + } else if name.get() == "pleaselintme" { + cx.span_lint(PLEASE_LINT, it.span, "item is named 'pleaselintme'"); + } + } +} + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_lint_pass(box Pass as LintPassObject); + reg.register_lint_group("lint_me", vec![TEST_LINT, PLEASE_LINT]); +} diff --git a/src/test/compile-fail-fulldeps/lint-group-plugin-deny-cmdline.rs b/src/test/compile-fail-fulldeps/lint-group-plugin-deny-cmdline.rs new file mode 100644 index 0000000000000..5edaa78eeea38 --- /dev/null +++ b/src/test/compile-fail-fulldeps/lint-group-plugin-deny-cmdline.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:lint_group_plugin_test.rs +// ignore-stage1 +// compile-flags: -D lint-me + +#![feature(phase)] + +#[phase(plugin)] +extern crate lint_group_plugin_test; + +fn lintme() { } //~ ERROR item is named 'lintme' + +fn pleaselintme() { } //~ ERROR item is named 'pleaselintme' + +pub fn main() { + lintme(); + pleaselintme(); +} diff --git a/src/test/compile-fail/lint-group-style.rs b/src/test/compile-fail/lint-group-style.rs new file mode 100644 index 0000000000000..63d65fc06aa4c --- /dev/null +++ b/src/test/compile-fail/lint-group-style.rs @@ -0,0 +1,40 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(bad_style)] +//~^ NOTE lint level defined here +#![allow(dead_code)] + +fn CamelCase() {} //~ ERROR function `CamelCase` should have a snake case name + +#[allow(bad_style)] +mod test { + fn CamelCase() {} + + #[forbid(bad_style)] + //~^ NOTE lint level defined here + //~^^ NOTE lint level defined here + mod bad { + fn CamelCase() {} //~ ERROR function `CamelCase` should have a snake case name + + static bad: int = 1; //~ ERROR static constant `bad` should have an uppercase name + } + + mod warn { + #![warn(bad_style)] + //~^ NOTE lint level defined here + + fn CamelCase() {} //~ WARN function `CamelCase` should have a snake case name + + struct snake_case; //~ WARN type `snake_case` should have a camel case name + } +} + +fn main() {} diff --git a/src/test/run-pass-fulldeps/lint-group-plugin.rs b/src/test/run-pass-fulldeps/lint-group-plugin.rs new file mode 100644 index 0000000000000..726670b5d7f93 --- /dev/null +++ b/src/test/run-pass-fulldeps/lint-group-plugin.rs @@ -0,0 +1,28 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:lint_group_plugin_test.rs +// ignore-stage1 +// ignore-pretty + +#![feature(phase)] + +#[phase(plugin)] +extern crate lint_group_plugin_test; + +fn lintme() { } //~ WARNING item is named 'lintme' +fn pleaselintme() { } //~ WARNING item is named 'pleaselintme' + +#[allow(lint_me)] +pub fn main() { + fn lintme() { } + + fn pleaselintme() { } +}