@@ -32,20 +32,29 @@ use syntax::{ast, visit, ast_util};
32
32
* added via the `add_lint` method on the Session structure. This requires a
33
33
* span and an id of the node that the lint is being added to. The lint isn't
34
34
* 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.
36
36
*
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.
42
43
*
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.
49
58
*/
50
59
51
60
#[ deriving( Eq ) ]
@@ -100,6 +109,13 @@ enum AttributedNode<'self> {
100
109
Crate ( @ast:: crate ) ,
101
110
}
102
111
112
+ #[ deriving( Eq ) ]
113
+ enum LintSource {
114
+ Node ( span ) ,
115
+ Default ,
116
+ CommandLine
117
+ }
118
+
103
119
static lint_table: & ' static [ ( & ' static str , LintSpec ) ] = & [
104
120
( "ctypes" ,
105
121
LintSpec {
@@ -240,29 +256,17 @@ pub fn get_lint_dict() -> LintDict {
240
256
return map;
241
257
}
242
258
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
-
255
259
struct Context {
256
260
// All known lint modes (string versions)
257
261
dict : @LintDict ,
258
262
// Current levels of each lint warning
259
- curr : LintModes ,
263
+ curr : SmallIntMap < ( level , LintSource ) > ,
260
264
// context we're checking in (used to access fields like sess)
261
265
tcx : ty:: ctxt ,
262
266
// When recursing into an attributed node of the ast which modifies lint
263
267
// levels, this stack keeps track of the previous lint levels of whatever
264
268
// was modified.
265
- lint_stack : ~[ ( lint , level ) ] ,
269
+ lint_stack : ~[ ( lint , level , LintSource ) ] ,
266
270
// Each of these visitors represents a lint pass. A number of the lint
267
271
// attributes are registered by adding a visitor to iterate over the ast.
268
272
// Others operate directly on @ast::item structures (or similar). Finally,
@@ -274,21 +278,65 @@ struct Context {
274
278
impl Context {
275
279
fn get_level ( & self , lint : lint ) -> level {
276
280
match self . curr . find ( & ( lint as uint ) ) {
277
- Some ( & c ) => c ,
281
+ Some ( & ( lvl , _ ) ) => lvl ,
278
282
None => allow
279
283
}
280
284
}
281
285
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 ) {
283
294
if level == allow {
284
295
self . curr . remove ( & ( lint as uint ) ) ;
285
296
} else {
286
- self . curr . insert ( lint as uint , level) ;
297
+ self . curr . insert ( lint as uint , ( level, src ) ) ;
287
298
}
288
299
}
289
300
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
+
290
310
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
+ }
292
340
}
293
341
294
342
/**
@@ -325,18 +373,19 @@ impl Context {
325
373
}
326
374
327
375
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) ) ;
329
378
pushed += 1 ;
330
- self . set_level ( lint, level) ;
379
+ self . set_level ( lint, level, Node ( meta . span ) ) ;
331
380
}
332
381
}
333
382
334
383
f ( ) ;
335
384
336
385
// rollback
337
386
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 ) ;
340
389
}
341
390
}
342
391
@@ -874,12 +923,12 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
874
923
875
924
// Install defaults.
876
925
for cx. dict. each_value |spec| {
877
- cx. set_level ( spec. lint , spec. default ) ;
926
+ cx. set_level ( spec. lint , spec. default , Default ) ;
878
927
}
879
928
880
929
// Install command-line options, overriding defaults.
881
930
for tcx. sess. opts. lint_opts. each |& ( lint, level) | {
882
- cx. set_level ( lint, level) ;
931
+ cx. set_level ( lint, level, CommandLine ) ;
883
932
}
884
933
885
934
// Register each of the lint passes with the context
0 commit comments