@@ -28,7 +28,7 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2828#endif
2929 public class UseWhitespace : ConfigurableRule
3030 {
31- private enum ErrorKind { Brace , Paren } ;
31+ private enum ErrorKind { Brace , Paren , Operator } ;
3232 private readonly int whiteSpaceSize = 1 ;
3333
3434 private List < Func < TokenOperations , IEnumerable < DiagnosticRecord > > > violationFinders
@@ -40,6 +40,8 @@ private List<Func<TokenOperations, IEnumerable<DiagnosticRecord>>> violationFind
4040 [ ConfigurableRuleProperty ( defaultValue : true ) ]
4141 public bool CheckOpenParen { get ; protected set ; }
4242
43+ [ ConfigurableRuleProperty ( defaultValue : true ) ]
44+ public bool CheckOperator { get ; protected set ; }
4345
4446 public override void ConfigureRule ( IDictionary < string , object > paramValueMap )
4547 {
@@ -53,6 +55,11 @@ public override void ConfigureRule(IDictionary<string, object> paramValueMap)
5355 {
5456 violationFinders . Add ( FindOpenParenViolations ) ;
5557 }
58+
59+ if ( CheckOperator )
60+ {
61+ violationFinders . Add ( FindOperatorViolations ) ;
62+ }
5663 }
5764
5865 /// <summary>
@@ -84,6 +91,8 @@ private string GetError(ErrorKind kind)
8491 {
8592 case ErrorKind . Brace :
8693 return string . Format ( CultureInfo . CurrentCulture , Strings . UseWhitespaceErrorBeforeBrace ) ;
94+ case ErrorKind . Operator :
95+ return string . Format ( CultureInfo . CurrentCulture , Strings . UseWhitespaceErrorOperator ) ;
8796 default :
8897 return string . Format ( CultureInfo . CurrentCulture , Strings . UseWhitespaceErrorBeforeParen ) ;
8998 }
@@ -149,6 +158,49 @@ private bool IsPreviousTokenApartByWhitespace(LinkedListNode<Token> tokenNode)
149158 ( tokenNode . Value . Extent . StartColumnNumber - tokenNode . Previous . Value . Extent . EndColumnNumber ) ;
150159 }
151160
161+ private IEnumerable < DiagnosticRecord > FindOperatorViolations ( TokenOperations tokenOperations )
162+ {
163+ foreach ( var tokenNode in tokenOperations . GetTokenNodes ( IsOperator ) )
164+ {
165+ var hasWhitespaceBefore = false ;
166+ var hasWhitespaceAfter = false ;
167+ if ( tokenNode . Previous != null
168+ && IsPreviousTokenOnSameLine ( tokenNode )
169+ && IsPreviousTokenApartByWhitespace ( tokenNode ) )
170+ {
171+ hasWhitespaceBefore = true ;
172+ }
173+
174+ if ( tokenNode . Next != null
175+ && IsPreviousTokenOnSameLine ( tokenNode . Next )
176+ && IsPreviousTokenApartByWhitespace ( tokenNode . Next ) )
177+ {
178+ hasWhitespaceAfter = true ;
179+ }
180+
181+ if ( ! hasWhitespaceAfter || ! hasWhitespaceBefore )
182+ {
183+ yield return new DiagnosticRecord (
184+ GetError ( ErrorKind . Operator ) ,
185+ tokenNode . Value . Extent ,
186+ GetName ( ) ,
187+ GetDiagnosticSeverity ( ) ,
188+ tokenOperations . Ast . Extent . File ,
189+ null ,
190+ null ) ;
191+ }
192+ }
193+ }
194+
195+ private bool IsOperator ( Token token )
196+ {
197+ return TokenTraits . HasTrait ( token . Kind , TokenFlags . AssignmentOperator )
198+ || TokenTraits . HasTrait ( token . Kind , TokenFlags . BinaryPrecedenceAdd )
199+ || TokenTraits . HasTrait ( token . Kind , TokenFlags . BinaryPrecedenceMultiply )
200+ || token . Kind == TokenKind . AndAnd
201+ || token . Kind == TokenKind . OrOr ;
202+ }
203+
152204 private bool IsPreviousTokenOnSameLine ( LinkedListNode < Token > lparen )
153205 {
154206 return lparen . Previous . Value . Extent . StartLineNumber == lparen . Value . Extent . EndLineNumber ;
0 commit comments