Skip to content

Commit 4d44abd

Browse files
committed
Change lints to indicate exactly where the level was set
1 parent 1daaf78 commit 4d44abd

File tree

3 files changed

+86
-51
lines changed

3 files changed

+86
-51
lines changed

src/librustc/driver/session.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -223,15 +223,6 @@ pub impl Session_ {
223223
fn unimpl(@self, msg: &str) -> ! {
224224
self.span_diagnostic.handler().unimpl(msg)
225225
}
226-
fn span_lint_level(@self, level: lint::level, sp: span, msg: &str) {
227-
match level {
228-
lint::allow => { },
229-
lint::warn => self.span_warn(sp, msg),
230-
lint::deny | lint::forbid => {
231-
self.span_err(sp, msg);
232-
}
233-
}
234-
}
235226
fn add_lint(@self, lint: lint::lint, id: ast::node_id, sp: span, msg: ~str) {
236227
match self.lints.find_mut(&id) {
237228
Some(arr) => { arr.push((lint, sp, msg)); return; }

src/librustc/middle/lint.rs

Lines changed: 85 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,29 @@ use syntax::{ast, visit, ast_util};
3232
* added via the `add_lint` method on the Session structure. This requires a
3333
* span and an id of the node that the lint is being added to. The lint isn't
3434
* actually emitted at that time because it is unknown what the actual lint
35-
* level for the particular attribute is.
35+
* level at that location is.
3636
*
37-
* To actually emit lint warnings/errors, a context keeps track of the current
38-
* state of all lint levels. Upon entering a node of the ast which can modify
39-
* the lint settings, the previous lint state is pushed onto a stack and the ast
40-
* is then recursed upon. Once the lint state has been altered, all of the known
41-
* lint passes are run on the node of the ast.
37+
* To actually emit lint warnings/errors, a separate pass is used just before
38+
* translation. A context keeps track of the current state of all lint levels.
39+
* Upon entering a node of the ast which can modify the lint settings, the
40+
* previous lint state is pushed onto a stack and the ast is then recursed upon.
41+
* As the ast is traversed, this keeps track of the current lint level for all
42+
* lint attributes.
4243
*
43-
* Each lint pass is a visit::vt<()> structure. These visitors are constructed
44-
* via the lint_*() functions below. There are also some lint checks which
45-
* operate directly on ast nodes (such as @ast::item), and those are organized
46-
* as check_item_*(). Each visitor added to the lint context is modified to stop
47-
* once it reaches a node which could alter the lint levels. This means that
48-
* everything is looked at once and only once by every lint pass.
44+
* At each node of the ast which can modify lint attributes, all known lint
45+
* passes are also applied. Each lint pass is a visit::vt<()> structure. These
46+
* visitors are constructed via the lint_*() functions below. There are also
47+
* some lint checks which operate directly on ast nodes (such as @ast::item),
48+
* and those are organized as check_item_*(). Each visitor added to the lint
49+
* context is modified to stop once it reaches a node which could alter the lint
50+
* levels. This means that everything is looked at once and only once by every
51+
* lint pass.
52+
*
53+
* With this all in place, to add a new lint warning, all you need to do is to
54+
* either invoke `add_lint` on the session at the appropriate time, or write a
55+
* lint pass in this module which is just an ast visitor. The context used when
56+
* traversing the ast has a `span_lint` method which only needs the span of the
57+
* item that's being warned about.
4958
*/
5059

5160
#[deriving(Eq)]
@@ -100,6 +109,13 @@ enum AttributedNode<'self> {
100109
Crate(@ast::crate),
101110
}
102111

112+
#[deriving(Eq)]
113+
enum LintSource {
114+
Node(span),
115+
Default,
116+
CommandLine
117+
}
118+
103119
static lint_table: &'static [(&'static str, LintSpec)] = &[
104120
("ctypes",
105121
LintSpec {
@@ -240,29 +256,17 @@ pub fn get_lint_dict() -> LintDict {
240256
return map;
241257
}
242258

243-
pub fn get_lint_name(lint_mode: lint) -> ~str {
244-
for lint_table.each |&(name, spec)| {
245-
if spec.lint == lint_mode {
246-
return name.to_str();
247-
}
248-
}
249-
fail!();
250-
}
251-
// This is a highly not-optimal set of data structure decisions.
252-
type LintModes = SmallIntMap<level>;
253-
type LintModeMap = HashMap<ast::node_id, LintModes>;
254-
255259
struct Context {
256260
// All known lint modes (string versions)
257261
dict: @LintDict,
258262
// Current levels of each lint warning
259-
curr: LintModes,
263+
curr: SmallIntMap<(level, LintSource)>,
260264
// context we're checking in (used to access fields like sess)
261265
tcx: ty::ctxt,
262266
// When recursing into an attributed node of the ast which modifies lint
263267
// levels, this stack keeps track of the previous lint levels of whatever
264268
// was modified.
265-
lint_stack: ~[(lint, level)],
269+
lint_stack: ~[(lint, level, LintSource)],
266270
// Each of these visitors represents a lint pass. A number of the lint
267271
// attributes are registered by adding a visitor to iterate over the ast.
268272
// Others operate directly on @ast::item structures (or similar). Finally,
@@ -274,21 +278,65 @@ struct Context {
274278
impl Context {
275279
fn get_level(&self, lint: lint) -> level {
276280
match self.curr.find(&(lint as uint)) {
277-
Some(&c) => c,
281+
Some(&(lvl, _)) => lvl,
278282
None => allow
279283
}
280284
}
281285

282-
fn set_level(&mut self, lint: lint, level: level) {
286+
fn get_source(&self, lint: lint) -> LintSource {
287+
match self.curr.find(&(lint as uint)) {
288+
Some(&(_, src)) => src,
289+
None => Default
290+
}
291+
}
292+
293+
fn set_level(&mut self, lint: lint, level: level, src: LintSource) {
283294
if level == allow {
284295
self.curr.remove(&(lint as uint));
285296
} else {
286-
self.curr.insert(lint as uint, level);
297+
self.curr.insert(lint as uint, (level, src));
287298
}
288299
}
289300

301+
fn lint_to_str(&self, lint: lint) -> ~str {
302+
for self.dict.each |k, v| {
303+
if v.lint == lint {
304+
return copy *k;
305+
}
306+
}
307+
fail!("unregistered lint %?", lint);
308+
}
309+
290310
fn span_lint(&self, lint: lint, span: span, msg: &str) {
291-
self.tcx.sess.span_lint_level(self.get_level(lint), span, msg);
311+
let (level, src) = match self.curr.find(&(lint as uint)) {
312+
Some(&pair) => pair,
313+
None => { return; }
314+
};
315+
if level == allow { return; }
316+
317+
let mut note = None;
318+
let msg = match src {
319+
Default | CommandLine => {
320+
fmt!("%s [-%c %s%s]", msg, match level {
321+
warn => 'W', deny => 'D', forbid => 'F',
322+
allow => fail!()
323+
}, str::replace(self.lint_to_str(lint), "_", "-"),
324+
if src == Default { " (default)" } else { "" })
325+
},
326+
Node(src) => {
327+
note = Some(src);
328+
msg.to_str()
329+
}
330+
};
331+
match level {
332+
warn => { self.tcx.sess.span_warn(span, msg); }
333+
deny | forbid => { self.tcx.sess.span_err(span, msg); }
334+
allow => fail!(),
335+
}
336+
337+
for note.each |&span| {
338+
self.tcx.sess.span_note(span, "lint level defined here");
339+
}
292340
}
293341

294342
/**
@@ -325,18 +373,19 @@ impl Context {
325373
}
326374

327375
if now != level {
328-
self.lint_stack.push((lint, now));
376+
let src = self.get_source(lint);
377+
self.lint_stack.push((lint, now, src));
329378
pushed += 1;
330-
self.set_level(lint, level);
379+
self.set_level(lint, level, Node(meta.span));
331380
}
332381
}
333382

334383
f();
335384

336385
// rollback
337386
for pushed.times {
338-
let (lint, level) = self.lint_stack.pop();
339-
self.set_level(lint, level);
387+
let (lint, lvl, src) = self.lint_stack.pop();
388+
self.set_level(lint, lvl, src);
340389
}
341390
}
342391

@@ -874,12 +923,12 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
874923

875924
// Install defaults.
876925
for cx.dict.each_value |spec| {
877-
cx.set_level(spec.lint, spec.default);
926+
cx.set_level(spec.lint, spec.default, Default);
878927
}
879928

880929
// Install command-line options, overriding defaults.
881930
for tcx.sess.opts.lint_opts.each |&(lint, level)| {
882-
cx.set_level(lint, level);
931+
cx.set_level(lint, level, CommandLine);
883932
}
884933

885934
// Register each of the lint passes with the context

src/librustc/middle/resolve_stage0.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5229,12 +5229,7 @@ pub impl Resolver {
52295229
!import_resolution.state.warned &&
52305230
import_resolution.span != dummy_sp() &&
52315231
import_resolution.privacy != Public {
5232-
import_resolution.state.warned = true;
5233-
let span = import_resolution.span;
5234-
self.session.span_lint_level(
5235-
self.unused_import_lint_level(module_),
5236-
span,
5237-
~"unused import");
5232+
// I swear I work in not(stage0)!
52385233
}
52395234
}
52405235
}

0 commit comments

Comments
 (0)