12
12
13
13
using System . Text . RegularExpressions ;
14
14
using System ;
15
+ using System . ComponentModel ;
15
16
using System . Collections . Generic ;
16
17
using System . Diagnostics . CodeAnalysis ;
17
18
using System . Globalization ;
20
21
using System . Management . Automation . Language ;
21
22
using System . IO ;
22
23
using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
24
+ using System . Threading . Tasks ;
25
+ using System . Collections . Concurrent ;
26
+ using System . Threading ;
23
27
24
28
namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . Commands
25
29
{
@@ -267,6 +271,13 @@ private void ProcessPath(string path)
267
271
268
272
}
269
273
274
+ ConcurrentBag < DiagnosticRecord > diagnostics ;
275
+ ConcurrentBag < SuppressedRecord > suppressed ;
276
+ Dictionary < string , List < RuleSuppression > > ruleSuppressions ;
277
+ List < Regex > includeRegexList ;
278
+ List < Regex > excludeRegexList ;
279
+ ConcurrentDictionary < string , List < object > > ruleDictionary ;
280
+
270
281
/// <summary>
271
282
/// Analyzes a single script file.
272
283
/// </summary>
@@ -275,15 +286,16 @@ private void AnalyzeFile(string filePath)
275
286
{
276
287
Token [ ] tokens = null ;
277
288
ParseError [ ] errors = null ;
278
- List < DiagnosticRecord > diagnostics = new List < DiagnosticRecord > ( ) ;
279
- List < SuppressedRecord > suppressed = new List < SuppressedRecord > ( ) ;
289
+ ConcurrentBag < DiagnosticRecord > diagnostics = new ConcurrentBag < DiagnosticRecord > ( ) ;
290
+ ConcurrentBag < SuppressedRecord > suppressed = new ConcurrentBag < SuppressedRecord > ( ) ;
291
+ BlockingCollection < List < object > > verboseOrErrors = new BlockingCollection < List < object > > ( ) ;
280
292
281
293
// Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash
282
294
List < KeyValuePair < CommandInfo , IScriptExtent > > cmdInfoTable = new List < KeyValuePair < CommandInfo , IScriptExtent > > ( ) ;
283
295
284
296
//Check wild card input for the Include/ExcludeRules and create regex match patterns
285
- List < Regex > includeRegexList = new List < Regex > ( ) ;
286
- List < Regex > excludeRegexList = new List < Regex > ( ) ;
297
+ includeRegexList = new List < Regex > ( ) ;
298
+ excludeRegexList = new List < Regex > ( ) ;
287
299
if ( includeRule != null )
288
300
{
289
301
foreach ( string rule in includeRule )
@@ -331,7 +343,7 @@ private void AnalyzeFile(string filePath)
331
343
return ;
332
344
}
333
345
334
- Dictionary < string , List < RuleSuppression > > ruleSuppressions = Helper . Instance . GetRuleSuppression ( ast ) ;
346
+ ruleSuppressions = Helper . Instance . GetRuleSuppression ( ast ) ;
335
347
336
348
foreach ( List < RuleSuppression > ruleSuppressionsList in ruleSuppressions . Values )
337
349
{
@@ -360,43 +372,75 @@ private void AnalyzeFile(string filePath)
360
372
361
373
if ( ScriptAnalyzer . Instance . ScriptRules != null )
362
374
{
363
- foreach ( IScriptRule scriptRule in ScriptAnalyzer . Instance . ScriptRules )
364
- {
365
- bool includeRegexMatch = false ;
366
- bool excludeRegexMatch = false ;
367
- foreach ( Regex include in includeRegexList )
375
+ var tasks = ScriptAnalyzer . Instance . ScriptRules . Select ( scriptRule => Task . Factory . StartNew ( ( ) =>
368
376
{
369
- if ( include . IsMatch ( scriptRule . GetName ( ) ) )
377
+ bool includeRegexMatch = false ;
378
+ bool excludeRegexMatch = false ;
379
+
380
+ foreach ( Regex include in includeRegexList )
370
381
{
371
- includeRegexMatch = true ;
372
- break ;
382
+ if ( include . IsMatch ( scriptRule . GetName ( ) ) )
383
+ {
384
+ includeRegexMatch = true ;
385
+ break ;
386
+ }
373
387
}
374
- }
375
388
376
- foreach ( Regex exclude in excludeRegexList )
377
- {
378
- if ( exclude . IsMatch ( scriptRule . GetName ( ) ) )
389
+ foreach ( Regex exclude in excludeRegexList )
379
390
{
380
- excludeRegexMatch = true ;
381
- break ;
391
+ if ( exclude . IsMatch ( scriptRule . GetName ( ) ) )
392
+ {
393
+ excludeRegexMatch = true ;
394
+ break ;
395
+ }
382
396
}
383
- }
384
-
385
- if ( ( includeRule == null || includeRegexMatch ) && ( excludeRule == null || ! excludeRegexMatch ) )
386
- {
387
- WriteVerbose ( string . Format ( CultureInfo . CurrentCulture , Strings . VerboseRunningMessage , scriptRule . GetName ( ) ) ) ;
388
397
389
- // Ensure that any unhandled errors from Rules are converted to non-terminating errors
390
- // We want the Engine to continue functioning even if one or more Rules throws an exception
391
- try
398
+ if ( ( includeRule == null || includeRegexMatch ) && ( excludeRule == null || ! excludeRegexMatch ) )
392
399
{
393
- var records = Helper . Instance . SuppressRule ( scriptRule . GetName ( ) , ruleSuppressions , scriptRule . AnalyzeScript ( ast , filePath ) . ToList ( ) ) ;
394
- diagnostics . AddRange ( records . Item2 ) ;
395
- suppressed . AddRange ( records . Item1 ) ;
400
+ List < object > result = new List < object > ( ) ;
401
+
402
+ result . Add ( string . Format ( CultureInfo . CurrentCulture , Strings . VerboseRunningMessage , scriptRule . GetName ( ) ) ) ;
403
+
404
+ // Ensure that any unhandled errors from Rules are converted to non-terminating errors
405
+ // We want the Engine to continue functioning even if one or more Rules throws an exception
406
+ try
407
+ {
408
+ var records = Helper . Instance . SuppressRule ( scriptRule . GetName ( ) , ruleSuppressions , scriptRule . AnalyzeScript ( ast , ast . Extent . File ) . ToList ( ) ) ;
409
+ foreach ( var record in records . Item2 )
410
+ {
411
+ diagnostics . Add ( record ) ;
412
+ }
413
+ foreach ( var suppressedRec in records . Item1 )
414
+ {
415
+ suppressed . Add ( suppressedRec ) ;
416
+ }
417
+ }
418
+ catch ( Exception scriptRuleException )
419
+ {
420
+ result . Add ( new ErrorRecord ( scriptRuleException , Strings . RuleErrorMessage , ErrorCategory . InvalidOperation , ast . Extent . File ) ) ;
421
+ }
422
+
423
+ verboseOrErrors . Add ( result ) ;
396
424
}
397
- catch ( Exception scriptRuleException )
425
+ } ) ) ;
426
+
427
+ Task . Factory . ContinueWhenAll ( tasks . ToArray ( ) , t => verboseOrErrors . CompleteAdding ( ) ) ;
428
+
429
+ while ( ! verboseOrErrors . IsCompleted )
430
+ {
431
+ List < object > data = null ;
432
+ try
433
+ {
434
+ data = verboseOrErrors . Take ( ) ;
435
+ }
436
+ catch ( InvalidOperationException ) { }
437
+
438
+ if ( data != null )
439
+ {
440
+ WriteVerbose ( data [ 0 ] as string ) ;
441
+ if ( data . Count == 2 )
398
442
{
399
- WriteError ( new ErrorRecord ( scriptRuleException , Strings . RuleErrorMessage , ErrorCategory . InvalidOperation , filePath ) ) ;
443
+ WriteError ( data [ 1 ] as ErrorRecord ) ;
400
444
}
401
445
}
402
446
}
@@ -437,8 +481,14 @@ private void AnalyzeFile(string filePath)
437
481
try
438
482
{
439
483
var records = Helper . Instance . SuppressRule ( tokenRule . GetName ( ) , ruleSuppressions , tokenRule . AnalyzeTokens ( tokens , filePath ) . ToList ( ) ) ;
440
- diagnostics . AddRange ( records . Item2 ) ;
441
- suppressed . AddRange ( records . Item1 ) ;
484
+ foreach ( var record in records . Item2 )
485
+ {
486
+ diagnostics . Add ( record ) ;
487
+ }
488
+ foreach ( var suppressedRec in records . Item1 )
489
+ {
490
+ suppressed . Add ( suppressedRec ) ;
491
+ }
442
492
}
443
493
catch ( Exception tokenRuleException )
444
494
{
@@ -489,8 +539,14 @@ private void AnalyzeFile(string filePath)
489
539
try
490
540
{
491
541
var records = Helper . Instance . SuppressRule ( dscResourceRule . GetName ( ) , ruleSuppressions , dscResourceRule . AnalyzeDSCClass ( ast , filePath ) . ToList ( ) ) ;
492
- diagnostics . AddRange ( records . Item2 ) ;
493
- suppressed . AddRange ( records . Item1 ) ;
542
+ foreach ( var record in records . Item2 )
543
+ {
544
+ diagnostics . Add ( record ) ;
545
+ }
546
+ foreach ( var suppressedRec in records . Item1 )
547
+ {
548
+ suppressed . Add ( suppressedRec ) ;
549
+ }
494
550
}
495
551
catch ( Exception dscResourceRuleException )
496
552
{
@@ -532,8 +588,14 @@ private void AnalyzeFile(string filePath)
532
588
try
533
589
{
534
590
var records = Helper . Instance . SuppressRule ( dscResourceRule . GetName ( ) , ruleSuppressions , dscResourceRule . AnalyzeDSCResource ( ast , filePath ) . ToList ( ) ) ;
535
- diagnostics . AddRange ( records . Item2 ) ;
536
- suppressed . AddRange ( records . Item1 ) ;
591
+ foreach ( var record in records . Item2 )
592
+ {
593
+ diagnostics . Add ( record ) ;
594
+ }
595
+ foreach ( var suppressedRec in records . Item1 )
596
+ {
597
+ suppressed . Add ( suppressedRec ) ;
598
+ }
537
599
}
538
600
catch ( Exception dscResourceRuleException )
539
601
{
@@ -573,15 +635,20 @@ private void AnalyzeFile(string filePath)
573
635
}
574
636
}
575
637
576
- diagnostics . AddRange ( ScriptAnalyzer . Instance . GetExternalRecord ( ast , tokens , exRules . ToArray ( ) , this , fileName ) ) ;
638
+ foreach ( var record in ScriptAnalyzer . Instance . GetExternalRecord ( ast , tokens , exRules . ToArray ( ) , this , fileName ) )
639
+ {
640
+ diagnostics . Add ( record ) ;
641
+ }
577
642
}
578
643
579
644
#endregion
580
645
646
+ IEnumerable < DiagnosticRecord > diagnosticsList = diagnostics ;
647
+
581
648
if ( severity != null )
582
649
{
583
650
var diagSeverity = severity . Select ( item => Enum . Parse ( typeof ( DiagnosticSeverity ) , item , true ) ) ;
584
- diagnostics = diagnostics . Where ( item => diagSeverity . Contains ( item . Severity ) ) . ToList ( ) ;
651
+ diagnosticsList = diagnostics . Where ( item => diagSeverity . Contains ( item . Severity ) ) ;
585
652
}
586
653
587
654
//Output through loggers
@@ -596,7 +663,7 @@ private void AnalyzeFile(string filePath)
596
663
}
597
664
else
598
665
{
599
- foreach ( DiagnosticRecord diagnostic in diagnostics )
666
+ foreach ( DiagnosticRecord diagnostic in diagnosticsList )
600
667
{
601
668
logger . LogObject ( diagnostic , this ) ;
602
669
}
0 commit comments