1
1
import driver:: session:: session;
2
- import middle:: ty:: ctxt ;
2
+ import middle:: ty;
3
3
import syntax :: { ast, visit} ;
4
4
import syntax:: attr;
5
- import std:: map:: hashmap;
5
+ import syntax:: codemap:: span;
6
+ import std:: map:: { map, hashmap, hash_from_strs} ;
6
7
import io:: writer_util;
7
8
8
- enum option {
9
+ export lint, ctypes, unused_imports;
10
+ export level, ignore, warn, error;
11
+ export lookup_lint, lint_dict, get_lint_dict, check_crate;
12
+
13
+ #[ doc="
14
+
15
+ A 'lint' check is a kind of miscallaneous constraint that a user _might_ want
16
+ to enforce, but might reasonably want to permit as well, on a module-by-module
17
+ basis. They contrast with static constraints enforced by other phases of the
18
+ compiler, which are generally required to hold in order to compile the program
19
+ correctly at all.
20
+
21
+ " ]
22
+
23
+ enum lint {
9
24
ctypes,
25
+ unused_imports,
10
26
}
11
27
12
- impl opt_ for option {
13
- fn desc ( ) -> str {
14
- "lint: " + alt self {
15
- ctypes { "ctypes usage checking" }
16
- }
17
- }
18
- fn run ( tcx : ty:: ctxt , crate : @ast:: crate , time_pass : bool ) {
19
- let checker = alt self {
20
- ctypes {
21
- bind check_ctypes( tcx, crate )
22
- }
23
- } ;
24
- time ( time_pass, self . desc ( ) , checker) ;
25
- }
28
+ enum level {
29
+ ignore, warn, error
26
30
}
27
31
28
- // FIXME: Copied from driver.rs, to work around a bug(#1566)
29
- fn time ( do_it : bool , what : str , thunk : fn ( ) ) {
30
- if !do_it{ ret thunk ( ) ; }
31
- let start = std:: time:: precise_time_s ( ) ;
32
- thunk ( ) ;
33
- let end = std:: time:: precise_time_s ( ) ;
34
- io:: stdout ( ) . write_str ( #fmt ( "time: %3.3f s\t %s\n " ,
35
- end - start, what) ) ;
32
+ type lint_spec = @{ lint : lint ,
33
+ desc : str ,
34
+ default : level } ;
35
+
36
+ type lint_dict = hashmap < str , lint_spec > ;
37
+
38
+ fn get_lint_dict ( ) -> lint_dict {
39
+ let v = [
40
+ ( "ctypes" ,
41
+ @{ lint: ctypes,
42
+ desc: "proper use of core::libc types in native modules" ,
43
+ default : warn} ) ,
44
+
45
+ ( "unused-imports" ,
46
+ @{ lint: unused_imports,
47
+ desc: "imports that are never used" ,
48
+ default : ignore} )
49
+ ] ;
50
+ hash_from_strs ( v)
36
51
}
37
52
38
- // Merge lint options specified by crate attributes and rustc command
39
- // line. Precedence: cmdline > attribute > default
40
- fn merge_opts ( attrs : [ ast:: attribute ] , cmd_opts : [ ( option , bool ) ] ) ->
41
- [ ( option , bool ) ] {
42
- fn str_to_option ( name : str ) -> ( option , bool ) {
43
- ret alt check name {
44
- "ctypes" { ( ctypes, true ) }
45
- "no_ctypes" { ( ctypes, false ) }
46
- }
47
- }
53
+ type ctxt = @{ dict : lint_dict ,
54
+ curr : hashmap < lint , level > ,
55
+ tcx : ty:: ctxt } ;
48
56
49
- fn meta_to_option ( meta : @ast:: meta_item ) -> ( option , bool ) {
50
- ret alt meta. node {
51
- ast:: meta_word ( name) {
52
- str_to_option ( name)
53
- }
54
- _ { fail "meta_to_option: meta_list contains a non-meta-word" ; }
55
- } ;
57
+ impl methods for ctxt {
58
+ fn get_level ( lint : lint ) -> level {
59
+ alt self . curr . find ( lint) {
60
+ some ( c) { c }
61
+ none { ignore }
62
+ }
56
63
}
57
64
58
- fn default ( ) -> [ ( option , bool ) ] {
59
- [ ( ctypes, true ) ]
65
+ fn set_level ( lint : lint , level : level ) {
66
+ if level == ignore {
67
+ self . curr . remove ( lint) ;
68
+ } else {
69
+ self . curr . insert ( lint, level) ;
70
+ }
60
71
}
61
72
62
- fn contains ( xs : [ ( option , bool ) ] , x : option ) -> bool {
63
- for xs. each { |c|
64
- let ( o, _) = c;
65
- if o == x { ret true; }
73
+ fn span_lint ( level : level , span : span , msg : str ) {
74
+ alt level {
75
+ ignore { }
76
+ warn { self. tcx . sess . span_warn ( span, msg) ; }
77
+ error { self . tcx. sess. span_err( span, msg) ; }
66
78
}
67
- ret false;
68
79
}
69
80
70
- let mut result = cmd_opts;
81
+ #[ doc="
82
+ Merge the warnings specified by any `warn(...)` attributes into the
83
+ current lint context, call the provided function, then reset the
84
+ warnings in effect to their previous state.
85
+ " ]
86
+ fn with_warn_attrs ( attrs : [ ast:: attribute ] , f : fn ( ctxt ) ) {
71
87
72
- let lint_metas =
73
- attr:: attr_metas( attr:: find_attrs_by_name( attrs, "lint" ) ) ;
88
+ let mut undo = [ ] ;
74
89
75
- vec:: iter( lint_metas) { |mi|
76
- alt mi. node {
77
- ast : : meta_list( _, list) {
78
- vec:: iter( list) { |e|
79
- let ( o, v) = meta_to_option( e) ;
80
- if ! contains( cmd_opts, o) {
81
- result += [ ( o, v) ] ;
90
+ let metas = attr:: attr_metas ( attr:: find_attrs_by_name ( attrs, "warn" ) ) ;
91
+ for metas. each { |meta|
92
+ alt meta. node {
93
+ ast : : meta_list( _, metas) {
94
+ for metas. each { |meta|
95
+ alt meta. node {
96
+ ast : : meta_word( lintname) {
97
+ alt lookup_lint( self . dict, lintname) {
98
+ none {
99
+ self. tcx. sess. span_err(
100
+ meta. span,
101
+ #fmt( "unknown warning: '%s'" , lintname) ) ;
102
+ }
103
+ some( ( lint, new_level) ) {
104
+ let old_level = self . get_level( lint) ;
105
+ self . set_level ( lint, new_level) ;
106
+ undo += [ ( lint, old_level) ]
107
+ }
108
+ }
109
+ }
110
+ _ {
111
+ self . tcx . sess . span_err (
112
+ meta. span ,
113
+ "malformed warning attribute" ) ;
114
+ }
115
+ }
82
116
}
117
+ }
118
+ _ {
119
+ self . tcx . sess . span_err ( meta. span ,
120
+ "malformed warning attribute" ) ;
121
+ }
83
122
}
84
- }
85
- _ { }
86
123
}
124
+
125
+ f ( self ) ;
126
+
127
+ for undo. each { |pair|
128
+ let ( lint, old_level) = pair;
129
+ self . set_level ( lint, old_level) ;
130
+ }
131
+ }
132
+ }
133
+
134
+
135
+ fn lookup_lint ( dict : lint_dict , s : str )
136
+ -> option < ( lint , level ) > {
137
+ let s = str:: replace ( s, "-" , "_" ) ;
138
+ let ( name, level) = if s. starts_with ( "no_" ) {
139
+ ( s. substr ( 3 u, s. len ( ) - 3 u) , ignore)
140
+ } else if s. starts_with ( "err_" ) {
141
+ ( s. substr ( 4 u, s. len ( ) - 4 u) , error)
142
+ } else {
143
+ ( s, warn)
87
144
} ;
145
+ alt dict. find ( name) {
146
+ none { none }
147
+ some( spec) { some ( ( spec. lint , level) ) }
148
+ }
149
+ }
150
+
151
+
152
+ // FIXME: Copied from driver.rs, to work around a bug(#1566)
153
+ fn time ( do_it : bool , what : str , thunk : fn ( ) ) {
154
+ if !do_it{ ret thunk ( ) ; }
155
+ let start = std:: time:: precise_time_s ( ) ;
156
+ thunk ( ) ;
157
+ let end = std:: time:: precise_time_s ( ) ;
158
+ io:: stdout ( ) . write_str ( #fmt ( "time: %3.3f s\t %s\n " ,
159
+ end - start, what) ) ;
160
+ }
88
161
89
- for default ( ) . each { |c|
90
- let ( o, v) = c;
91
- if !contains( result, o) {
92
- result += [ ( o, v) ] ;
162
+ fn check_item ( cx : ctxt , i : @ast:: item ) {
163
+ cx. with_warn_attrs ( i. attrs ) { |cx|
164
+ cx. curr . items { |lint, level|
165
+ alt lint {
166
+ ctypes { check_item_ctypes( cx, level, i) ; }
167
+ unused_imports { check_item_unused_imports( cx, level, i) ; }
168
+ }
93
169
}
94
170
}
171
+ }
95
172
96
- ret result;
173
+ fn check_item_unused_imports ( _cx : ctxt , _level : level , _it : @ast:: item ) {
174
+ // FIXME: Don't know how to check this in lint yet, it's currently being
175
+ // done over in resolve. When resolve is rewritten, do it here instead.
97
176
}
98
177
99
- fn check_ctypes( tcx: ty:: ctxt, crate : @ast:: crate) {
100
- fn check_native_fn( tcx: ty:: ctxt, decl: ast:: fn_decl) {
178
+ fn check_item_ctypes ( cx : ctxt , level : level , it : @ast:: item ) {
179
+
180
+ fn check_native_fn ( cx : ctxt , level : level , decl : ast:: fn_decl ) {
101
181
let tys = vec:: map ( decl. inputs ) { |a| a. ty } ;
102
182
for vec:: each( tys + [ decl. output] ) { |ty|
103
183
alt ty. node {
104
184
ast : : ty_path( _, id) {
105
- alt tcx. def_map. get( id) {
185
+ alt cx . tcx. def_map. get( id) {
106
186
ast:: def_prim_ty( ast:: ty_int( ast:: ty_i) ) {
107
- tcx . sess . span_warn (
108
- ty. span,
187
+ cx . span_lint (
188
+ level , ty. span,
109
189
"found rust type `int` in native module, while \
110
190
libc::c_int or libc::c_long should be used") ;
111
191
}
112
192
ast:: def_prim_ty( ast:: ty_uint( ast:: ty_u) ) {
113
- tcx . sess . span_warn (
114
- ty. span,
193
+ cx . span_lint (
194
+ level , ty. span,
115
195
"found rust type `uint` in native module, while \
116
196
libc::c_uint or libc::c_ulong should be used") ;
117
197
}
@@ -123,40 +203,55 @@ fn check_ctypes(tcx: ty::ctxt, crate: @ast::crate) {
123
203
}
124
204
}
125
205
126
- fn check_item( tcx: ty:: ctxt, it: @ast:: item) {
127
- alt it. node {
128
- ast:: item_native_mod( nmod) if attr:: native_abi( it. attrs) !=
129
- either:: right( ast:: native_abi_rust_intrinsic) {
130
- for nmod. items. each { |ni|
131
- alt ni. node {
132
- ast : : native_item_fn( decl, tps) {
133
- check_native_fn( tcx, decl) ;
134
- }
135
- _ { }
136
- }
206
+ alt it. node {
207
+ ast:: item_native_mod( nmod) if attr:: native_abi ( it. attrs ) !=
208
+ either:: right ( ast:: native_abi_rust_intrinsic) {
209
+ for nmod. items. each { |ni|
210
+ alt ni. node {
211
+ ast : : native_item_fn( decl, tps) {
212
+ check_native_fn( cx, level, decl) ;
213
+ }
214
+ _ { }
137
215
}
138
- }
139
- _ { /* nothing to do */ }
140
216
}
217
+ }
218
+ _ { /* nothing to do */ }
141
219
}
142
-
143
- let visit = visit:: mk_simple_visitor ( @{
144
- visit_item: bind check_item ( tcx, _)
145
- with * visit:: default_simple_visitor ( )
146
- } ) ;
147
- visit:: visit_crate ( * crate , ( ) , visit) ;
148
220
}
149
221
222
+
150
223
fn check_crate ( tcx : ty:: ctxt , crate : @ast:: crate ,
151
- opts : [ ( option , bool ) ] , time : bool ) {
152
- let lint_opts = lint:: merge_opts ( crate . node. attrs , opts) ;
153
- for lint_opts. each { |opt|
154
- let ( lopt, switch) = opt;
155
- if switch == true {
156
- lopt. run ( tcx, crate , time) ;
224
+ lint_opts : [ ( lint , level ) ] , time_pass : bool ) {
225
+
226
+ fn hash_lint ( & & lint: lint ) -> uint { lint as uint }
227
+ fn eq_lint ( & & a: lint , & & b: lint ) -> bool { a == b }
228
+
229
+ let cx = @{ dict: get_lint_dict ( ) ,
230
+ curr: hashmap ( hash_lint, eq_lint) ,
231
+ tcx: tcx} ;
232
+
233
+ // Install defaults.
234
+ cx. dict . items { |_k, spec| cx. set_level ( spec. lint , spec. default ) ; }
235
+
236
+ // Install command-line options, overriding defaults.
237
+ for lint_opts. each { |pair|
238
+ let ( lint, level) = pair;
239
+ cx. set_level ( lint, level) ;
240
+ }
241
+
242
+ time ( time_pass, "lint checking" ) { ||
243
+ cx. with_warn_attrs ( crate . node. attrs ) { |cx|
244
+ let visit = visit:: mk_simple_visitor ( @{
245
+ visit_item : fn @( i: @ast:: item) { check_item ( cx, i) ; }
246
+ with * visit:: default_simple_visitor ( )
247
+ } ) ;
248
+ visit:: visit_crate ( * crate , ( ) , visit) ;
157
249
}
158
250
}
251
+
252
+ tcx. sess . abort_if_errors ( ) ;
159
253
}
254
+
160
255
//
161
256
// Local Variables:
162
257
// mode: rust
0 commit comments