Skip to content

Commit 05ca9f7

Browse files
committed
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.
2 parents 87f3741 + 7e694e7 commit 05ca9f7

30 files changed

+2892
-2138
lines changed

src/librustc/driver/config.rs

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use back;
1919
use back::link;
2020
use back::target_strs;
2121
use back::{arm, x86, x86_64, mips, mipsel};
22-
use middle::lint;
22+
use lint;
2323

2424
use syntax::abi;
2525
use syntax::ast;
@@ -70,7 +70,8 @@ pub struct Options {
7070
pub gc: bool,
7171
pub optimize: OptLevel,
7272
pub debuginfo: DebugInfoLevel,
73-
pub lint_opts: Vec<(lint::Lint, lint::Level)> ,
73+
pub lint_opts: Vec<(String, lint::Level)>,
74+
pub describe_lints: bool,
7475
pub output_types: Vec<back::link::OutputType> ,
7576
// This was mutable for rustpkg, which updates search paths based on the
7677
// parsed code. It remains mutable in case its replacements wants to use
@@ -104,6 +105,7 @@ pub fn basic_options() -> Options {
104105
optimize: No,
105106
debuginfo: NoDebugInfo,
106107
lint_opts: Vec::new(),
108+
describe_lints: false,
107109
output_types: Vec::new(),
108110
addl_lib_search_paths: RefCell::new(HashSet::new()),
109111
maybe_sysroot: None,
@@ -585,30 +587,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
585587
let no_trans = matches.opt_present("no-trans");
586588
let no_analysis = matches.opt_present("no-analysis");
587589

588-
let lint_levels = [lint::Allow, lint::Warn,
589-
lint::Deny, lint::Forbid];
590-
let mut lint_opts = Vec::new();
591-
let lint_dict = lint::get_lint_dict();
592-
for level in lint_levels.iter() {
593-
let level_name = lint::level_to_str(*level);
594-
595-
let level_short = level_name.slice_chars(0, 1);
596-
let level_short = level_short.to_ascii().to_upper().into_str();
597-
let flags = matches.opt_strs(level_short.as_slice())
598-
.move_iter()
599-
.collect::<Vec<_>>()
600-
.append(matches.opt_strs(level_name).as_slice());
601-
for lint_name in flags.iter() {
602-
let lint_name = lint_name.replace("-", "_").into_string();
603-
match lint_dict.find_equiv(&lint_name) {
604-
None => {
605-
early_error(format!("unknown {} flag: {}",
606-
level_name,
607-
lint_name).as_slice());
608-
}
609-
Some(lint) => {
610-
lint_opts.push((lint.lint, *level));
611-
}
590+
let mut lint_opts = vec!();
591+
let mut describe_lints = false;
592+
593+
for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() {
594+
for lint_name in matches.opt_strs(level.as_str()).move_iter() {
595+
if lint_name.as_slice() == "help" {
596+
describe_lints = true;
597+
} else {
598+
lint_opts.push((lint_name.replace("-", "_").into_string(), level));
612599
}
613600
}
614601
}
@@ -752,6 +739,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
752739
optimize: opt_level,
753740
debuginfo: debuginfo,
754741
lint_opts: lint_opts,
742+
describe_lints: describe_lints,
755743
output_types: output_types,
756744
addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
757745
maybe_sysroot: sysroot_opt,

src/librustc/driver/driver.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ use metadata::common::LinkMeta;
2020
use metadata::creader;
2121
use middle::cfg;
2222
use middle::cfg::graphviz::LabelledCFG;
23-
use middle::{trans, freevars, stability, kind, ty, typeck, lint, reachable};
23+
use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
2424
use middle::dependency_format;
2525
use middle;
2626
use plugin::load::Plugins;
2727
use plugin::registry::Registry;
2828
use plugin;
29+
use lint;
2930
use util::common::time;
3031
use util::ppaux;
3132
use util::nodemap::{NodeSet};
@@ -78,8 +79,12 @@ pub fn compile_input(sess: Session,
7879
&sess);
7980
let id = link::find_crate_id(krate.attrs.as_slice(),
8081
outputs.out_filestem.as_slice());
81-
let (expanded_crate, ast_map) =
82-
phase_2_configure_and_expand(&sess, krate, &id);
82+
let (expanded_crate, ast_map)
83+
= match phase_2_configure_and_expand(&sess, krate, &id) {
84+
None => return,
85+
Some(p) => p,
86+
};
87+
8388
(outputs, expanded_crate, ast_map)
8489
};
8590
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)
172177
/// syntax expansion, secondary `cfg` expansion, synthesis of a test
173178
/// harness if one is to be provided and injection of a dependency on the
174179
/// standard library and prelude.
180+
///
181+
/// Returns `None` if we're aborting after handling -W help.
175182
pub fn phase_2_configure_and_expand(sess: &Session,
176183
mut krate: ast::Crate,
177184
crate_id: &CrateId)
178-
-> (ast::Crate, syntax::ast_map::Map) {
185+
-> Option<(ast::Crate, syntax::ast_map::Map)> {
179186
let time_passes = sess.time_passes();
180187

181188
*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,
209216
}
210217
});
211218

212-
let Registry { syntax_exts, .. } = registry;
219+
let Registry { syntax_exts, lint_passes, .. } = registry;
220+
221+
{
222+
let mut ls = sess.lint_store.borrow_mut();
223+
for pass in lint_passes.move_iter() {
224+
ls.register_pass(Some(sess), true, pass);
225+
}
226+
}
227+
228+
// Lint plugins are registered; now we can process command line flags.
229+
if sess.opts.describe_lints {
230+
super::describe_lints(&*sess.lint_store.borrow(), true);
231+
return None;
232+
}
233+
sess.lint_store.borrow_mut().process_command_line(sess);
234+
235+
// Abort if there are errors from lint processing or a plugin registrar.
236+
sess.abort_if_errors();
213237

214238
krate = time(time_passes, "expansion", (krate, macros, syntax_exts),
215239
|(krate, macros, syntax_exts)| {
@@ -253,7 +277,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
253277
krate.encode(&mut json).unwrap();
254278
}
255279

256-
(krate, map)
280+
Some((krate, map))
257281
}
258282

259283
pub struct CrateAnalysis {
@@ -366,7 +390,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
366390
});
367391

368392
time(time_passes, "lint checking", (), |_|
369-
lint::check_crate(&ty_cx, &exported_items, krate));
393+
lint::check_crate(&ty_cx, krate, &exported_items));
370394

371395
CrateAnalysis {
372396
exp_map2: exp_map2,
@@ -630,9 +654,11 @@ pub fn pretty_print_input(sess: Session,
630654

631655
let (krate, ast_map, is_expanded) = match ppm {
632656
PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => {
633-
let (krate, ast_map) = phase_2_configure_and_expand(&sess,
634-
krate,
635-
&id);
657+
let (krate, ast_map)
658+
= match phase_2_configure_and_expand(&sess, krate, &id) {
659+
None => return,
660+
Some(p) => p,
661+
};
636662
(krate, Some(ast_map), true)
637663
}
638664
_ => (krate, None, false)
@@ -766,15 +792,15 @@ pub fn collect_crate_types(session: &Session,
766792
}
767793
Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
768794
Some(_) => {
769-
session.add_lint(lint::UnknownCrateType,
795+
session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPE,
770796
ast::CRATE_NODE_ID,
771797
a.span,
772798
"invalid `crate_type` \
773799
value".to_string());
774800
None
775801
}
776802
_ => {
777-
session.add_lint(lint::UnknownCrateType,
803+
session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPE,
778804
ast::CRATE_NODE_ID,
779805
a.span,
780806
"`crate_type` requires a \

src/librustc/driver/mod.rs

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ pub use syntax::diagnostic;
1313
use back::link;
1414
use driver::driver::{Input, FileInput, StrInput};
1515
use driver::session::{Session, build_session};
16-
use middle::lint;
16+
use lint::Lint;
17+
use lint;
1718
use metadata;
1819

1920
use std::any::AnyRefExt;
20-
use std::cmp;
2121
use std::io;
2222
use std::os;
2323
use std::str;
@@ -49,9 +49,18 @@ fn run_compiler(args: &[String]) {
4949
Some(matches) => matches,
5050
None => return
5151
};
52+
let sopts = config::build_session_options(&matches);
5253

5354
let (input, input_file_path) = match matches.free.len() {
54-
0u => early_error("no input filename given"),
55+
0u => {
56+
if sopts.describe_lints {
57+
let mut ls = lint::LintStore::new();
58+
ls.register_builtin(None);
59+
describe_lints(&ls, false);
60+
return;
61+
}
62+
early_error("no input filename given");
63+
}
5564
1u => {
5665
let ifile = matches.free.get(0).as_slice();
5766
if ifile == "-" {
@@ -66,7 +75,6 @@ fn run_compiler(args: &[String]) {
6675
_ => early_error("multiple input filenames provided")
6776
};
6877

69-
let sopts = config::build_session_options(&matches);
7078
let sess = build_session(sopts, input_file_path);
7179
let cfg = config::build_configuration(&sess);
7280
let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
@@ -124,41 +132,68 @@ Additional help:
124132
config::optgroups().as_slice()));
125133
}
126134

127-
fn describe_warnings() {
135+
fn describe_lints(lint_store: &lint::LintStore, loaded_plugins: bool) {
128136
println!("
129137
Available lint options:
130138
-W <foo> Warn about <foo>
131139
-A <foo> Allow <foo>
132140
-D <foo> Deny <foo>
133141
-F <foo> Forbid <foo> (deny, and deny all overrides)
134-
");
135142
136-
let lint_dict = lint::get_lint_dict();
137-
let mut lint_dict = lint_dict.move_iter()
138-
.map(|(k, v)| (v, k))
139-
.collect::<Vec<(lint::LintSpec, &'static str)> >();
140-
lint_dict.as_mut_slice().sort();
143+
");
141144

142-
let mut max_key = 0;
143-
for &(_, name) in lint_dict.iter() {
144-
max_key = cmp::max(name.len(), max_key);
145-
}
146-
fn padded(max: uint, s: &str) -> String {
147-
format!("{}{}", " ".repeat(max - s.len()), s)
145+
fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> {
146+
let mut lints: Vec<_> = lints.move_iter().map(|(x, _)| x).collect();
147+
lints.sort_by(|x: &&Lint, y: &&Lint| {
148+
match x.default_level.cmp(&y.default_level) {
149+
// The sort doesn't case-fold but it's doubtful we care.
150+
Equal => x.name.cmp(&y.name),
151+
r => r,
152+
}
153+
});
154+
lints
148155
}
149-
println!("\nAvailable lint checks:\n");
150-
println!(" {} {:7.7s} {}",
151-
padded(max_key, "name"), "default", "meaning");
152-
println!(" {} {:7.7s} {}\n",
153-
padded(max_key, "----"), "-------", "-------");
154-
for (spec, name) in lint_dict.move_iter() {
155-
let name = name.replace("_", "-");
156-
println!(" {} {:7.7s} {}",
157-
padded(max_key, name.as_slice()),
158-
lint::level_to_str(spec.default),
159-
spec.desc);
156+
157+
let (plugin, builtin) = lint_store.get_lints().partitioned(|&(_, p)| p);
158+
let plugin = sort_lints(plugin);
159+
let builtin = sort_lints(builtin);
160+
161+
// FIXME (#7043): We should use the width in character cells rather than
162+
// the number of codepoints.
163+
let max_name_len = plugin.iter().chain(builtin.iter())
164+
.map(|&s| s.name.char_len())
165+
.max().unwrap_or(0);
166+
let padded = |x: &str| {
167+
" ".repeat(max_name_len - x.char_len()).append(x)
168+
};
169+
170+
println!("Lint checks provided by rustc:\n");
171+
println!(" {} {:7.7s} {}", padded("name"), "default", "meaning");
172+
println!(" {} {:7.7s} {}", padded("----"), "-------", "-------");
173+
174+
let print_lints = |lints: Vec<&Lint>| {
175+
for lint in lints.move_iter() {
176+
let name = lint.name_lower().replace("_", "-");
177+
println!(" {} {:7.7s} {}",
178+
padded(name.as_slice()), lint.default_level.as_str(), lint.desc);
179+
}
180+
println!("\n");
181+
};
182+
183+
print_lints(builtin);
184+
185+
match (loaded_plugins, plugin.len()) {
186+
(false, 0) => {
187+
println!("Compiler plugins can provide additional lints. To see a listing of these, \
188+
re-run `rustc -W help` with a crate filename.");
189+
}
190+
(false, _) => fail!("didn't load lint plugins but got them anyway!"),
191+
(true, 0) => println!("This crate does not load any lint plugins."),
192+
(true, _) => {
193+
println!("Lint checks provided by plugins loaded by this crate:\n");
194+
print_lints(plugin);
195+
}
160196
}
161-
println!("");
162197
}
163198

164199
fn describe_debug_flags() {
@@ -214,12 +249,7 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
214249
return None;
215250
}
216251

217-
let lint_flags = matches.opt_strs("W").move_iter().collect::<Vec<_>>().append(
218-
matches.opt_strs("warn").as_slice());
219-
if lint_flags.iter().any(|x| x.as_slice() == "help") {
220-
describe_warnings();
221-
return None;
222-
}
252+
// Don't handle -W help here, because we might first load plugins.
223253

224254
let r = matches.opt_strs("Z");
225255
if r.iter().any(|x| x.as_slice() == "help") {

0 commit comments

Comments
 (0)