@@ -39,7 +39,10 @@ private enum ErrorKind
39
39
TokenKind . Foreach ,
40
40
TokenKind . While ,
41
41
TokenKind . Until ,
42
- TokenKind . Do
42
+ TokenKind . Do ,
43
+ TokenKind . Else ,
44
+ TokenKind . Catch ,
45
+ TokenKind . Finally
43
46
} ;
44
47
45
48
private List < Func < TokenOperations , IEnumerable < DiagnosticRecord > > > violationFinders
@@ -78,6 +81,7 @@ public override void ConfigureRule(IDictionary<string, object> paramValueMap)
78
81
if ( CheckOpenBrace )
79
82
{
80
83
violationFinders . Add ( FindOpenBraceViolations ) ;
84
+ violationFinders . Add ( FindSpaceAfterClosingBraceViolations ) ;
81
85
violationFinders . Add ( FindKeywordAfterBraceViolations ) ;
82
86
}
83
87
@@ -301,7 +305,7 @@ private IEnumerable<DiagnosticRecord> FindKeywordAfterBraceViolations(TokenOpera
301
305
keyword . Extent . StartLineNumber ,
302
306
keywordNode . Previous . Value . Extent . EndColumnNumber ,
303
307
keyword . Extent . StartColumnNumber ,
304
- " " ,
308
+ whiteSpace ,
305
309
keyword . Extent . File )
306
310
} ;
307
311
@@ -321,54 +325,131 @@ private IEnumerable<DiagnosticRecord> FindKeywordAfterBraceViolations(TokenOpera
321
325
322
326
private IEnumerable < DiagnosticRecord > FindInnerBraceViolations ( TokenOperations tokenOperations )
323
327
{
328
+ // Handle opening braces
324
329
foreach ( var lCurly in tokenOperations . GetTokenNodes ( TokenKind . LCurly ) )
325
330
{
326
331
if ( lCurly . Next == null
327
- || ! ( lCurly . Previous == null || IsPreviousTokenOnSameLine ( lCurly ) )
332
+ || ( lCurly . Previous != null && ! IsPreviousTokenOnSameLine ( lCurly ) )
328
333
|| lCurly . Next . Value . Kind == TokenKind . NewLine
329
- || lCurly . Next . Value . Kind == TokenKind . LineContinuation
330
- || lCurly . Next . Value . Kind == TokenKind . RCurly
331
- )
334
+ || lCurly . Next . Value . Kind == TokenKind . LineContinuation )
335
+ {
336
+ continue ;
337
+ }
338
+
339
+ // Special handling for empty braces - they should have a space
340
+ if ( lCurly . Next . Value . Kind == TokenKind . RCurly )
332
341
{
342
+ if ( ! IsNextTokenApartByWhitespace ( lCurly ) )
343
+ {
344
+ var prevToken = lCurly . Previous ? . Value ?? lCurly . Value ;
345
+ var nextToken = lCurly . Next ? . Value ?? lCurly . Value ;
346
+
347
+ yield return new DiagnosticRecord (
348
+ GetError ( ErrorKind . AfterOpeningBrace ) ,
349
+ lCurly . Value . Extent ,
350
+ GetName ( ) ,
351
+ GetDiagnosticSeverity ( ) ,
352
+ tokenOperations . Ast . Extent . File ,
353
+ null ,
354
+ GetCorrections ( prevToken , lCurly . Value , nextToken , true , false ) . ToList ( ) ) ;
355
+ }
333
356
continue ;
334
357
}
335
358
336
359
if ( ! IsNextTokenApartByWhitespace ( lCurly ) )
337
360
{
361
+ var prevToken = lCurly . Previous ? . Value ?? lCurly . Value ;
338
362
yield return new DiagnosticRecord (
339
363
GetError ( ErrorKind . AfterOpeningBrace ) ,
340
364
lCurly . Value . Extent ,
341
365
GetName ( ) ,
342
366
GetDiagnosticSeverity ( ) ,
343
367
tokenOperations . Ast . Extent . File ,
344
368
null ,
345
- GetCorrections ( lCurly . Previous . Value , lCurly . Value , lCurly . Next . Value , true , false ) . ToList ( ) ) ;
369
+ GetCorrections ( prevToken , lCurly . Value , lCurly . Next . Value , true , false ) . ToList ( ) ) ;
346
370
}
347
371
}
348
372
373
+ // Handle closing braces
349
374
foreach ( var rCurly in tokenOperations . GetTokenNodes ( TokenKind . RCurly ) )
350
375
{
351
- if ( rCurly . Previous == null
352
- || ! IsPreviousTokenOnSameLine ( rCurly )
353
- || rCurly . Previous . Value . Kind == TokenKind . LCurly
376
+ if ( rCurly . Previous == null )
377
+ {
378
+ continue ;
379
+ }
380
+
381
+ if ( ! IsPreviousTokenOnSameLine ( rCurly )
354
382
|| rCurly . Previous . Value . Kind == TokenKind . NewLine
355
383
|| rCurly . Previous . Value . Kind == TokenKind . LineContinuation
356
- || rCurly . Previous . Value . Kind == TokenKind . AtCurly
357
- )
384
+ || rCurly . Previous . Value . Kind == TokenKind . AtCurly )
385
+ {
386
+ continue ;
387
+ }
388
+
389
+ // Skip empty braces that already have space
390
+ if ( rCurly . Previous . Value . Kind == TokenKind . LCurly && IsPreviousTokenApartByWhitespace ( rCurly ) )
358
391
{
359
392
continue ;
360
393
}
361
394
362
- if ( ! IsPreviousTokenApartByWhitespace ( rCurly ) )
395
+ // Use AST to check if this is a hashtable
396
+ var ast = tokenOperations . GetAstPosition ( rCurly . Value ) ;
397
+
398
+ if ( ast is HashtableAst hashtableAst )
399
+ {
400
+ if ( rCurly . Value . Extent . EndOffset == hashtableAst . Extent . EndOffset )
401
+ {
402
+ continue ;
403
+ }
404
+ }
405
+
406
+ bool hasSpace = IsPreviousTokenApartByWhitespace ( rCurly ) ;
407
+
408
+ if ( ! hasSpace )
363
409
{
410
+ var nextToken = rCurly . Next ? . Value ?? rCurly . Value ;
364
411
yield return new DiagnosticRecord (
365
412
GetError ( ErrorKind . BeforeClosingBrace ) ,
366
413
rCurly . Value . Extent ,
367
414
GetName ( ) ,
368
415
GetDiagnosticSeverity ( ) ,
369
416
tokenOperations . Ast . Extent . File ,
370
417
null ,
371
- GetCorrections ( rCurly . Previous . Value , rCurly . Value , rCurly . Next . Value , false , true ) . ToList ( ) ) ;
418
+ GetCorrections ( rCurly . Previous . Value , rCurly . Value , nextToken , false , true ) . ToList ( ) ) ;
419
+ }
420
+ }
421
+ }
422
+
423
+ private IEnumerable < DiagnosticRecord > FindSpaceAfterClosingBraceViolations ( TokenOperations tokenOperations )
424
+ {
425
+ foreach ( var rCurly in tokenOperations . GetTokenNodes ( TokenKind . RCurly ) )
426
+ {
427
+ if ( rCurly . Next == null
428
+ || ! IsPreviousTokenOnSameLine ( rCurly . Next )
429
+ || rCurly . Next . Value . Kind == TokenKind . NewLine
430
+ || rCurly . Next . Value . Kind == TokenKind . EndOfInput
431
+ || rCurly . Next . Value . Kind == TokenKind . Semi
432
+ || rCurly . Next . Value . Kind == TokenKind . Comma
433
+ || rCurly . Next . Value . Kind == TokenKind . RParen )
434
+ {
435
+ continue ;
436
+ }
437
+
438
+ // Need space after } before keywords, numbers, or another }
439
+ if ( ( IsKeyword ( rCurly . Next . Value )
440
+ || rCurly . Next . Value . Kind == TokenKind . Number
441
+ || rCurly . Next . Value . Kind == TokenKind . RCurly )
442
+ && ! IsNextTokenApartByWhitespace ( rCurly ) )
443
+ {
444
+ var prevToken = rCurly . Previous ? . Value ?? rCurly . Value ;
445
+ yield return new DiagnosticRecord (
446
+ GetError ( ErrorKind . BeforeOpeningBrace ) ,
447
+ rCurly . Value . Extent ,
448
+ GetName ( ) ,
449
+ GetDiagnosticSeverity ( ) ,
450
+ tokenOperations . Ast . Extent . File ,
451
+ null ,
452
+ GetCorrections ( prevToken , rCurly . Value , rCurly . Next . Value , true , false ) . ToList ( ) ) ;
372
453
}
373
454
}
374
455
}
@@ -456,8 +537,7 @@ private IEnumerable<DiagnosticRecord> FindOpenParenViolations(TokenOperations to
456
537
457
538
private IEnumerable < DiagnosticRecord > FindParameterViolations ( Ast ast )
458
539
{
459
- IEnumerable < Ast > commandAsts = ast . FindAll (
460
- testAst => testAst is CommandAst , true ) ;
540
+ IEnumerable < Ast > commandAsts = ast . FindAll ( testAst => testAst is CommandAst , true ) ;
461
541
foreach ( CommandAst commandAst in commandAsts )
462
542
{
463
543
/// When finding all the command parameter elements, there is no guarantee that
@@ -471,6 +551,7 @@ private IEnumerable<DiagnosticRecord> FindParameterViolations(Ast ast)
471
551
) . ThenBy (
472
552
e => e . Extent . StartColumnNumber
473
553
) . ToList ( ) ;
554
+
474
555
for ( int i = 0 ; i < commandParameterAstElements . Count - 1 ; i ++ )
475
556
{
476
557
IScriptExtent leftExtent = commandParameterAstElements [ i ] . Extent ;
@@ -511,33 +592,90 @@ private bool IsSeparator(Token token)
511
592
512
593
private IEnumerable < DiagnosticRecord > FindSeparatorViolations ( TokenOperations tokenOperations )
513
594
{
514
- Func < LinkedListNode < Token > , bool > predicate = node =>
595
+ foreach ( var tokenNode in tokenOperations . GetTokenNodes ( IsSeparator ) )
515
596
{
516
- return node . Next != null
517
- && node . Next . Value . Kind != TokenKind . NewLine
518
- && node . Next . Value . Kind != TokenKind . Comment
519
- && node . Next . Value . Kind != TokenKind . EndOfInput // semicolon can be followed by end of input
520
- && ! IsPreviousTokenApartByWhitespace ( node . Next ) ;
521
- } ;
597
+ if ( tokenNode . Next == null
598
+ || tokenNode . Next . Value . Kind == TokenKind . NewLine
599
+ || tokenNode . Next . Value . Kind == TokenKind . Comment
600
+ || tokenNode . Next . Value . Kind == TokenKind . EndOfInput )
601
+ {
602
+ continue ;
603
+ }
522
604
523
- foreach ( var tokenNode in tokenOperations . GetTokenNodes ( IsSeparator ) . Where ( predicate ) )
524
- {
525
- var errorKind = tokenNode . Value . Kind == TokenKind . Comma
526
- ? ErrorKind . SeparatorComma
527
- : ErrorKind . SeparatorSemi ;
528
- yield return getDiagnosticRecord (
529
- tokenNode . Value ,
530
- errorKind ,
531
- GetCorrections (
532
- tokenNode . Previous . Value ,
533
- tokenNode . Value ,
534
- tokenNode . Next . Value ,
535
- true ,
536
- false ) ) ;
605
+ var separator = tokenNode . Value ;
606
+
607
+ // Check if comma is part of a parameter value by looking at surrounding tokens
608
+ if ( separator . Kind == TokenKind . Comma )
609
+ {
610
+ // Look for pattern: word,word (no spaces) which indicates parameter value
611
+ if ( tokenNode . Previous != null && tokenNode . Next != null )
612
+ {
613
+ var prevTok = tokenNode . Previous . Value ;
614
+ var nextTok = tokenNode . Next . Value ;
615
+
616
+ // Skip if comma appears to be within a parameter value (no spaces around it)
617
+ if ( ( prevTok . Kind == TokenKind . Identifier || prevTok . Kind == TokenKind . Generic ) &&
618
+ ( nextTok . Kind == TokenKind . Identifier || nextTok . Kind == TokenKind . Generic ) &&
619
+ prevTok . Extent . EndColumnNumber == separator . Extent . StartColumnNumber &&
620
+ separator . Extent . EndColumnNumber == nextTok . Extent . StartColumnNumber )
621
+ {
622
+ // This looks like key=value,key=value pattern
623
+ continue ;
624
+ }
625
+ }
626
+ }
627
+
628
+ var prevToken = tokenNode . Previous . Value ;
629
+ var nextToken = tokenNode . Next . Value ;
630
+
631
+ // Check for space before separator (should not exist)
632
+ if ( tokenNode . Previous != null && IsPreviousTokenOnSameLine ( tokenNode ) )
633
+ {
634
+ var spaceBefore = separator . Extent . StartColumnNumber - prevToken . Extent . EndColumnNumber ;
635
+ if ( spaceBefore > 0 )
636
+ {
637
+ // Remove space before separator
638
+ yield return new DiagnosticRecord (
639
+ GetError ( separator . Kind == TokenKind . Comma ? ErrorKind . SeparatorComma : ErrorKind . SeparatorSemi ) ,
640
+ separator . Extent ,
641
+ GetName ( ) ,
642
+ GetDiagnosticSeverity ( ) ,
643
+ separator . Extent . File ,
644
+ null ,
645
+ new List < CorrectionExtent > {
646
+ new CorrectionExtent (
647
+ prevToken . Extent . EndLineNumber ,
648
+ separator . Extent . StartLineNumber ,
649
+ prevToken . Extent . EndColumnNumber ,
650
+ separator . Extent . StartColumnNumber ,
651
+ string . Empty ,
652
+ separator . Extent . File )
653
+ } ) ;
654
+ }
655
+ }
656
+
657
+ // Check for space after separator (should exist)
658
+ if ( ! IsPreviousTokenApartByWhitespace ( tokenNode . Next ) )
659
+ {
660
+ var errorKind = separator . Kind == TokenKind . Comma ? ErrorKind . SeparatorComma : ErrorKind . SeparatorSemi ;
661
+
662
+ yield return GetDiagnosticRecord (
663
+ separator ,
664
+ errorKind ,
665
+ new List < CorrectionExtent > {
666
+ new CorrectionExtent (
667
+ separator . Extent . EndLineNumber ,
668
+ nextToken . Extent . StartLineNumber ,
669
+ separator . Extent . EndColumnNumber ,
670
+ nextToken . Extent . StartColumnNumber ,
671
+ whiteSpace ,
672
+ separator . Extent . File )
673
+ } ) ;
674
+ }
537
675
}
538
676
}
539
677
540
- private DiagnosticRecord getDiagnosticRecord (
678
+ private DiagnosticRecord GetDiagnosticRecord (
541
679
Token token ,
542
680
ErrorKind errKind ,
543
681
List < CorrectionExtent > corrections )
@@ -602,6 +740,11 @@ private IEnumerable<DiagnosticRecord> FindOperatorViolations(TokenOperations tok
602
740
{
603
741
var token = tokenNode . Value ;
604
742
743
+ if ( IsSeparator ( token ) )
744
+ {
745
+ continue ;
746
+ }
747
+
605
748
if ( tokenNode . Previous == null || tokenNode . Next == null || token . Kind == TokenKind . DotDot )
606
749
{
607
750
continue ;
@@ -617,7 +760,6 @@ private IEnumerable<DiagnosticRecord> FindOperatorViolations(TokenOperations tok
617
760
tokenNode . Previous . Previous != null )
618
761
{
619
762
var beforeLParen = tokenNode . Previous . Previous . Value ;
620
-
621
763
isUnaryInMethodCall = beforeLParen . Kind == TokenKind . Dot ||
622
764
( beforeLParen . TokenFlags & TokenFlags . MemberName ) == TokenFlags . MemberName ;
623
765
}
0 commit comments