Store the registered lints in the Session

This commit is contained in:
Keegan McAllister 2014-06-10 14:03:19 -07:00
parent 442fbc473e
commit 819f76ca82
8 changed files with 208 additions and 128 deletions

View File

@ -79,8 +79,12 @@ pub fn compile_input(sess: Session,
&sess);
let id = link::find_crate_id(krate.attrs.as_slice(),
outputs.out_filestem.as_slice());
let (expanded_crate, ast_map) =
phase_2_configure_and_expand(&sess, krate, &id);
let (expanded_crate, ast_map)
= match phase_2_configure_and_expand(&sess, krate, &id) {
None => return,
Some(p) => p,
};
(outputs, expanded_crate, ast_map)
};
write_out_deps(&sess, input, &outputs, &expanded_crate);
@ -173,10 +177,12 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
/// syntax expansion, secondary `cfg` expansion, synthesis of a test
/// harness if one is to be provided and injection of a dependency on the
/// standard library and prelude.
///
/// Returns `None` if we're aborting after handling -W help.
pub fn phase_2_configure_and_expand(sess: &Session,
mut krate: ast::Crate,
crate_id: &CrateId)
-> (ast::Crate, syntax::ast_map::Map) {
-> Option<(ast::Crate, syntax::ast_map::Map)> {
let time_passes = sess.time_passes();
*sess.crate_types.borrow_mut() = collect_crate_types(sess, krate.attrs.as_slice());
@ -212,6 +218,17 @@ pub fn phase_2_configure_and_expand(sess: &Session,
let Registry { syntax_exts, .. } = registry;
// Process command line flags for lints.
// Do this here because we will have lint plugins eventually.
if sess.opts.describe_lints {
super::describe_lints(&*sess.lint_store.borrow());
return None;
}
sess.lint_store.borrow_mut().process_command_line(sess);
// Abort if there are errors from lint processing or a plugin registrar.
sess.abort_if_errors();
krate = time(time_passes, "expansion", (krate, macros, syntax_exts),
|(krate, macros, syntax_exts)| {
// Windows dlls do not have rpaths, so they don't know how to find their
@ -254,7 +271,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
krate.encode(&mut json).unwrap();
}
(krate, map)
Some((krate, map))
}
pub struct CrateAnalysis {
@ -631,9 +648,11 @@ pub fn pretty_print_input(sess: Session,
let (krate, ast_map, is_expanded) = match ppm {
PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => {
let (krate, ast_map) = phase_2_configure_and_expand(&sess,
krate,
&id);
let (krate, ast_map)
= match phase_2_configure_and_expand(&sess, krate, &id) {
None => return,
Some(p) => p,
};
(krate, Some(ast_map), true)
}
_ => (krate, None, false)

View File

@ -13,6 +13,7 @@ pub use syntax::diagnostic;
use back::link;
use driver::driver::{Input, FileInput, StrInput};
use driver::session::{Session, build_session};
use lint::Lint;
use lint;
use metadata;
@ -48,15 +49,18 @@ fn run_compiler(args: &[String]) {
Some(matches) => matches,
None => return
};
let sopts = config::build_session_options(&matches);
if sopts.describe_lints {
describe_lints();
return;
}
let (input, input_file_path) = match matches.free.len() {
0u => early_error("no input filename given"),
0u => {
if sopts.describe_lints {
let mut ls = lint::LintStore::new();
ls.register_builtin(None);
describe_lints(&ls);
return;
}
early_error("no input filename given");
}
1u => {
let ifile = matches.free.get(0).as_slice();
if ifile == "-" {
@ -128,43 +132,56 @@ Additional help:
config::optgroups().as_slice()));
}
fn describe_lints() {
fn describe_lints(lint_store: &lint::LintStore) {
println!("
Available lint options:
-W <foo> Warn about <foo>
-A <foo> Allow <foo>
-D <foo> Deny <foo>
-F <foo> Forbid <foo> (deny, and deny all overrides)
");
let mut builtin_specs = lint::builtin_lint_specs();
builtin_specs.sort_by(|x, y| {
match x.default_level.cmp(&y.default_level) {
Equal => x.name.cmp(&y.name),
r => r,
}
});
fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> {
let mut lints: Vec<_> = lints.move_iter().map(|(x, _)| x).collect();
lints.sort_by(|x: &&Lint, y: &&Lint| {
match x.default_level.cmp(&y.default_level) {
Equal => x.name.cmp(&y.name),
r => r,
}
});
lints
}
// FIXME: What if someone uses combining characters or East Asian fullwidth
// characters in a lint name?!?!?
let max_name_len = builtin_specs.iter()
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 max_name_len = builtin.iter()
.map(|&s| s.name.char_len())
.max().unwrap_or(0);
let padded = |x: &str| {
format!("{}{}", " ".repeat(max_name_len - x.char_len()), x)
" ".repeat(max_name_len - x.char_len()).append(x)
};
println!("\nAvailable lint checks:\n");
println!("Lint checks provided by rustc:\n");
println!(" {} {:7.7s} {}", padded("name"), "default", "meaning");
println!(" {} {:7.7s} {}", padded("----"), "-------", "-------");
println!("");
for spec in builtin_specs.move_iter() {
let name = spec.name.replace("_", "-");
println!(" {} {:7.7s} {}",
padded(name.as_slice()), spec.default_level.as_str(), spec.desc);
}
println!("");
let print_lints = |lints: Vec<&Lint>| {
for lint in lints.move_iter() {
let name = lint.name.replace("_", "-");
println!(" {} {:7.7s} {}",
padded(name.as_slice()), lint.default_level.as_str(), lint.desc);
}
println!("\n");
};
print_lints(builtin);
// Describe lint plugins here once they exist.
}
fn describe_debug_flags() {

View File

@ -43,6 +43,7 @@ pub struct Session {
// expected to be absolute. `None` means that there is no source file.
pub local_crate_source_file: Option<Path>,
pub working_dir: Path,
pub lint_store: RefCell<lint::LintStore>,
pub lints: RefCell<NodeMap<Vec<(lint::LintId, codemap::Span, String)>>>,
pub node_id: Cell<ast::NodeId>,
pub crate_types: RefCell<Vec<config::CrateType>>,
@ -226,7 +227,7 @@ pub fn build_session_(sopts: config::Options,
}
);
Session {
let sess = Session {
targ_cfg: target_cfg,
opts: sopts,
cstore: CStore::new(token::get_ident_interner()),
@ -238,12 +239,16 @@ pub fn build_session_(sopts: config::Options,
default_sysroot: default_sysroot,
local_crate_source_file: local_crate_source_file,
working_dir: os::getcwd(),
lint_store: RefCell::new(lint::LintStore::new()),
lints: RefCell::new(NodeMap::new()),
node_id: Cell::new(1),
crate_types: RefCell::new(Vec::new()),
features: front::feature_gate::Features::new(),
recursion_limit: Cell::new(64),
}
};
sess.lint_store.borrow_mut().register_builtin(Some(&sess));
sess
}
// Seems out of place, but it uses session, so I'm putting it here

View File

@ -1517,7 +1517,7 @@ impl LintPass for GatherNodeLevels {
match it.node {
ast::ItemEnum(..) => {
let lint_id = lint::LintId::of(variant_size_difference);
match cx.get_level_source(lint_id) {
match cx.lints.get_level_source(lint_id) {
lvlsrc @ (lvl, _) if lvl != lint::Allow => {
cx.insert_node_level(it.id, lint_id, lvlsrc);
},

View File

@ -48,6 +48,7 @@ use middle::ty;
use middle::typeck::astconv::AstConv;
use middle::typeck::infer;
use driver::session::Session;
use driver::early_error;
use std::collections::HashMap;
use std::rc::Rc;
@ -58,6 +59,7 @@ use std::default::Default;
use std::hash::Hash;
use std::tuple::Tuple2;
use std::hash;
use std::mem;
use syntax::ast_util::IdVisitingOperation;
use syntax::attr::AttrMetaMethods;
use syntax::attr;
@ -115,7 +117,7 @@ pub struct Lint {
pub desc: &'static str,
}
type LintArray = &'static [&'static Lint];
pub type LintArray = &'static [&'static Lint];
/// Trait for types providing lint checks. Each `check` method checks a single
/// syntax node, and should not invoke methods recursively (unlike `Visitor`).
@ -123,7 +125,7 @@ type LintArray = &'static [&'static Lint];
//
// FIXME: eliminate the duplication with `Visitor`. But this also
// contains a few lint-specific methods with no equivalent in `Visitor`.
trait LintPass {
pub trait LintPass {
/// Get descriptions of the lints this `LintPass` object can emit.
///
/// NB: there is no enforcement that the object only emits lints it registered.
@ -246,15 +248,117 @@ pub enum LintSource {
pub type LevelSource = (Level, LintSource);
struct Context<'a> {
/// Information about the registered lints.
/// This is basically the subset of `Context` that we can
/// build early in the compile pipeline.
pub struct LintStore {
/// Registered lints. The bool is true if the lint was
/// added by a plugin.
lints: Vec<(&'static Lint, bool)>,
/// Trait objects for each lint pass.
lint_objects: Vec<RefCell<LintPassObject>>,
passes: Vec<RefCell<LintPassObject>>,
/// Lints indexed by name.
lints_by_name: HashMap<&'static str, LintId>,
by_name: HashMap<&'static str, LintId>,
/// Current levels of each lint, and where they were set.
levels: HashMap<LintId, LevelSource>,
}
impl LintStore {
fn get_level_source(&self, lint: LintId) -> LevelSource {
match self.levels.find(&lint) {
Some(&s) => s,
None => (Allow, Default),
}
}
fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
if lvlsrc.val0() == Allow {
self.levels.remove(&lint);
} else {
self.levels.insert(lint, lvlsrc);
}
}
pub fn new() -> LintStore {
LintStore {
lints: vec!(),
passes: vec!(),
by_name: HashMap::new(),
levels: HashMap::new(),
}
}
pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] {
self.lints.as_slice()
}
pub fn register_pass(&mut self, sess: Option<&Session>,
from_plugin: bool, pass: LintPassObject) {
for &lint in pass.get_lints().iter() {
self.lints.push((lint, from_plugin));
let id = LintId::of(lint);
if !self.by_name.insert(lint.name, id) {
let msg = format!("duplicate specification of lint {}", lint.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()),
}
}
if lint.default_level != Allow {
self.levels.insert(id, (lint.default_level, Default));
}
}
self.passes.push(RefCell::new(pass));
}
pub fn register_builtin(&mut self, sess: Option<&Session>) {
macro_rules! add_builtin_lints ( ( $sess:ident, $($name:ident),*, ) => (
{$(
{
let obj: builtin::$name = Default::default();
self.register_pass($sess, false, box obj as LintPassObject);
};
)*}
))
add_builtin_lints!(sess,
WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory,
RawPointerDeriving, UnusedAttribute, PathStatement,
UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes,
NonSnakeCaseFunctions, NonUppercaseStatics,
NonUppercasePatternStatics, UppercaseVariables,
UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut,
UnnecessaryAllocation, MissingDoc, Stability,
GatherNodeLevels, HardwiredLints,
)
}
pub fn process_command_line(&mut self, sess: &Session) {
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()),
}
}
}
}
/// Context for lint checking.
pub struct Context<'a> {
/// The store of registered lints.
lints: LintStore,
/// Context we're checking in (used to access fields like sess).
tcx: &'a ty::ctxt,
@ -271,7 +375,7 @@ struct Context<'a> {
/// Convenience macro for calling a `LintPass` method on every pass in the context.
macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => (
for obj in $cx.lint_objects.iter() {
for obj in $cx.lints.passes.iter() {
obj.borrow_mut().$f($cx, $($args),*);
}
))
@ -316,26 +420,11 @@ pub fn emit_lint(sess: &Session, lint: &'static Lint,
}
impl<'a> Context<'a> {
fn get_level_source(&self, lint: LintId) -> LevelSource {
match self.levels.find(&lint) {
Some(&s) => s,
None => (Allow, Default),
}
}
fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
if lvlsrc.val0() == Allow {
self.levels.remove(&lint);
} else {
self.levels.insert(lint, lvlsrc);
}
}
fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
let (level, src) = match self.levels.find(&LintId::of(lint)) {
pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
let (level, src) = match self.lints.levels.find(&LintId::of(lint)) {
None => return,
Some(&(Warn, src))
=> (self.get_level_source(LintId::of(builtin::warnings)).val0(), src),
=> (self.lints.get_level_source(LintId::of(builtin::warnings)).val0(), src),
Some(&pair) => pair,
};
@ -357,17 +446,17 @@ impl<'a> Context<'a> {
let lint_attrs = self.gather_lint_attrs(attrs);
let mut pushed = 0u;
for (lint_id, level, span) in lint_attrs.move_iter() {
let now = self.get_level_source(lint_id).val0();
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.get_level_source(lint_id).val1();
let src = self.lints.get_level_source(lint_id).val1();
self.level_stack.push((lint_id, (now, src)));
pushed += 1;
self.set_level(lint_id, (level, Node(span)));
self.lints.set_level(lint_id, (level, Node(span)));
}
}
@ -378,7 +467,7 @@ impl<'a> Context<'a> {
// rollback
for _ in range(0, pushed) {
let (lint, lvlsrc) = self.level_stack.pop().unwrap();
self.set_level(lint, lvlsrc);
self.lints.set_level(lint, lvlsrc);
}
}
@ -419,7 +508,7 @@ impl<'a> Context<'a> {
for meta in metas.iter() {
match meta.node {
ast::MetaWord(ref lint_name) => {
match self.lints_by_name.find_equiv(lint_name) {
match self.lints.by_name.find_equiv(lint_name) {
Some(lint_id) => out.push((*lint_id, level, meta.span)),
None => self.span_lint(builtin::unrecognized_lint,
@ -636,75 +725,21 @@ impl<'a> IdVisitingOperation for Context<'a> {
}
}
fn builtin_lints() -> Vec<Box<LintPass>> {
macro_rules! builtin_lints (( $($name:ident),*, ) => (
vec!($(
{
let obj: builtin::$name = Default::default();
box obj as LintPassObject
}
),*)
))
builtin_lints!(
WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory,
RawPointerDeriving, UnusedAttribute, PathStatement,
UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes,
NonSnakeCaseFunctions, NonUppercaseStatics,
NonUppercasePatternStatics, UppercaseVariables,
UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut,
UnnecessaryAllocation, MissingDoc, Stability,
GatherNodeLevels, HardwiredLints,
)
}
/// Get specs for all builtin lints. Used for `-W help`.
pub fn builtin_lint_specs() -> Vec<&'static Lint> {
builtin_lints().move_iter()
.flat_map(|x| x.get_lints().iter().map(|&y| y))
.collect()
}
pub fn check_crate(tcx: &ty::ctxt,
exported_items: &ExportedItems,
krate: &ast::Crate) {
let lints = builtin_lints().move_iter().map(|x| RefCell::new(x)).collect();
// We want to own the lint store, so move it out of the session.
let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(),
LintStore::new());
let mut cx = Context {
lint_objects: lints,
lints_by_name: HashMap::new(),
levels: HashMap::new(),
lints: lint_store,
tcx: tcx,
level_stack: Vec::new(),
node_levels: RefCell::new(HashMap::new()),
};
// Index the lints by name, and set the default levels.
for obj in cx.lint_objects.iter() {
for &lint in obj.borrow_mut().get_lints().iter() {
let id = LintId::of(lint);
if !cx.lints_by_name.insert(lint.name, id) {
cx.tcx.sess.err(format!("duplicate specification of lint {}",
lint.name).as_slice());
}
if lint.default_level != Allow {
cx.levels.insert(id, (lint.default_level, Default));
}
}
}
// Set command line lint levels.
for &(ref lint_name, level) in tcx.sess.opts.lint_opts.iter() {
match cx.lints_by_name.find_equiv(&lint_name.as_slice()) {
Some(&lint_id) => cx.set_level(lint_id, (level, CommandLine)),
None => cx.tcx.sess.err(format!("unknown {} flag: {}",
level.as_str(), lint_name).as_slice()),
}
}
tcx.sess.abort_if_errors();
// Visit the whole crate.
cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
cx.visit_id(ast::CRATE_NODE_ID);

View File

@ -120,7 +120,8 @@ fn test_env(_test_name: &str,
name: "test".to_owned(),
version: None };
let (krate, ast_map) =
driver::phase_2_configure_and_expand(&sess, krate, &krate_id);
driver::phase_2_configure_and_expand(&sess, krate, &krate_id)
.expect("phase 2 aborted");
// run just enough stuff to build a tcx:
let lang_items = lang_items::collect_language_items(&krate, &sess);

View File

@ -102,8 +102,10 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
}
let krate = phase_1_parse_input(&sess, cfg, &input);
let (krate, ast_map) = phase_2_configure_and_expand(&sess, krate,
&from_str("rustdoc").unwrap());
let (krate, ast_map)
= phase_2_configure_and_expand(&sess, krate, &from_str("rustdoc").unwrap())
.expect("phase_2_configure_and_expand aborted in rustdoc!");
let driver::driver::CrateAnalysis {
exported_items, public_items, ty_cx, ..
} = phase_3_run_analysis_passes(sess, &krate, ast_map);

View File

@ -69,7 +69,8 @@ pub fn run(input: &str,
}));
let krate = driver::phase_1_parse_input(&sess, cfg, &input);
let (krate, _) = driver::phase_2_configure_and_expand(&sess, krate,
&from_str("rustdoc-test").unwrap());
&from_str("rustdoc-test").unwrap())
.expect("phase_2_configure_and_expand aborted in rustdoc!");
let ctx = box(GC) core::DocContext {
krate: krate,