@@ -51,7 +51,7 @@ impl<'a> SuppressionsParser<'a> {
51
51
line_suppressions : parser. line_suppressions ,
52
52
range_suppressions : parser. range_suppressions ,
53
53
diagnostics : parser. diagnostics ,
54
- line_index : LineIndex :: new ( doc ) ,
54
+ line_index : parser . line_index ,
55
55
}
56
56
}
57
57
@@ -60,6 +60,11 @@ impl<'a> SuppressionsParser<'a> {
60
60
/// suppression, this will stop.
61
61
fn parse_file_suppressions ( & mut self ) {
62
62
while let Some ( ( _, preview) ) = self . lines . peek ( ) {
63
+ if preview. trim ( ) . is_empty ( ) {
64
+ self . lines . next ( ) ;
65
+ continue ;
66
+ }
67
+
63
68
if !preview. trim ( ) . starts_with ( "-- pgt-ignore-all" ) {
64
69
return ;
65
70
}
@@ -109,9 +114,16 @@ impl<'a> SuppressionsParser<'a> {
109
114
SuppressionKind :: End => {
110
115
let matching_start_idx = self
111
116
. start_suppressions_stack
112
- . iter_mut ( )
113
- . rev ( )
114
- . position ( |start| start. rule_specifier == suppr. rule_specifier ) ;
117
+ . iter ( )
118
+ . enumerate ( )
119
+ . filter_map ( |( idx, s) | {
120
+ if s. rule_specifier == suppr. rule_specifier {
121
+ Some ( idx)
122
+ } else {
123
+ None
124
+ }
125
+ } )
126
+ . last ( ) ;
115
127
116
128
if let Some ( start_idx) = matching_start_idx {
117
129
let start = self . start_suppressions_stack . remove ( start_idx) ;
@@ -153,3 +165,191 @@ impl<'a> SuppressionsParser<'a> {
153
165
}
154
166
}
155
167
}
168
+
169
+ #[ cfg( test) ]
170
+ mod tests {
171
+ use pgt_analyse:: RuleCategory ;
172
+
173
+ use super :: * ;
174
+ use crate :: suppression:: { RuleSpecifier , SuppressionKind } ;
175
+
176
+ #[ test]
177
+ fn test_parse_line_suppressions ( ) {
178
+ let doc = r#"
179
+ SELECT 1;
180
+ -- pgt-ignore lint/safety/banDropColumn
181
+ SELECT 2;
182
+ "# ;
183
+ let suppressions = SuppressionsParser :: parse ( doc) ;
184
+
185
+ // Should have a line suppression on line 1 (0-based index)
186
+ let suppression = suppressions
187
+ . line_suppressions
188
+ . get ( & 2 )
189
+ . expect ( "no suppression found" ) ;
190
+
191
+ assert_eq ! ( suppression. kind, SuppressionKind :: Line ) ;
192
+ assert_eq ! (
193
+ suppression. rule_specifier,
194
+ RuleSpecifier :: Rule (
195
+ RuleCategory :: Lint ,
196
+ "safety" . to_string( ) ,
197
+ "banDropColumn" . to_string( )
198
+ )
199
+ ) ;
200
+ }
201
+
202
+ #[ test]
203
+ fn test_parse_multiple_line_suppressions ( ) {
204
+ let doc = r#"
205
+ SELECT 1;
206
+ -- pgt-ignore lint/safety/banDropColumn
207
+ -- pgt-ignore lint/safety/banDropTable
208
+ -- pgt-ignore lint/safety/banDropSomething
209
+ "# ;
210
+
211
+ let suppressions = SuppressionsParser :: parse ( doc) ;
212
+
213
+ assert_eq ! ( suppressions. line_suppressions. len( ) , 3 ) ;
214
+
215
+ assert_eq ! (
216
+ suppressions
217
+ . line_suppressions
218
+ . get( & 2 )
219
+ . unwrap( )
220
+ . rule_specifier
221
+ . rule( ) ,
222
+ Some ( "banDropColumn" )
223
+ ) ;
224
+
225
+ assert_eq ! (
226
+ suppressions
227
+ . line_suppressions
228
+ . get( & 3 )
229
+ . unwrap( )
230
+ . rule_specifier
231
+ . rule( ) ,
232
+ Some ( "banDropTable" )
233
+ ) ;
234
+
235
+ assert_eq ! (
236
+ suppressions
237
+ . line_suppressions
238
+ . get( & 4 )
239
+ . unwrap( )
240
+ . rule_specifier
241
+ . rule( ) ,
242
+ Some ( "banDropSomething" )
243
+ ) ;
244
+ }
245
+
246
+ #[ test]
247
+ fn parses_file_level_suppressions ( ) {
248
+ let doc = r#"
249
+ -- pgt-ignore-all lint
250
+ -- pgt-ignore-all action
251
+
252
+ SELECT 1;
253
+ -- pgt-ignore-all lint/safety
254
+ "# ;
255
+
256
+ let suppressions = SuppressionsParser :: parse ( doc) ;
257
+
258
+ assert_eq ! ( suppressions. diagnostics. len( ) , 1 ) ;
259
+ assert_eq ! ( suppressions. file_suppressions. len( ) , 2 ) ;
260
+
261
+ assert_eq ! (
262
+ suppressions. file_suppressions[ 0 ] . rule_specifier,
263
+ RuleSpecifier :: Category ( RuleCategory :: Lint )
264
+ ) ;
265
+ assert_eq ! (
266
+ suppressions. file_suppressions[ 1 ] . rule_specifier,
267
+ RuleSpecifier :: Category ( RuleCategory :: Action )
268
+ ) ;
269
+
270
+ assert_eq ! (
271
+ suppressions. diagnostics[ 0 ] . message. to_string( ) ,
272
+ String :: from( "File suppressions should be at the top of the file." )
273
+ ) ;
274
+ }
275
+
276
+ #[ test]
277
+ fn parses_range_suppressions ( ) {
278
+ let doc = r#"
279
+ -- pgt-ignore-start lint/safety/banDropTable
280
+ drop table users;
281
+ drop table auth;
282
+ drop table posts;
283
+ -- pgt-ignore-end lint/safety/banDropTable
284
+ "# ;
285
+
286
+ let suppressions = SuppressionsParser :: parse ( doc) ;
287
+
288
+ assert_eq ! ( suppressions. range_suppressions. len( ) , 1 ) ;
289
+
290
+ assert_eq ! (
291
+ suppressions. range_suppressions[ 0 ] ,
292
+ RangeSuppression {
293
+ suppressed_range: TextRange :: new( 1 . into( ) , 141 . into( ) ) ,
294
+ start_suppression: Suppression {
295
+ kind: SuppressionKind :: Start ,
296
+ rule_specifier: RuleSpecifier :: Rule (
297
+ RuleCategory :: Lint ,
298
+ "safety" . to_string( ) ,
299
+ "banDropTable" . to_string( )
300
+ ) ,
301
+ suppression_range: TextRange :: new( 1 . into( ) , 45 . into( ) ) ,
302
+ explanation: None ,
303
+ } ,
304
+ }
305
+ ) ;
306
+ }
307
+
308
+ #[ test]
309
+ fn parses_range_suppressions_with_errors ( ) {
310
+ let doc = r#"
311
+ -- pgt-ignore-start lint/safety/banDropTable
312
+ drop table users;
313
+ -- pgt-ignore-start lint/safety/banDropTable
314
+ drop table auth;
315
+ drop table posts;
316
+ -- pgt-ignore-end lint/safety/banDropTable
317
+ -- pgt-ignore-end lint/safety/banDropColumn
318
+ "# ;
319
+
320
+ let suppressions = SuppressionsParser :: parse ( doc) ;
321
+
322
+ assert_eq ! ( suppressions. range_suppressions. len( ) , 1 ) ;
323
+ assert_eq ! ( suppressions. diagnostics. len( ) , 2 ) ;
324
+
325
+ // the inner, nested start/end combination is recognized.
326
+ assert_eq ! (
327
+ suppressions. range_suppressions[ 0 ] ,
328
+ RangeSuppression {
329
+ suppressed_range: TextRange :: new( 64 . into( ) , 186 . into( ) ) ,
330
+ start_suppression: Suppression {
331
+ kind: SuppressionKind :: Start ,
332
+ rule_specifier: RuleSpecifier :: Rule (
333
+ RuleCategory :: Lint ,
334
+ "safety" . to_string( ) ,
335
+ "banDropTable" . to_string( )
336
+ ) ,
337
+ suppression_range: TextRange :: new( 64 . into( ) , 108 . into( ) ) ,
338
+ explanation: None ,
339
+ } ,
340
+ }
341
+ ) ;
342
+
343
+ // the outer end is an error
344
+ assert_eq ! (
345
+ suppressions. diagnostics[ 0 ] . message. to_string( ) ,
346
+ String :: from( "This end suppression does not have a matching start." )
347
+ ) ;
348
+
349
+ // the outer start is an error
350
+ assert_eq ! (
351
+ suppressions. diagnostics[ 1 ] . message. to_string( ) ,
352
+ String :: from( "This start suppression does not have a matching end." )
353
+ ) ;
354
+ }
355
+ }
0 commit comments