Skip to content

Commit

Permalink
auto merge of #15024 : kmcallister/rust/lint, r=alexcrichton
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed Jun 24, 2014
2 parents 87f3741 + 7e694e7 commit 05ca9f7
Show file tree
Hide file tree
Showing 30 changed files with 2,892 additions and 2,138 deletions.
40 changes: 14 additions & 26 deletions src/librustc/driver/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 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));
}
let mut lint_opts = vec!();
let mut describe_lints = false;

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));
}
}
}
Expand Down Expand Up @@ -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,
Expand Down
50 changes: 38 additions & 12 deletions src/librustc/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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)| {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -766,15 +792,15 @@ 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` \
value".to_string());
None
}
_ => {
session.add_lint(lint::UnknownCrateType,
session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPE,
ast::CRATE_NODE_ID,
a.span,
"`crate_type` requires a \
Expand Down
100 changes: 65 additions & 35 deletions src/librustc/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 == "-" {
Expand All @@ -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));
Expand Down Expand Up @@ -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();
");

let mut max_key = 0;
for &(_, name) in lint_dict.iter() {
max_key = cmp::max(name.len(), max_key);
}
fn padded(max: uint, s: &str) -> String {
format!("{}{}", " ".repeat(max - s.len()), s)
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
}
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);

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);
}
}
println!("");
}

fn describe_debug_flags() {
Expand Down Expand Up @@ -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") {
Expand Down
Loading

0 comments on commit 05ca9f7

Please sign in to comment.