@@ -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+
103119static 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-
255259struct 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 {
274278impl 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
0 commit comments