Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement lint plugins #15024

Merged
merged 20 commits into from
Jun 24, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
75bfeda
Move lint.rs out of middle
kmcallister Jun 1, 2014
3144614
Move lint infrastructure and individual lints into separate files
kmcallister Jun 1, 2014
5d4c96a
Rename lint::Lint to lint::LintId
kmcallister Jun 1, 2014
69b6bc5
Convert lints to a trait-based infrastructure
kmcallister Jun 2, 2014
442fbc4
Replace enum LintId with an extensible alternative
kmcallister Jun 4, 2014
819f76c
Store the registered lints in the Session
kmcallister Jun 10, 2014
c7af606
Clean up and document the public lint API
kmcallister Jun 6, 2014
c1898b9
Stop using Default for initializing builtin lints
kmcallister Jun 13, 2014
609552e
Run lint passes using the Option dance instead of RefCells
kmcallister Jun 13, 2014
21e7b93
Use names in Lint structs in an ASCII-case-insensitive way
kmcallister Jun 13, 2014
b5542f7
Convert builtin lints to uppercase names for style consistency
kmcallister Jun 13, 2014
a813a37
Rework lint attr parsing and use it in middle::dead
kmcallister Jun 13, 2014
6fede93
Make the crate and its exported items available in the lint context
kmcallister Jun 17, 2014
ba1c0c4
Drop the ExportedItems argument from LintPass::check_crate
kmcallister Jun 18, 2014
c747626
Reindent function call continuations, and other style fixes
kmcallister Jun 18, 2014
e67e6e6
List builtin lints one per line for better diffs
kmcallister Jun 18, 2014
51d438e
Incorporate upstream changes to old lint code
kmcallister Jun 18, 2014
2f274d1
Implement lint plugins
kmcallister Jun 19, 2014
7dc724b
Test lint plugins
kmcallister Jun 18, 2014
7e694e7
More upstream lint changes
kmcallister Jun 24, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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