auto merge of #15024 : kmcallister/rust/lint, r=alexcrichton

This is a rebase of #14804 with two new commits on top to implement and test lint plugins.

r? @alexcrichton @huonw: Can you take a look at the new commits, and also weigh in about any issues from the old PR that you feel are still unresolved? I'm leaving the old branch alone to preserve discussion history.
This commit is contained in:
bors 2014-06-24 22:06:48 +00:00
commit 05ca9f747d
30 changed files with 2890 additions and 2136 deletions

View File

@ -19,7 +19,7 @@ use back;
use back::link;
use back::target_strs;
use back::{arm, x86, x86_64, mips, mipsel};
use middle::lint;
use lint;
use syntax::abi;
use syntax::ast;
@ -70,7 +70,8 @@ pub struct Options {
pub gc: bool,
pub optimize: OptLevel,
pub debuginfo: DebugInfoLevel,
pub lint_opts: Vec<(lint::Lint, lint::Level)> ,
pub lint_opts: Vec<(String, lint::Level)>,
pub describe_lints: bool,
pub output_types: Vec<back::link::OutputType> ,
// This was mutable for rustpkg, which updates search paths based on the
// parsed code. It remains mutable in case its replacements wants to use
@ -104,6 +105,7 @@ pub fn basic_options() -> Options {
optimize: No,
debuginfo: NoDebugInfo,
lint_opts: Vec::new(),
describe_lints: false,
output_types: Vec::new(),
addl_lib_search_paths: RefCell::new(HashSet::new()),
maybe_sysroot: None,
@ -585,30 +587,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let no_trans = matches.opt_present("no-trans");
let no_analysis = matches.opt_present("no-analysis");
let lint_levels = [lint::Allow, lint::Warn,
lint::Deny, lint::Forbid];
let mut lint_opts = Vec::new();
let lint_dict = lint::get_lint_dict();
for level in lint_levels.iter() {
let level_name = lint::level_to_str(*level);
let mut lint_opts = vec!();
let mut describe_lints = false;
let level_short = level_name.slice_chars(0, 1);
let level_short = level_short.to_ascii().to_upper().into_str();
let flags = matches.opt_strs(level_short.as_slice())
.move_iter()
.collect::<Vec<_>>()
.append(matches.opt_strs(level_name).as_slice());
for lint_name in flags.iter() {
let lint_name = lint_name.replace("-", "_").into_string();
match lint_dict.find_equiv(&lint_name) {
None => {
early_error(format!("unknown {} flag: {}",
level_name,
lint_name).as_slice());
}
Some(lint) => {
lint_opts.push((lint.lint, *level));
}
for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() {
for lint_name in matches.opt_strs(level.as_str()).move_iter() {
if lint_name.as_slice() == "help" {
describe_lints = true;
} else {
lint_opts.push((lint_name.replace("-", "_").into_string(), level));
}
}
}
@ -752,6 +739,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
optimize: opt_level,
debuginfo: debuginfo,
lint_opts: lint_opts,
describe_lints: describe_lints,
output_types: output_types,
addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
maybe_sysroot: sysroot_opt,

View File

@ -20,12 +20,13 @@ use metadata::common::LinkMeta;
use metadata::creader;
use middle::cfg;
use middle::cfg::graphviz::LabelledCFG;
use middle::{trans, freevars, stability, kind, ty, typeck, lint, reachable};
use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
use middle::dependency_format;
use middle;
use plugin::load::Plugins;
use plugin::registry::Registry;
use plugin;
use lint;
use util::common::time;
use util::ppaux;
use util::nodemap::{NodeSet};
@ -78,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);
@ -172,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());
@ -209,7 +216,24 @@ pub fn phase_2_configure_and_expand(sess: &Session,
}
});
let Registry { syntax_exts, .. } = registry;
let Registry { syntax_exts, lint_passes, .. } = registry;
{
let mut ls = sess.lint_store.borrow_mut();
for pass in lint_passes.move_iter() {
ls.register_pass(Some(sess), true, pass);
}
}
// Lint plugins are registered; now we can process command line flags.
if sess.opts.describe_lints {
super::describe_lints(&*sess.lint_store.borrow(), true);
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)| {
@ -253,7 +277,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
krate.encode(&mut json).unwrap();
}
(krate, map)
Some((krate, map))
}
pub struct CrateAnalysis {
@ -366,7 +390,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
});
time(time_passes, "lint checking", (), |_|
lint::check_crate(&ty_cx, &exported_items, krate));
lint::check_crate(&ty_cx, krate, &exported_items));
CrateAnalysis {
exp_map2: exp_map2,
@ -630,9 +654,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)
@ -766,7 +792,7 @@ pub fn collect_crate_types(session: &Session,
}
Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
Some(_) => {
session.add_lint(lint::UnknownCrateType,
session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPE,
ast::CRATE_NODE_ID,
a.span,
"invalid `crate_type` \
@ -774,7 +800,7 @@ pub fn collect_crate_types(session: &Session,
None
}
_ => {
session.add_lint(lint::UnknownCrateType,
session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPE,
ast::CRATE_NODE_ID,
a.span,
"`crate_type` requires a \

View File

@ -13,11 +13,11 @@ pub use syntax::diagnostic;
use back::link;
use driver::driver::{Input, FileInput, StrInput};
use driver::session::{Session, build_session};
use middle::lint;
use lint::Lint;
use lint;
use metadata;
use std::any::AnyRefExt;
use std::cmp;
use std::io;
use std::os;
use std::str;
@ -49,9 +49,18 @@ fn run_compiler(args: &[String]) {
Some(matches) => matches,
None => return
};
let sopts = config::build_session_options(&matches);
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, false);
return;
}
early_error("no input filename given");
}
1u => {
let ifile = matches.free.get(0).as_slice();
if ifile == "-" {
@ -66,7 +75,6 @@ fn run_compiler(args: &[String]) {
_ => early_error("multiple input filenames provided")
};
let sopts = config::build_session_options(&matches);
let sess = build_session(sopts, input_file_path);
let cfg = config::build_configuration(&sess);
let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
@ -124,41 +132,68 @@ Additional help:
config::optgroups().as_slice()));
}
fn describe_warnings() {
fn describe_lints(lint_store: &lint::LintStore, loaded_plugins: bool) {
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 lint_dict = lint::get_lint_dict();
let mut lint_dict = lint_dict.move_iter()
.map(|(k, v)| (v, k))
.collect::<Vec<(lint::LintSpec, &'static str)> >();
lint_dict.as_mut_slice().sort();
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) {
// The sort doesn't case-fold but it's doubtful we care.
Equal => x.name.cmp(&y.name),
r => r,
}
});
lints
}
let mut max_key = 0;
for &(_, name) in lint_dict.iter() {
max_key = cmp::max(name.len(), max_key);
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 = plugin.iter().chain(builtin.iter())
.map(|&s| s.name.char_len())
.max().unwrap_or(0);
let padded = |x: &str| {
" ".repeat(max_name_len - x.char_len()).append(x)
};
println!("Lint checks provided by rustc:\n");
println!(" {} {:7.7s} {}", padded("name"), "default", "meaning");
println!(" {} {:7.7s} {}", padded("----"), "-------", "-------");
let print_lints = |lints: Vec<&Lint>| {
for lint in lints.move_iter() {
let name = lint.name_lower().replace("_", "-");
println!(" {} {:7.7s} {}",
padded(name.as_slice()), lint.default_level.as_str(), lint.desc);
}
println!("\n");
};
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.");
}
(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);
}
}
fn padded(max: uint, s: &str) -> String {
format!("{}{}", " ".repeat(max - s.len()), s)
}
println!("\nAvailable lint checks:\n");
println!(" {} {:7.7s} {}",
padded(max_key, "name"), "default", "meaning");
println!(" {} {:7.7s} {}\n",
padded(max_key, "----"), "-------", "-------");
for (spec, name) in lint_dict.move_iter() {
let name = name.replace("_", "-");
println!(" {} {:7.7s} {}",
padded(max_key, name.as_slice()),
lint::level_to_str(spec.default),
spec.desc);
}
println!("");
}
fn describe_debug_flags() {
@ -214,12 +249,7 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
return None;
}
let lint_flags = matches.opt_strs("W").move_iter().collect::<Vec<_>>().append(
matches.opt_strs("warn").as_slice());
if lint_flags.iter().any(|x| x.as_slice() == "help") {
describe_warnings();
return None;
}
// Don't handle -W help here, because we might first load plugins.
let r = matches.opt_strs("Z");
if r.iter().any(|x| x.as_slice() == "help") {

View File

@ -14,7 +14,7 @@ use driver::driver;
use front;
use metadata::cstore::CStore;
use metadata::filesearch;
use middle::lint;
use lint;
use util::nodemap::NodeMap;
use syntax::ast::NodeId;
@ -43,7 +43,8 @@ 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 lints: RefCell<NodeMap<Vec<(lint::Lint, codemap::Span, String)>>>,
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>>,
pub features: front::feature_gate::Features,
@ -106,16 +107,17 @@ impl Session {
self.diagnostic().handler().unimpl(msg)
}
pub fn add_lint(&self,
lint: lint::Lint,
lint: &'static lint::Lint,
id: ast::NodeId,
sp: Span,
msg: String) {
let lint_id = lint::LintId::of(lint);
let mut lints = self.lints.borrow_mut();
match lints.find_mut(&id) {
Some(arr) => { arr.push((lint, sp, msg)); return; }
Some(arr) => { arr.push((lint_id, sp, msg)); return; }
None => {}
}
lints.insert(id, vec!((lint, sp, msg)));
lints.insert(id, vec!((lint_id, sp, msg)));
}
pub fn next_node_id(&self) -> ast::NodeId {
self.reserve_node_ids(1)
@ -225,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()),
@ -237,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

@ -18,7 +18,7 @@
//! Features are enabled in programs via the crate-level attributes of
//! `#![feature(...)]` with a comma-separated list of features.
use middle::lint;
use lint;
use syntax::abi::RustIntrinsic;
use syntax::ast::NodeId;
@ -409,7 +409,7 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) {
directive not necessary");
}
None => {
sess.add_lint(lint::UnknownFeatures,
sess.add_lint(lint::builtin::UNKNOWN_FEATURES,
ast::CRATE_NODE_ID,
mi.span,
"unknown feature".to_string());

View File

@ -56,7 +56,6 @@ pub mod middle {
pub mod check_match;
pub mod check_const;
pub mod check_static;
pub mod lint;
pub mod borrowck;
pub mod dataflow;
pub mod mem_categorization;
@ -113,6 +112,8 @@ pub mod driver;
pub mod plugin;
pub mod lint;
pub mod util {
pub mod common;
pub mod ppaux;
@ -126,6 +127,15 @@ pub mod lib {
pub mod llvmdeps;
}
// A private module so that macro-expanded idents like
// `::rustc::lint::Lint` will also work in `rustc` itself.
//
// `libstd` uses the same trick.
#[doc(hidden)]
mod rustc {
pub use lint;
}
pub fn main() {
let args = std::os::args().iter()
.map(|x| x.to_string())

1501
src/librustc/lint/builtin.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,675 @@
// Copyright 2012-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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Implementation of lint checking.
//!
//! The lint checking is mostly consolidated into one pass which runs just
//! before translation to LLVM bytecode. Throughout compilation, lint warnings
//! can be added via the `add_lint` method on the Session structure. This
//! requires a span and an id of the node that the lint is being added to. The
//! lint isn't actually emitted at that time because it is unknown what the
//! actual lint level at that location is.
//!
//! To actually emit lint warnings/errors, a separate pass is used just before
//! translation. A context keeps track of the current state of all lint levels.
//! Upon entering a node of the ast which can modify the lint settings, the
//! previous lint state is pushed onto a stack and the ast is then recursed
//! upon. As the ast is traversed, this keeps track of the current lint level
//! for all lint attributes.
use middle::privacy::ExportedItems;
use middle::ty;
use middle::typeck::astconv::AstConv;
use middle::typeck::infer;
use driver::session::Session;
use driver::early_error;
use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject};
use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
use lint::builtin;
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;
use std::tuple::Tuple2;
use std::mem;
use syntax::ast_util::IdVisitingOperation;
use syntax::attr::AttrMetaMethods;
use syntax::attr;
use syntax::codemap::Span;
use syntax::visit::{Visitor, FnKind};
use syntax::parse::token::InternedString;
use syntax::{ast, ast_util, visit};
/// 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.
/// This is only `None` while iterating over the objects. See the definition
/// of run_lints.
passes: Option<Vec<LintPassObject>>,
/// Lints indexed by name.
by_name: HashMap<String, 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: Some(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_lower(), id) {
let msg = format!("duplicate specification of lint {}", lint.name_lower());
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.get_mut_ref().push(pass);
}
pub fn register_builtin(&mut self, sess: Option<&Session>) {
macro_rules! add_builtin ( ( $sess:ident, $($name:ident),*, ) => (
{$(
self.register_pass($sess, false, box builtin::$name as LintPassObject);
)*}
))
macro_rules! add_builtin_with_new ( ( $sess:ident, $($name:ident),*, ) => (
{$(
self.register_pass($sess, false, box builtin::$name::new() as LintPassObject);
)*}
))
add_builtin!(sess,
HardwiredLints,
WhileTrue,
UnusedCasts,
CTypes,
HeapMemory,
UnusedAttribute,
PathStatement,
UnusedResult,
NonCamelCaseTypes,
NonSnakeCaseFunctions,
NonUppercaseStatics,
NonUppercasePatternStatics,
UppercaseVariables,
UnnecessaryParens,
UnusedUnsafe,
UnsafeBlock,
UnusedMut,
UnnecessaryAllocation,
Stability,
)
add_builtin_with_new!(sess,
TypeLimits,
RawPointerDeriving,
MissingDoc,
)
// We have one lint pass defined in this module.
self.register_pass(sess, false, box GatherNodeLevels as LintPassObject);
}
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> {
/// Type context we're checking in.
pub tcx: &'a ty::ctxt,
/// The crate being checked.
pub krate: &'a ast::Crate,
/// Items exported from the crate being checked.
pub exported_items: &'a ExportedItems,
/// The store of registered lints.
lints: LintStore,
/// When recursing into an attributed node of the ast which modifies lint
/// levels, this stack keeps track of the previous lint levels of whatever
/// was modified.
level_stack: Vec<(LintId, LevelSource)>,
/// Level of lints for certain NodeIds, stored here because the body of
/// the lint needs to run in trans.
node_levels: RefCell<HashMap<(ast::NodeId, LintId), LevelSource>>,
}
/// Convenience macro for calling a `LintPass` method on every pass in the context.
macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ({
// Move the vector of passes out of `$cx` so that we can
// iterate over it mutably while passing `$cx` to the methods.
let mut passes = $cx.lints.passes.take_unwrap();
for obj in passes.mut_iter() {
obj.$f($cx, $($args),*);
}
$cx.lints.passes = Some(passes);
}))
/// Parse the lint attributes into a vector, with `Err`s for malformed lint
/// attributes. Writing this as an iterator is an enormous mess.
pub fn gather_attrs(attrs: &[ast::Attribute])
-> Vec<Result<(InternedString, Level, Span), Span>> {
let mut out = vec!();
for attr in attrs.iter() {
let level = match Level::from_str(attr.name().get()) {
None => continue,
Some(lvl) => lvl,
};
attr::mark_used(attr);
let meta = attr.node.value;
let metas = match meta.node {
ast::MetaList(_, ref metas) => metas,
_ => {
out.push(Err(meta.span));
continue;
}
};
for meta in metas.iter() {
out.push(match meta.node {
ast::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)),
_ => Err(meta.span),
});
}
}
out
}
/// Emit a lint as a warning or an error (or not at all)
/// according to `level`.
///
/// This lives outside of `Context` so it can be used by checks
/// in trans that run after the main lint pass is finished. Most
/// lints elsewhere in the compiler should call
/// `Session::add_lint()` instead.
pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
lvlsrc: LevelSource, span: Option<Span>, msg: &str) {
let (mut level, source) = lvlsrc;
if level == Allow { return }
let name = lint.name_lower();
let mut note = None;
let msg = match source {
Default => {
format!("{}, #[{}({})] on by default", msg,
level.as_str(), name)
},
CommandLine => {
format!("{} [-{} {}]", msg,
match level {
Warn => 'W', Deny => 'D', Forbid => 'F',
Allow => fail!()
}, name.replace("_", "-"))
},
Node(src) => {
note = Some(src);
msg.to_string()
}
};
// For purposes of printing, we can treat forbid as deny.
if level == Forbid { level = Deny; }
match (level, span) {
(Warn, Some(sp)) => sess.span_warn(sp, msg.as_slice()),
(Warn, None) => sess.warn(msg.as_slice()),
(Deny, Some(sp)) => sess.span_err(sp, msg.as_slice()),
(Deny, None) => sess.err(msg.as_slice()),
_ => sess.bug("impossible level in raw_emit_lint"),
}
for span in note.move_iter() {
sess.span_note(span, "lint level defined here");
}
}
impl<'a> Context<'a> {
fn new(tcx: &'a ty::ctxt,
krate: &'a ast::Crate,
exported_items: &'a ExportedItems) -> Context<'a> {
// 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());
Context {
tcx: tcx,
krate: krate,
exported_items: exported_items,
lints: lint_store,
level_stack: vec!(),
node_levels: RefCell::new(HashMap::new()),
}
}
/// Get the overall compiler `Session` object.
pub fn sess(&'a self) -> &'a Session {
&self.tcx.sess
}
fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
let (level, src) = match self.lints.levels.find(&LintId::of(lint)) {
None => return,
Some(&(Warn, src)) => {
let lint_id = LintId::of(builtin::WARNINGS);
(self.lints.get_level_source(lint_id).val0(), src)
}
Some(&pair) => pair,
};
raw_emit_lint(&self.tcx.sess, lint, (level, src), span, msg);
}
/// Emit a lint at the appropriate level, with no associated span.
pub fn lint(&self, lint: &'static Lint, msg: &str) {
self.lookup_and_emit(lint, None, msg);
}
/// Emit a lint at the appropriate level, for a particular span.
pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
self.lookup_and_emit(lint, Some(span), msg);
}
/**
* Merge the lints specified by any lint attributes into the
* current lint context, call the provided function, then reset the
* lints in effect to their previous state.
*/
fn with_lint_attrs(&mut self,
attrs: &[ast::Attribute],
f: |&mut Context|) {
// Parse all of the lint attributes, and then add them all to the
// current dictionary of lint information. Along the way, keep a history
// of what we changed so we can roll everything back after invoking the
// specified closure
let mut pushed = 0u;
for result in gather_attrs(attrs).move_iter() {
let (lint_id, level, span) = 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),
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)));
}
}
run_lints!(self, enter_lint_attrs, attrs);
f(self);
run_lints!(self, exit_lint_attrs, attrs);
// rollback
for _ in range(0, pushed) {
let (lint, lvlsrc) = self.level_stack.pop().unwrap();
self.lints.set_level(lint, lvlsrc);
}
}
fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
let mut v = ast_util::IdVisitor {
operation: self,
pass_through_items: false,
visited_outermost: false,
};
f(&mut v);
}
}
impl<'a> AstConv for Context<'a>{
fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx }
fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype {
ty::lookup_item_type(self.tcx, id)
}
fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
ty::lookup_trait_def(self.tcx, id)
}
fn ty_infer(&self, _span: Span) -> ty::t {
infer::new_infer_ctxt(self.tcx).next_ty_var()
}
}
impl<'a> Visitor<()> for Context<'a> {
fn visit_item(&mut self, it: &ast::Item, _: ()) {
self.with_lint_attrs(it.attrs.as_slice(), |cx| {
run_lints!(cx, check_item, it);
cx.visit_ids(|v| v.visit_item(it, ()));
visit::walk_item(cx, it, ());
})
}
fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) {
self.with_lint_attrs(it.attrs.as_slice(), |cx| {
run_lints!(cx, check_foreign_item, it);
visit::walk_foreign_item(cx, it, ());
})
}
fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
self.with_lint_attrs(i.attrs.as_slice(), |cx| {
run_lints!(cx, check_view_item, i);
cx.visit_ids(|v| v.visit_view_item(i, ()));
visit::walk_view_item(cx, i, ());
})
}
fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
run_lints!(self, check_pat, p);
visit::walk_pat(self, p, ());
}
fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
run_lints!(self, check_expr, e);
visit::walk_expr(self, e, ());
}
fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
run_lints!(self, check_stmt, s);
visit::walk_stmt(self, s, ());
}
fn visit_fn(&mut self, fk: &FnKind, decl: &ast::FnDecl,
body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
match *fk {
visit::FkMethod(_, _, m) => {
self.with_lint_attrs(m.attrs.as_slice(), |cx| {
run_lints!(cx, check_fn, fk, decl, body, span, id);
cx.visit_ids(|v| {
v.visit_fn(fk, decl, body, span, id, ());
});
visit::walk_fn(cx, fk, decl, body, span, ());
})
},
_ => {
run_lints!(self, check_fn, fk, decl, body, span, id);
visit::walk_fn(self, fk, decl, body, span, ());
}
}
}
fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
self.with_lint_attrs(t.attrs.as_slice(), |cx| {
run_lints!(cx, check_ty_method, t);
visit::walk_ty_method(cx, t, ());
})
}
fn visit_struct_def(&mut self,
s: &ast::StructDef,
ident: ast::Ident,
g: &ast::Generics,
id: ast::NodeId,
_: ()) {
run_lints!(self, check_struct_def, s, ident, g, id);
visit::walk_struct_def(self, s, ());
run_lints!(self, check_struct_def_post, s, ident, g, id);
}
fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
run_lints!(cx, check_struct_field, s);
visit::walk_struct_field(cx, s, ());
})
}
fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
run_lints!(cx, check_variant, v, g);
visit::walk_variant(cx, v, g, ());
})
}
// FIXME(#10894) should continue recursing
fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
run_lints!(self, check_ty, t);
}
fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) {
run_lints!(self, check_ident, sp, id);
}
fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId, _: ()) {
run_lints!(self, check_mod, m, s, n);
visit::walk_mod(self, m, ());
}
fn visit_local(&mut self, l: &ast::Local, _: ()) {
run_lints!(self, check_local, l);
visit::walk_local(self, l, ());
}
fn visit_block(&mut self, b: &ast::Block, _: ()) {
run_lints!(self, check_block, b);
visit::walk_block(self, b, ());
}
fn visit_arm(&mut self, a: &ast::Arm, _: ()) {
run_lints!(self, check_arm, a);
visit::walk_arm(self, a, ());
}
fn visit_decl(&mut self, d: &ast::Decl, _: ()) {
run_lints!(self, check_decl, d);
visit::walk_decl(self, d, ());
}
fn visit_expr_post(&mut self, e: &ast::Expr, _: ()) {
run_lints!(self, check_expr_post, e);
}
fn visit_generics(&mut self, g: &ast::Generics, _: ()) {
run_lints!(self, check_generics, g);
visit::walk_generics(self, g, ());
}
fn visit_trait_method(&mut self, m: &ast::TraitMethod, _: ()) {
run_lints!(self, check_trait_method, m);
visit::walk_trait_method(self, m, ());
}
fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<ast::Lifetime>, _: ()) {
run_lints!(self, check_opt_lifetime_ref, sp, lt);
}
fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime, _: ()) {
run_lints!(self, check_lifetime_ref, lt);
}
fn visit_lifetime_decl(&mut self, lt: &ast::Lifetime, _: ()) {
run_lints!(self, check_lifetime_decl, lt);
}
fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf, _: ()) {
run_lints!(self, check_explicit_self, es);
visit::walk_explicit_self(self, es, ());
}
fn visit_mac(&mut self, mac: &ast::Mac, _: ()) {
run_lints!(self, check_mac, mac);
visit::walk_mac(self, mac, ());
}
fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId, _: ()) {
run_lints!(self, check_path, p, id);
visit::walk_path(self, p, ());
}
fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) {
run_lints!(self, check_attribute, attr);
}
}
// Output any lints that were previously added to the session.
impl<'a> IdVisitingOperation for Context<'a> {
fn visit_id(&self, id: ast::NodeId) {
match self.tcx.sess.lints.borrow_mut().pop(&id) {
None => {}
Some(lints) => {
for (lint_id, span, msg) in lints.move_iter() {
self.span_lint(lint_id.lint, span, msg.as_slice())
}
}
}
}
}
// This lint pass is defined here because it touches parts of the `Context`
// that we don't want to expose. It records the lint level at certain AST
// nodes, so that the variant size difference check in trans can call
// `raw_emit_lint`.
struct GatherNodeLevels;
impl LintPass for GatherNodeLevels {
fn get_lints(&self) -> LintArray {
lint_array!()
}
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
match it.node {
ast::ItemEnum(..) => {
let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCE);
match cx.lints.get_level_source(lint_id) {
lvlsrc @ (lvl, _) if lvl != Allow => {
cx.node_levels.borrow_mut()
.insert((it.id, lint_id), lvlsrc);
},
_ => { }
}
},
_ => { }
}
}
}
/// Perform lint checking on a crate.
///
/// Consumes the `lint_store` field of the `Session`.
pub fn check_crate(tcx: &ty::ctxt,
krate: &ast::Crate,
exported_items: &ExportedItems) {
let mut cx = Context::new(tcx, krate, exported_items);
// Visit the whole crate.
cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
cx.visit_id(ast::CRATE_NODE_ID);
cx.visit_ids(|v| {
v.visited_outermost = true;
visit::walk_crate(v, krate, ());
});
// since the root module isn't visited as an item (because it isn't an
// item), warn for it here.
run_lints!(cx, check_crate, krate);
visit::walk_crate(cx, krate, ());
});
// If we missed any lints added to the session, then there's a bug somewhere
// in the iteration code.
for (id, v) in tcx.sess.lints.borrow().iter() {
for &(lint, span, ref msg) in v.iter() {
tcx.sess.span_bug(span,
format!("unprocessed lint {} at {}: {}",
lint.as_str(), tcx.map.node_to_str(*id), *msg).as_slice())
}
}
tcx.sess.abort_if_errors();
*tcx.node_lint_levels.borrow_mut() = cx.node_levels.unwrap();
}

253
src/librustc/lint/mod.rs Normal file
View File

@ -0,0 +1,253 @@
// Copyright 2012-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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Lints, aka compiler warnings.
//!
//! A 'lint' check is a kind of miscellaneous constraint that a user _might_
//! want to enforce, but might reasonably want to permit as well, on a
//! module-by-module basis. They contrast with static constraints enforced by
//! other phases of the compiler, which are generally required to hold in order
//! to compile the program at all.
//!
//! Most lints can be written as `LintPass` instances. These run just before
//! translation to LLVM bytecode. The `LintPass`es built into rustc are defined
//! within `builtin.rs`, which has further comments on how to add such a lint.
//! rustc can also load user-defined lint plugins via the plugin mechanism.
//!
//! Some of rustc's lints are defined elsewhere in the compiler and work by
//! calling `add_lint()` on the overall `Session` object. This works when
//! it happens before the main lint pass, which emits the lints stored by
//! `add_lint()`. To emit lints after the main lint pass (from trans, for
//! example) requires more effort. See `emit_lint` and `GatherNodeLevels`
//! in `context.rs`.
#![macro_escape]
use std::hash;
use std::ascii::StrAsciiExt;
use syntax::codemap::Span;
use syntax::visit::FnKind;
use syntax::ast;
pub use lint::context::{Context, LintStore, raw_emit_lint, check_crate, gather_attrs};
/// Specification of a single lint.
pub struct Lint {
/// A string identifier for the lint.
///
/// This identifies the lint in attributes and in command-line arguments.
/// In those contexts it is always lowercase, but this field is compared
/// in a way which is case-insensitive for ASCII characters. This allows
/// `declare_lint!()` invocations to follow the convention of upper-case
/// statics without repeating the name.
///
/// The name is written with underscores, e.g. "unused_imports".
/// On the command line, underscores become dashes.
pub name: &'static str,
/// Default level for the lint.
pub default_level: Level,
/// Description of the lint or the issue it detects.
///
/// e.g. "imports that are never used"
pub desc: &'static str,
}
impl Lint {
/// Get the lint's name, with ASCII letters converted to lowercase.
pub fn name_lower(&self) -> String {
self.name.to_ascii_lower()
}
}
/// Build a `Lint` initializer.
#[macro_export]
macro_rules! lint_initializer (
($name:ident, $level:ident, $desc:expr) => (
::rustc::lint::Lint {
name: stringify!($name),
default_level: ::rustc::lint::$level,
desc: $desc,
}
)
)
/// Declare a static item of type `&'static Lint`.
#[macro_export]
macro_rules! declare_lint (
// FIXME(#14660): deduplicate
(pub $name:ident, $level:ident, $desc:expr) => (
pub static $name: &'static ::rustc::lint::Lint
= &lint_initializer!($name, $level, $desc);
);
($name:ident, $level:ident, $desc:expr) => (
static $name: &'static ::rustc::lint::Lint
= &lint_initializer!($name, $level, $desc);
);
)
/// Declare a static `LintArray` and return it as an expression.
#[macro_export]
macro_rules! lint_array ( ($( $lint:expr ),*) => (
{
static array: LintArray = &[ $( $lint ),* ];
array
}
))
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`). By default they
/// do nothing.
//
// FIXME: eliminate the duplication with `Visitor`. But this also
// contains a few lint-specific methods with no equivalent in `Visitor`.
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.
/// And some `rustc` internal `LintPass`es register lints to be emitted by other
/// parts of the compiler. If you want enforced access restrictions for your
/// `Lint`, make it a private `static` item in its own module.
fn get_lints(&self) -> LintArray;
fn check_crate(&mut self, _: &Context, _: &ast::Crate) { }
fn check_ident(&mut self, _: &Context, _: Span, _: ast::Ident) { }
fn check_mod(&mut self, _: &Context, _: &ast::Mod, _: Span, _: ast::NodeId) { }
fn check_view_item(&mut self, _: &Context, _: &ast::ViewItem) { }
fn check_foreign_item(&mut self, _: &Context, _: &ast::ForeignItem) { }
fn check_item(&mut self, _: &Context, _: &ast::Item) { }
fn check_local(&mut self, _: &Context, _: &ast::Local) { }
fn check_block(&mut self, _: &Context, _: &ast::Block) { }
fn check_stmt(&mut self, _: &Context, _: &ast::Stmt) { }
fn check_arm(&mut self, _: &Context, _: &ast::Arm) { }
fn check_pat(&mut self, _: &Context, _: &ast::Pat) { }
fn check_decl(&mut self, _: &Context, _: &ast::Decl) { }
fn check_expr(&mut self, _: &Context, _: &ast::Expr) { }
fn check_expr_post(&mut self, _: &Context, _: &ast::Expr) { }
fn check_ty(&mut self, _: &Context, _: &ast::Ty) { }
fn check_generics(&mut self, _: &Context, _: &ast::Generics) { }
fn check_fn(&mut self, _: &Context,
_: &FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { }
fn check_ty_method(&mut self, _: &Context, _: &ast::TypeMethod) { }
fn check_trait_method(&mut self, _: &Context, _: &ast::TraitMethod) { }
fn check_struct_def(&mut self, _: &Context,
_: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { }
fn check_struct_def_post(&mut self, _: &Context,
_: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { }
fn check_struct_field(&mut self, _: &Context, _: &ast::StructField) { }
fn check_variant(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) { }
fn check_opt_lifetime_ref(&mut self, _: &Context, _: Span, _: &Option<ast::Lifetime>) { }
fn check_lifetime_ref(&mut self, _: &Context, _: &ast::Lifetime) { }
fn check_lifetime_decl(&mut self, _: &Context, _: &ast::Lifetime) { }
fn check_explicit_self(&mut self, _: &Context, _: &ast::ExplicitSelf) { }
fn check_mac(&mut self, _: &Context, _: &ast::Mac) { }
fn check_path(&mut self, _: &Context, _: &ast::Path, _: ast::NodeId) { }
fn check_attribute(&mut self, _: &Context, _: &ast::Attribute) { }
/// Called when entering a syntax node that can have lint attributes such
/// as `#[allow(...)]`. Called with *all* the attributes of that node.
fn enter_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { }
/// Counterpart to `enter_lint_attrs`.
fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { }
}
/// A lint pass boxed up as a trait object.
pub type LintPassObject = Box<LintPass + 'static>;
/// Identifies a lint known to the compiler.
#[deriving(Clone)]
pub struct LintId {
// Identity is based on pointer equality of this field.
lint: &'static Lint,
}
impl PartialEq for LintId {
fn eq(&self, other: &LintId) -> bool {
(self.lint as *Lint) == (other.lint as *Lint)
}
}
impl Eq for LintId { }
impl<S: hash::Writer> hash::Hash<S> for LintId {
fn hash(&self, state: &mut S) {
let ptr = self.lint as *Lint;
ptr.hash(state);
}
}
impl LintId {
/// Get the `LintId` for a `Lint`.
pub fn of(lint: &'static Lint) -> LintId {
LintId {
lint: lint,
}
}
/// Get the name of the lint.
pub fn as_str(&self) -> String {
self.lint.name_lower()
}
}
/// Setting for how to handle a lint.
#[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum Level {
Allow, Warn, Deny, Forbid
}
impl Level {
/// Convert a level to a lower-case string.
pub fn as_str(self) -> &'static str {
match self {
Allow => "allow",
Warn => "warn",
Deny => "deny",
Forbid => "forbid",
}
}
/// Convert a lower-case string to a level.
pub fn from_str(x: &str) -> Option<Level> {
match x {
"allow" => Some(Allow),
"warn" => Some(Warn),
"deny" => Some(Deny),
"forbid" => Some(Forbid),
_ => None,
}
}
}
/// How a lint level was set.
#[deriving(Clone, PartialEq, Eq)]
pub enum LintSource {
/// Lint is at the default level as declared
/// in rustc or a plugin.
Default,
/// Lint level was set by an attribute.
Node(Span),
/// Lint level was set by a command-line flag.
CommandLine,
}
pub type LevelSource = (Level, LintSource);
pub mod builtin;
mod context;

View File

@ -13,7 +13,7 @@
// from live codes are live, and everything else is dead.
use middle::def;
use middle::lint::{Allow, contains_lint, DeadCode};
use lint;
use middle::privacy;
use middle::ty;
use middle::typeck;
@ -23,14 +23,13 @@ use std::collections::HashSet;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{local_def, is_local};
use syntax::attr::AttrMetaMethods;
use syntax::attr;
use syntax::codemap;
use syntax::parse::token;
use syntax::visit::Visitor;
use syntax::visit;
pub static DEAD_CODE_LINT_STR: &'static str = "dead_code";
// Any local node that may call something in its body block should be
// explored. For example, if it's a live NodeItem that is a
// function, then we should explore its block to check for codes that
@ -266,8 +265,19 @@ impl<'a> Visitor<MarkSymbolVisitorContext> for MarkSymbolVisitor<'a> {
}
fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
contains_lint(attrs, Allow, DEAD_CODE_LINT_STR)
|| attr::contains_name(attrs.as_slice(), "lang")
if attr::contains_name(attrs.as_slice(), "lang") {
return true;
}
let dead_code = lint::builtin::DEAD_CODE.name_lower();
for attr in lint::gather_attrs(attrs).move_iter() {
match attr {
Ok((ref name, lint::Allow, _))
if name.get() == dead_code.as_slice() => return true,
_ => (),
}
}
false
}
// This visitor seeds items that
@ -446,7 +456,7 @@ impl<'a> DeadVisitor<'a> {
ident: ast::Ident) {
self.tcx
.sess
.add_lint(DeadCode,
.add_lint(lint::builtin::DEAD_CODE,
id,
span,
format!("code is never used: `{}`",

File diff suppressed because it is too large Load Diff

View File

@ -104,10 +104,10 @@
use middle::def::*;
use middle::freevars;
use middle::lint::{UnusedVariable, DeadAssignment};
use middle::mem_categorization::Typer;
use middle::pat_util;
use middle::ty;
use lint;
use util::nodemap::NodeMap;
use std::fmt;
@ -1560,11 +1560,11 @@ impl<'a> Liveness<'a> {
};
if is_assigned {
self.ir.tcx.sess.add_lint(UnusedVariable, id, sp,
self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLE, id, sp,
format!("variable `{}` is assigned to, but never used",
*name));
} else {
self.ir.tcx.sess.add_lint(UnusedVariable, id, sp,
self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLE, id, sp,
format!("unused variable: `{}`", *name));
}
}
@ -1582,7 +1582,7 @@ impl<'a> Liveness<'a> {
if self.live_on_exit(ln, var).is_none() {
let r = self.should_warn(var);
for name in r.iter() {
self.ir.tcx.sess.add_lint(DeadAssignment, id, sp,
self.ir.tcx.sess.add_lint(lint::builtin::DEAD_ASSIGNMENT, id, sp,
format!("value assigned to `{}` is never read", *name));
}
}

View File

@ -17,7 +17,7 @@ use std::mem::replace;
use metadata::csearch;
use middle::def;
use middle::lint;
use lint;
use middle::resolve;
use middle::ty;
use middle::typeck::{MethodCall, MethodMap, MethodOrigin, MethodParam};
@ -1394,7 +1394,7 @@ impl<'a> Visitor<()> for VisiblePrivateTypesVisitor<'a> {
ast::TyPath(ref p, _, path_id) => {
if self.path_is_private_type(path_id) {
self.tcx.sess.add_lint(
lint::VisiblePrivateTypes,
lint::builtin::VISIBLE_PRIVATE_TYPES,
path_id, p.span,
"private type in exported type \
signature".to_string());

View File

@ -15,9 +15,9 @@ use metadata::csearch;
use metadata::decoder::{DefLike, DlDef, DlField, DlImpl};
use middle::def::*;
use middle::lang_items::LanguageItems;
use middle::lint::{UnnecessaryQualification, UnusedImports};
use middle::pat_util::pat_bindings;
use middle::subst::{ParamSpace, FnSpace, TypeSpace};
use lint;
use util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
use syntax::ast::*;
@ -4632,7 +4632,7 @@ impl<'a> Resolver<'a> {
match (def, unqualified_def) {
(Some((d, _)), Some((ud, _))) if d == ud => {
self.session
.add_lint(UnnecessaryQualification,
.add_lint(lint::builtin::UNNECESSARY_QUALIFICATION,
id,
path.span,
"unnecessary qualification".to_string());
@ -5487,7 +5487,7 @@ impl<'a> Resolver<'a> {
if !self.used_imports.contains(&(id, TypeNS)) &&
!self.used_imports.contains(&(id, ValueNS)) {
self.session
.add_lint(UnusedImports,
.add_lint(lint::builtin::UNUSED_IMPORTS,
id,
p.span,
"unused import".to_string());
@ -5511,7 +5511,7 @@ impl<'a> Resolver<'a> {
if !self.used_imports.contains(&(id, TypeNS)) &&
!self.used_imports.contains(&(id, ValueNS)) {
self.session.add_lint(UnusedImports,
self.session.add_lint(lint::builtin::UNUSED_IMPORTS,
id,
span,
"unused import".to_string());

View File

@ -36,7 +36,7 @@ use lib::llvm::{ModuleRef, ValueRef, BasicBlockRef};
use lib::llvm::{llvm, Vector};
use lib;
use metadata::{csearch, encoder, loader};
use middle::lint;
use lint;
use middle::astencode;
use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
use middle::weak_lang_items;
@ -1552,49 +1552,52 @@ fn trans_enum_def(ccx: &CrateContext, enum_definition: &ast::EnumDef,
fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) {
let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully
let (lvl, src) = ccx.tcx.node_lint_levels.borrow()
.find(&(id, lint::VariantSizeDifference))
.map_or((lint::Allow, lint::Default), |&(lvl,src)| (lvl, src));
let levels = ccx.tcx.node_lint_levels.borrow();
let lint_id = lint::LintId::of(lint::builtin::VARIANT_SIZE_DIFFERENCE);
let lvlsrc = match levels.find(&(id, lint_id)) {
None | Some(&(lint::Allow, _)) => return,
Some(&lvlsrc) => lvlsrc,
};
if lvl != lint::Allow {
let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id));
match *avar {
adt::General(_, ref variants) => {
for var in variants.iter() {
let mut size = 0;
for field in var.fields.iter().skip(1) {
// skip the discriminant
size += llsize_of_real(ccx, sizing_type_of(ccx, *field));
}
sizes.push(size);
let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id));
match *avar {
adt::General(_, ref variants) => {
for var in variants.iter() {
let mut size = 0;
for field in var.fields.iter().skip(1) {
// skip the discriminant
size += llsize_of_real(ccx, sizing_type_of(ccx, *field));
}
},
_ => { /* its size is either constant or unimportant */ }
}
sizes.push(size);
}
},
_ => { /* its size is either constant or unimportant */ }
}
let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0),
|(l, s, li), (idx, &size)|
if size > l {
(size, l, idx)
} else if size > s {
(l, size, li)
} else {
(l, s, li)
}
);
let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0),
|(l, s, li), (idx, &size)|
if size > l {
(size, l, idx)
} else if size > s {
(l, size, li)
} else {
(l, s, li)
}
);
// we only warn if the largest variant is at least thrice as large as
// the second-largest.
if largest > slargest * 3 && slargest > 0 {
lint::emit_lint(lvl, src,
// we only warn if the largest variant is at least thrice as large as
// the second-largest.
if largest > slargest * 3 && slargest > 0 {
// Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing
// pass for the latter already ran.
lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::VARIANT_SIZE_DIFFERENCE,
lvlsrc, Some(sp),
format!("enum variant is more than three times larger \
({} bytes) than the next largest (ignoring padding)",
largest).as_slice(),
sp, lint::lint_to_str(lint::VariantSizeDifference), ccx.tcx());
({} bytes) than the next largest (ignoring padding)",
largest).as_slice());
ccx.sess().span_note(enum_def.variants.get(largest_index).span,
"this variant is the largest");
}
ccx.sess().span_note(enum_def.variants.get(largest_index).span,
"this variant is the largest");
}
}

View File

@ -14,7 +14,7 @@ use back::svh::Svh;
use driver::session::Session;
use metadata::csearch;
use mc = middle::mem_categorization;
use middle::lint;
use lint;
use middle::const_eval;
use middle::def;
use middle::dependency_format;
@ -367,8 +367,8 @@ pub struct ctxt {
pub dependency_formats: RefCell<dependency_format::Dependencies>,
pub node_lint_levels: RefCell<HashMap<(ast::NodeId, lint::Lint),
(lint::Level, lint::LintSource)>>,
pub node_lint_levels: RefCell<HashMap<(ast::NodeId, lint::LintId),
lint::LevelSource>>,
/// The types that must be asserted to be the same size for `transmute`
/// to be valid. We gather up these restrictions in the intrinsicck pass

View File

@ -79,7 +79,6 @@ type parameter).
use middle::const_eval;
use middle::def;
use middle::lint::UnreachableCode;
use middle::pat_util::pat_id_map;
use middle::pat_util;
use middle::subst;
@ -111,6 +110,7 @@ use middle::typeck::{require_same_types, vtable_map};
use middle::typeck::{MethodCall, MethodMap};
use middle::typeck::{TypeAndSubsts};
use middle::lang_items::TypeIdLangItem;
use lint;
use util::common::{block_query, indenter, loop_query};
use util::ppaux;
use util::ppaux::{UserString, Repr};
@ -3416,7 +3416,7 @@ pub fn check_block_with_expected(fcx: &FnCtxt,
fcx.ccx
.tcx
.sess
.add_lint(UnreachableCode,
.add_lint(lint::builtin::UNREACHABLE_CODE,
s_id,
s.span,
"unreachable statement".to_string());
@ -3443,7 +3443,7 @@ pub fn check_block_with_expected(fcx: &FnCtxt,
fcx.ccx
.tcx
.sess
.add_lint(UnreachableCode,
.add_lint(lint::builtin::UNREACHABLE_CODE,
e.id,
e.span,
"unreachable expression".to_string());

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

@ -10,6 +10,8 @@
//! Used by plugin crates to tell `rustc` about the plugins they provide.
use lint::LintPassObject;
use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT};
use syntax::ext::base::{IdentTT, ItemDecorator, ItemModifier, BasicMacroExpander};
use syntax::ext::base::{MacroExpanderFn};
@ -31,6 +33,9 @@ pub struct Registry {
#[doc(hidden)]
pub syntax_exts: Vec<NamedSyntaxExtension>,
#[doc(hidden)]
pub lint_passes: Vec<LintPassObject>,
}
impl Registry {
@ -39,6 +44,7 @@ impl Registry {
Registry {
krate_span: krate.span,
syntax_exts: vec!(),
lint_passes: vec!(),
}
}
@ -67,4 +73,9 @@ impl Registry {
span: None,
}, None));
}
/// Register a compiler lint pass.
pub fn register_lint_pass(&mut self, lint_pass: LintPassObject) {
self.lint_passes.push(lint_pass);
}
}

View File

@ -11,7 +11,7 @@
use rustc;
use rustc::{driver, middle};
use rustc::middle::privacy;
use rustc::middle::lint;
use rustc::lint;
use syntax::ast;
use syntax::parse::token;
@ -75,11 +75,13 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
let input = FileInput(cpath.clone());
let warning_lint = lint::builtin::WARNINGS.name_lower();
let sessopts = driver::config::Options {
maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()),
addl_lib_search_paths: RefCell::new(libs),
crate_types: vec!(driver::config::CrateTypeRlib),
lint_opts: vec!((lint::Warnings, lint::Allow)),
lint_opts: vec!((warning_lint, lint::Allow)),
..rustc::driver::config::basic_options().clone()
};
@ -100,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,

View File

@ -0,0 +1,47 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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'")
struct Pass;
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(TEST_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'");
}
}
}
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_lint_pass(box Pass as LintPassObject);
}

View File

@ -0,0 +1,24 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:lint_plugin_test.rs
// ignore-stage1
#![feature(phase)]
#![deny(test_lint)]
#[phase(plugin)]
extern crate lint_plugin_test;
fn lintme() { } //~ ERROR item is named 'lintme'
pub fn main() {
lintme();
}

View File

@ -0,0 +1,24 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:lint_plugin_test.rs
// ignore-stage1
// compile-flags: -D test-lint
#![feature(phase)]
#[phase(plugin)]
extern crate lint_plugin_test;
fn lintme() { } //~ ERROR item is named 'lintme'
pub fn main() {
lintme();
}

View File

@ -0,0 +1,25 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:lint_plugin_test.rs
// ignore-stage1
#![feature(phase)]
#![forbid(test_lint)]
#[phase(plugin)]
extern crate lint_plugin_test;
fn lintme() { } //~ ERROR item is named 'lintme'
#[allow(test_lint)] //~ ERROR allow(test_lint) overruled by outer forbid(test_lint)
pub fn main() {
lintme();
}

View File

@ -0,0 +1,25 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:lint_plugin_test.rs
// ignore-stage1
// compile-flags: -F test-lint
#![feature(phase)]
#[phase(plugin)]
extern crate lint_plugin_test;
fn lintme() { } //~ ERROR item is named 'lintme'
#[allow(test_lint)] //~ ERROR allow(test_lint) overruled by outer forbid(test_lint)
pub fn main() {
lintme();
}

View File

@ -0,0 +1,15 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![forbid(experimental)]
#[allow(experimental)] //~ ERROR allow(experimental) overruled by outer forbid(experimental)
fn main() {
}

View File

@ -0,0 +1,15 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -F experimental
#[allow(experimental)] //~ ERROR allow(experimental) overruled by outer forbid(experimental)
fn main() {
}

View File

@ -0,0 +1,23 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:lint_plugin_test.rs
// ignore-stage1
// compile-flags: -A test-lint
#![feature(phase)]
#[phase(plugin)]
extern crate lint_plugin_test;
fn lintme() { }
pub fn main() {
}

View File

@ -0,0 +1,25 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:lint_plugin_test.rs
// ignore-stage1
// ignore-pretty
#![feature(phase)]
#[phase(plugin)]
extern crate lint_plugin_test;
fn lintme() { } //~ WARNING item is named 'lintme'
#[allow(test_lint)]
pub fn main() {
fn lintme() { }
}