@@ -1323,156 +1323,177 @@ internal List<RuleSuppression> GetSuppressionsConfiguration(ConfigurationDefinit
1323
1323
/// Suppress the rules from the diagnostic records list.
1324
1324
/// Returns a list of suppressed records as well as the ones that are not suppressed
1325
1325
/// </summary>
1326
- /// <param name="ruleSuppressions"></param>
1327
- /// <param name="diagnostics"></param>
1326
+ /// <param name="ruleName">The name of the rule possibly being suppressed.</param>
1327
+ /// <param name="ruleSuppressionsDict">A lookup table from rule name to suppressions of that rule.</param>
1328
+ /// <param name="diagnostics">The list of all diagnostics emitted by the given rule.</param>
1329
+ /// <param name="errors">Any errors that arise due to misconfigured suppression settings.</param>
1330
+ /// <returns>A pair, with the first item being the list of suppressed diagnostics, and the second the list of unsuppressed diagnostics.</returns>
1328
1331
public Tuple < List < SuppressedRecord > , List < DiagnosticRecord > > SuppressRule (
1329
1332
string ruleName ,
1330
1333
Dictionary < string , List < RuleSuppression > > ruleSuppressionsDict ,
1331
1334
List < DiagnosticRecord > diagnostics ,
1332
- out List < ErrorRecord > errorRecords )
1335
+ out List < ErrorRecord > errors )
1333
1336
{
1334
- List < SuppressedRecord > suppressedRecords = new List < SuppressedRecord > ( ) ;
1335
- List < DiagnosticRecord > unSuppressedRecords = new List < DiagnosticRecord > ( ) ;
1336
- Tuple < List < SuppressedRecord > , List < DiagnosticRecord > > result = Tuple . Create ( suppressedRecords , unSuppressedRecords ) ;
1337
- errorRecords = new List < ErrorRecord > ( ) ;
1338
- if ( diagnostics == null || diagnostics . Count == 0 )
1337
+ var suppressedRecords = new List < SuppressedRecord > ( ) ;
1338
+ var unsuppressedRecords = new List < DiagnosticRecord > ( ) ;
1339
+ errors = new List < ErrorRecord > ( ) ;
1340
+ var result = new Tuple < List < SuppressedRecord > , List < DiagnosticRecord > > ( suppressedRecords , unsuppressedRecords ) ;
1341
+
1342
+ if ( diagnostics is null || diagnostics . Count == 0 )
1339
1343
{
1340
1344
return result ;
1341
1345
}
1342
1346
1343
- if ( ruleSuppressionsDict == null || ! ruleSuppressionsDict . ContainsKey ( ruleName )
1344
- || ruleSuppressionsDict [ ruleName ] . Count == 0 )
1347
+ if ( ruleSuppressionsDict is null
1348
+ || ! ruleSuppressionsDict . TryGetValue ( ruleName , out List < RuleSuppression > ruleSuppressions )
1349
+ || ruleSuppressions . Count == 0 )
1345
1350
{
1346
- unSuppressedRecords . AddRange ( diagnostics ) ;
1351
+ unsuppressedRecords . AddRange ( diagnostics ) ;
1347
1352
return result ;
1348
1353
}
1349
1354
1350
- List < RuleSuppression > ruleSuppressions = ruleSuppressionsDict [ ruleName ] ;
1351
- var offsetArr = GetOffsetArray ( diagnostics ) ;
1352
- int recordIndex = 0 ;
1353
- int startRecord = 0 ;
1354
- bool [ ] suppressed = new bool [ diagnostics . Count ] ;
1355
- foreach ( RuleSuppression ruleSuppression in ruleSuppressions )
1355
+ // We need to report errors for any rule suppressions with IDs that are not applied to anything
1356
+ // Start by assuming all suppressions are unapplied and then we'll remove them as we apply them later
1357
+ var unappliedSuppressions = new HashSet < RuleSuppression > ( ) ;
1358
+ foreach ( RuleSuppression suppression in ruleSuppressions )
1356
1359
{
1357
- int suppressionCount = 0 ;
1358
- while ( startRecord < diagnostics . Count
1359
- // && diagnostics[startRecord].Extent.StartOffset < ruleSuppression.StartOffset)
1360
- // && diagnostics[startRecord].Extent.StartLineNumber < ruleSuppression.st)
1361
- && offsetArr [ startRecord ] != null && offsetArr [ startRecord ] . Item1 < ruleSuppression . StartOffset )
1360
+ if ( ! string . IsNullOrWhiteSpace ( suppression . RuleSuppressionID ) )
1362
1361
{
1363
- startRecord += 1 ;
1362
+ unappliedSuppressions . Add ( suppression ) ;
1364
1363
}
1365
-
1366
- // at this point, start offset of startRecord is greater or equals to rulesuppression.startoffset
1367
- recordIndex = startRecord ;
1368
-
1369
- while ( recordIndex < diagnostics . Count )
1364
+ }
1365
+
1366
+ // We have suppressions and diagnostics
1367
+ // For each diagnostic, collect all the suppressions that apply to it
1368
+ // and if there are any, record the diagnostic as suppressed
1369
+ foreach ( DiagnosticRecord diagnostic in diagnostics )
1370
+ {
1371
+ Tuple < int , int > offsetPair = GetOffset ( diagnostic ) ;
1372
+
1373
+ var appliedSuppressions = new List < RuleSuppression > ( ) ;
1374
+ foreach ( RuleSuppression suppression in ruleSuppressions )
1370
1375
{
1371
- DiagnosticRecord record = diagnostics [ recordIndex ] ;
1372
- var curOffset = offsetArr [ recordIndex ] ;
1373
-
1374
- //if (record.Extent.EndOffset > ruleSuppression.EndOffset)
1375
- if ( curOffset != null && curOffset . Item2 > ruleSuppression . EndOffset )
1376
+ // Check that the diagnostic is within the suppressed extent, if we have such an extent
1377
+ if ( ! ( offsetPair is null )
1378
+ && ( offsetPair . Item1 < suppression . StartOffset || offsetPair . Item2 > suppression . EndOffset ) )
1376
1379
{
1377
- break ;
1380
+ continue ;
1378
1381
}
1379
-
1380
- // we suppress if there is no suppression id or if there is suppression id and it matches
1381
- if ( string . IsNullOrWhiteSpace ( ruleSuppression . RuleSuppressionID )
1382
- || ( ! String . IsNullOrWhiteSpace ( record . RuleSuppressionID ) &&
1383
- string . Equals ( ruleSuppression . RuleSuppressionID , record . RuleSuppressionID , StringComparison . OrdinalIgnoreCase ) ) )
1382
+
1383
+ // If there's a rule suppression ID, check that it matches the diagnostic
1384
+ if ( ! string . IsNullOrWhiteSpace ( suppression . RuleSuppressionID )
1385
+ && ! string . Equals ( diagnostic . RuleSuppressionID , suppression . RuleSuppressionID , StringComparison . OrdinalIgnoreCase ) )
1384
1386
{
1385
- suppressed [ recordIndex ] = true ;
1386
- suppressedRecords . Add ( new SuppressedRecord ( record , ruleSuppression ) ) ;
1387
- suppressionCount += 1 ;
1387
+ continue ;
1388
1388
}
1389
-
1390
- recordIndex += 1 ;
1389
+
1390
+ unappliedSuppressions . Remove ( suppression ) ;
1391
+ appliedSuppressions . Add ( suppression ) ;
1391
1392
}
1392
-
1393
- // If we cannot found any error but the rulesuppression has a rulesuppressionid then it must be used wrongly
1394
- if ( ! String . IsNullOrWhiteSpace ( ruleSuppression . RuleSuppressionID ) && suppressionCount == 0 )
1393
+
1394
+ if ( appliedSuppressions . Count > 0 )
1395
1395
{
1396
- // checks whether are given a string or a file path
1397
- if ( String . IsNullOrWhiteSpace ( diagnostics . First ( ) . Extent . File ) )
1398
- {
1399
- ruleSuppression . Error = String . Format ( CultureInfo . CurrentCulture , Strings . RuleSuppressionErrorFormatScriptDefinition , ruleSuppression . StartAttributeLine ,
1400
- String . Format ( Strings . RuleSuppressionIDError , ruleSuppression . RuleSuppressionID ) ) ;
1401
- }
1402
- else
1403
- {
1404
- ruleSuppression . Error = String . Format ( CultureInfo . CurrentCulture , Strings . RuleSuppressionErrorFormat , ruleSuppression . StartAttributeLine ,
1405
- System . IO . Path . GetFileName ( diagnostics . First ( ) . Extent . File ) , String . Format ( Strings . RuleSuppressionIDError , ruleSuppression . RuleSuppressionID ) ) ;
1406
- }
1407
- errorRecords . Add ( new ErrorRecord ( new ArgumentException ( ruleSuppression . Error ) , ruleSuppression . Error , ErrorCategory . InvalidArgument , ruleSuppression ) ) ;
1408
- //this.outputWriter.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression));
1396
+ suppressedRecords . Add ( new SuppressedRecord ( diagnostic , appliedSuppressions ) ) ;
1397
+ }
1398
+ else
1399
+ {
1400
+ unsuppressedRecords . Add ( diagnostic ) ;
1409
1401
}
1410
1402
}
1411
-
1412
- for ( int i = 0 ; i < suppressed . Length ; i += 1 )
1403
+
1404
+ // Do any error reporting for misused RuleSuppressionIDs here.
1405
+ // If the user applied a suppression qualified by a suppression ID,
1406
+ // but that suppression didn't apply only because the suppression ID didn't match
1407
+ // we let the user know in the form of an error.
1408
+ string analyzedFile = diagnostics [ 0 ] ? . Extent ? . File ;
1409
+ bool appliedToFile = ! string . IsNullOrEmpty ( analyzedFile ) ;
1410
+ string analyzedFileName = appliedToFile ? Path . GetFileName ( analyzedFile ) : null ;
1411
+ foreach ( RuleSuppression unappliedSuppression in unappliedSuppressions )
1413
1412
{
1414
- if ( ! suppressed [ i ] )
1413
+ string suppressionIDError = string . Format ( Strings . RuleSuppressionIDError , unappliedSuppression . RuleSuppressionID ) ;
1414
+
1415
+ if ( appliedToFile )
1415
1416
{
1416
- unSuppressedRecords . Add ( diagnostics [ i ] ) ;
1417
+ unappliedSuppression . Error = string . Format (
1418
+ CultureInfo . CurrentCulture ,
1419
+ Strings . RuleSuppressionErrorFormat ,
1420
+ unappliedSuppression . StartAttributeLine ,
1421
+ analyzedFileName ,
1422
+ suppressionIDError ) ;
1417
1423
}
1424
+ else
1425
+ {
1426
+ unappliedSuppression . Error = string . Format (
1427
+ CultureInfo . CurrentCulture ,
1428
+ Strings . RuleSuppressionErrorFormatScriptDefinition ,
1429
+ unappliedSuppression . StartAttributeLine ,
1430
+ suppressionIDError ) ;
1431
+ }
1432
+
1433
+ errors . Add (
1434
+ new ErrorRecord (
1435
+ new ArgumentException ( unappliedSuppression . Error ) ,
1436
+ unappliedSuppression . Error ,
1437
+ ErrorCategory . InvalidArgument ,
1438
+ unappliedSuppression ) ) ;
1418
1439
}
1419
1440
1420
1441
return result ;
1421
1442
}
1422
1443
1423
- private Tuple < int , int > [ ] GetOffsetArray ( List < DiagnosticRecord > diagnostics )
1444
+ private Tuple < int , int > GetOffset ( DiagnosticRecord diagnostic )
1424
1445
{
1425
- Func < int , int , Tuple < int , int > > GetTuple = ( x , y ) => new Tuple < int , int > ( x , y ) ;
1426
- Func < Tuple < int , int > > GetDefaultTuple = ( ) => GetTuple ( 0 , 0 ) ;
1427
- var offsets = new Tuple < int , int > [ diagnostics . Count ] ;
1428
- for ( int k = 0 ; k < diagnostics . Count ; k ++ )
1446
+ IScriptExtent extent = diagnostic . Extent ;
1447
+
1448
+ if ( extent is null )
1429
1449
{
1430
- var ext = diagnostics [ k ] . Extent ;
1431
- if ( ext == null )
1432
- {
1433
- continue ;
1434
- }
1435
- if ( ext . StartOffset == 0 && ext . EndOffset == 0 )
1450
+ return null ;
1451
+ }
1452
+
1453
+ if ( extent . StartOffset != 0 && extent . EndOffset != 0 )
1454
+ {
1455
+ return Tuple . Create ( extent . StartOffset , extent . EndOffset ) ;
1456
+ }
1457
+
1458
+ // We're forced to determine the offset ourselves from the lines and columns now
1459
+ // Rather than counting every line in the file, we scan through the tokens looking for ones matching the offset
1460
+
1461
+ Token startToken = null ;
1462
+ int i = 0 ;
1463
+ for ( ; i < Tokens . Length ; i ++ )
1464
+ {
1465
+ Token curr = Tokens [ i ] ;
1466
+ if ( curr . Extent . StartLineNumber == extent . StartLineNumber
1467
+ && curr . Extent . StartColumnNumber == extent . StartColumnNumber )
1436
1468
{
1437
- // check if line and column number correspond to 0 offsets
1438
- if ( ext . StartLineNumber == 1
1439
- && ext . StartColumnNumber == 1
1440
- && ext . EndLineNumber == 1
1441
- && ext . EndColumnNumber == 1 )
1442
- {
1443
- offsets [ k ] = GetDefaultTuple ( ) ;
1444
- continue ;
1445
- }
1446
- // created using the ScriptExtent constructor, which sets
1447
- // StartOffset and EndOffset to 0
1448
- // find the token the corresponding start line and column number
1449
- var startToken = Tokens . Where ( x
1450
- => x . Extent . StartLineNumber == ext . StartLineNumber
1451
- && x . Extent . StartColumnNumber == ext . StartColumnNumber )
1452
- . FirstOrDefault ( ) ;
1453
- if ( startToken == null )
1454
- {
1455
- offsets [ k ] = GetDefaultTuple ( ) ;
1456
- continue ;
1457
- }
1458
- var endToken = Tokens . Where ( x
1459
- => x . Extent . EndLineNumber == ext . EndLineNumber
1460
- && x . Extent . EndColumnNumber == ext . EndColumnNumber )
1461
- . FirstOrDefault ( ) ;
1462
- if ( endToken == null )
1463
- {
1464
- offsets [ k ] = GetDefaultTuple ( ) ;
1465
- continue ;
1466
- }
1467
- offsets [ k ] = GetTuple ( startToken . Extent . StartOffset , endToken . Extent . EndOffset ) ;
1469
+ startToken = curr ;
1470
+ break ;
1468
1471
}
1469
- else
1472
+ }
1473
+
1474
+ if ( startToken is null )
1475
+ {
1476
+ return Tuple . Create ( 0 , 0 ) ;
1477
+ }
1478
+
1479
+ Token endToken = null ;
1480
+ for ( ; i < Tokens . Length ; i ++ )
1481
+ {
1482
+ Token curr = Tokens [ i ] ;
1483
+ if ( curr . Extent . EndLineNumber == extent . EndLineNumber
1484
+ && curr . Extent . EndColumnNumber == extent . EndColumnNumber )
1470
1485
{
1471
- // Extent has valid offsets
1472
- offsets [ k ] = GetTuple ( ext . StartOffset , ext . EndOffset ) ;
1486
+ endToken = curr ;
1487
+ break ;
1473
1488
}
1474
1489
}
1475
- return offsets ;
1490
+
1491
+ if ( endToken is null )
1492
+ {
1493
+ return Tuple . Create ( 0 , 0 ) ;
1494
+ }
1495
+
1496
+ return Tuple . Create ( startToken . Extent . StartOffset , endToken . Extent . EndOffset ) ;
1476
1497
}
1477
1498
1478
1499
public static string [ ] ProcessCustomRulePaths ( string [ ] rulePaths , SessionState sessionState , bool recurse = false )
0 commit comments