&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);
/// 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());
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
krate.encode(&mut json).unwrap();
}
- (krate, map)
+ Some((krate, map))
}
pub struct CrateAnalysis {
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)
use back::link;
use driver::driver::{Input, FileInput, StrInput};
use driver::session::{Session, build_session};
+use lint::Lint;
use lint;
use metadata;
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 == "-" {
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() {
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;
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;
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`).
//
// 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.
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,
/// 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),*);
}
))
}
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,
};
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)));
}
}
// 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);
}
}
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,
}
}
-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);