Skip to content

Commit 7e60fb3

Browse files
committed
fix: Formatting misses certain keywords (unary operators, 'until')
1 parent ea70855 commit 7e60fb3

File tree

2 files changed

+278
-20
lines changed

2 files changed

+278
-20
lines changed

Rules/UseConsistentWhitespace.cs

Lines changed: 106 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2222
#endif
2323
public class UseConsistentWhitespace : ConfigurableRule
2424
{
25-
private enum ErrorKind { BeforeOpeningBrace, Paren, Operator, SeparatorComma, SeparatorSemi,
26-
AfterOpeningBrace, BeforeClosingBrace, BeforePipe, AfterPipe, BetweenParameter };
25+
private enum ErrorKind
26+
{
27+
BeforeOpeningBrace, Paren, Operator, SeparatorComma, SeparatorSemi,
28+
AfterOpeningBrace, BeforeClosingBrace, BeforePipe, AfterPipe, BetweenParameter
29+
};
30+
2731
private const int whiteSpaceSize = 1;
2832
private const string whiteSpace = " ";
2933
private readonly SortedSet<TokenKind> openParenKeywordAllowList = new SortedSet<TokenKind>()
@@ -33,7 +37,9 @@ private enum ErrorKind { BeforeOpeningBrace, Paren, Operator, SeparatorComma, Se
3337
TokenKind.Switch,
3438
TokenKind.For,
3539
TokenKind.Foreach,
36-
TokenKind.While
40+
TokenKind.While,
41+
TokenKind.Until,
42+
TokenKind.Do
3743
};
3844

3945
private List<Func<TokenOperations, IEnumerable<DiagnosticRecord>>> violationFinders
@@ -72,6 +78,7 @@ public override void ConfigureRule(IDictionary<string, object> paramValueMap)
7278
if (CheckOpenBrace)
7379
{
7480
violationFinders.Add(FindOpenBraceViolations);
81+
violationFinders.Add(FindKeywordAfterBraceViolations);
7582
}
7683

7784
if (CheckInnerBrace)
@@ -194,6 +201,7 @@ private bool IsOperator(Token token)
194201
return TokenTraits.HasTrait(token.Kind, TokenFlags.AssignmentOperator)
195202
|| TokenTraits.HasTrait(token.Kind, TokenFlags.BinaryPrecedenceAdd)
196203
|| TokenTraits.HasTrait(token.Kind, TokenFlags.BinaryPrecedenceMultiply)
204+
|| TokenTraits.HasTrait(token.Kind, TokenFlags.UnaryOperator)
197205
|| token.Kind == TokenKind.AndAnd
198206
|| token.Kind == TokenKind.OrOr;
199207
}
@@ -229,7 +237,6 @@ private IEnumerable<DiagnosticRecord> FindOpenBraceViolations(TokenOperations to
229237
{
230238
foreach (var lcurly in tokenOperations.GetTokenNodes(TokenKind.LCurly))
231239
{
232-
233240
if (lcurly.Previous == null
234241
|| !IsPreviousTokenOnSameLine(lcurly)
235242
|| lcurly.Previous.Value.Kind == TokenKind.LCurly
@@ -239,11 +246,28 @@ private IEnumerable<DiagnosticRecord> FindOpenBraceViolations(TokenOperations to
239246
continue;
240247
}
241248

249+
if (lcurly.Previous.Value.Kind == TokenKind.RCurly && lcurly.Previous.Previous != null)
250+
{
251+
var keywordBeforeBrace = lcurly.Previous.Previous.Value;
252+
if (IsKeyword(keywordBeforeBrace) && !IsPreviousTokenApartByWhitespace(lcurly.Previous))
253+
{
254+
yield return new DiagnosticRecord(
255+
GetError(ErrorKind.BeforeOpeningBrace),
256+
lcurly.Previous.Value.Extent,
257+
GetName(),
258+
GetDiagnosticSeverity(),
259+
tokenOperations.Ast.Extent.File,
260+
null,
261+
GetCorrections(keywordBeforeBrace, lcurly.Previous.Value, lcurly.Value, false, true).ToList());
262+
}
263+
continue;
264+
}
265+
242266
if (IsPreviousTokenApartByWhitespace(lcurly) || IsPreviousTokenLParen(lcurly))
243267
{
244268
continue;
245269
}
246-
270+
247271
yield return new DiagnosticRecord(
248272
GetError(ErrorKind.BeforeOpeningBrace),
249273
lcurly.Value.Extent,
@@ -255,6 +279,46 @@ private IEnumerable<DiagnosticRecord> FindOpenBraceViolations(TokenOperations to
255279
}
256280
}
257281

282+
private IEnumerable<DiagnosticRecord> FindKeywordAfterBraceViolations(TokenOperations tokenOperations)
283+
{
284+
foreach (var keywordNode in tokenOperations.GetTokenNodes(IsKeyword))
285+
{
286+
var keyword = keywordNode.Value;
287+
288+
if (keywordNode.Previous != null)
289+
{
290+
if (keywordNode.Previous.Value.Kind == TokenKind.RCurly &&
291+
IsPreviousTokenOnSameLine(keywordNode))
292+
{
293+
var hasWhitespace = IsPreviousTokenApartByWhitespace(keywordNode);
294+
295+
if (!hasWhitespace)
296+
{
297+
var corrections = new List<CorrectionExtent>
298+
{
299+
new CorrectionExtent(
300+
keywordNode.Previous.Value.Extent.EndLineNumber,
301+
keyword.Extent.StartLineNumber,
302+
keywordNode.Previous.Value.Extent.EndColumnNumber,
303+
keyword.Extent.StartColumnNumber,
304+
" ",
305+
keyword.Extent.File)
306+
};
307+
308+
yield return new DiagnosticRecord(
309+
GetError(ErrorKind.BeforeOpeningBrace),
310+
keyword.Extent,
311+
GetName(),
312+
GetDiagnosticSeverity(),
313+
tokenOperations.Ast.Extent.File,
314+
null,
315+
corrections);
316+
}
317+
}
318+
}
319+
}
320+
}
321+
258322
private IEnumerable<DiagnosticRecord> FindInnerBraceViolations(TokenOperations tokenOperations)
259323
{
260324
foreach (var lCurly in tokenOperations.GetTokenNodes(TokenKind.LCurly))
@@ -509,7 +573,7 @@ private static bool IsPreviousTokenApartByWhitespace(LinkedListNode<Token> token
509573
hasRedundantWhitespace = actualWhitespaceSize - whiteSpaceSize > 0;
510574
return whiteSpaceSize == actualWhitespaceSize;
511575
}
512-
576+
513577
private static bool IsPreviousTokenLParen(LinkedListNode<Token> tokenNode)
514578
{
515579
return tokenNode.Previous.Value.Kind == TokenKind.LParen;
@@ -536,17 +600,30 @@ private IEnumerable<DiagnosticRecord> FindOperatorViolations(TokenOperations tok
536600
{
537601
foreach (var tokenNode in tokenOperations.GetTokenNodes(IsOperator))
538602
{
539-
if (tokenNode.Previous == null
540-
|| tokenNode.Next == null
541-
|| tokenNode.Value.Kind == TokenKind.DotDot)
603+
var token = tokenNode.Value;
604+
605+
if (tokenNode.Previous == null || tokenNode.Next == null || token.Kind == TokenKind.DotDot)
542606
{
543607
continue;
544608
}
545609

546-
// exclude unary operator for cases like $foo.bar(-$Var)
547-
if (TokenTraits.HasTrait(tokenNode.Value.Kind, TokenFlags.UnaryOperator) &&
548-
tokenNode.Previous.Value.Kind == TokenKind.LParen &&
549-
tokenNode.Next.Value.Kind == TokenKind.Variable)
610+
// Check unary operator handling
611+
bool isUnaryInMethodCall = false;
612+
if (TokenTraits.HasTrait(token.Kind, TokenFlags.UnaryOperator))
613+
{
614+
// Only skip if it's a unary operator in a method call like $foo.bar(-$var)
615+
if (tokenNode.Previous.Value.Kind == TokenKind.LParen &&
616+
tokenNode.Next.Value.Kind == TokenKind.Variable &&
617+
tokenNode.Previous.Previous != null)
618+
{
619+
var beforeLParen = tokenNode.Previous.Previous.Value;
620+
621+
isUnaryInMethodCall = beforeLParen.Kind == TokenKind.Dot ||
622+
(beforeLParen.TokenFlags & TokenFlags.MemberName) == TokenFlags.MemberName;
623+
}
624+
}
625+
626+
if (isUnaryInMethodCall)
550627
{
551628
continue;
552629
}
@@ -561,22 +638,30 @@ private IEnumerable<DiagnosticRecord> FindOperatorViolations(TokenOperations tok
561638
}
562639
}
563640

641+
// Check whitespace
564642
var hasWhitespaceBefore = IsPreviousTokenOnSameLineAndApartByWhitespace(tokenNode);
565-
var hasWhitespaceAfter = tokenNode.Next.Value.Kind == TokenKind.NewLine
566-
|| IsPreviousTokenOnSameLineAndApartByWhitespace(tokenNode.Next);
643+
var hasWhitespaceAfter = tokenNode.Next.Value.Kind == TokenKind.NewLine ||
644+
IsPreviousTokenOnSameLineAndApartByWhitespace(tokenNode.Next);
645+
646+
// Special case: Don't require space before unary operator if preceded by LParen
647+
if (TokenTraits.HasTrait(token.Kind, TokenFlags.UnaryOperator) &&
648+
tokenNode.Previous.Value.Kind == TokenKind.LParen)
649+
{
650+
hasWhitespaceBefore = true;
651+
}
567652

568653
if (!hasWhitespaceAfter || !hasWhitespaceBefore)
569654
{
570655
yield return new DiagnosticRecord(
571656
GetError(ErrorKind.Operator),
572-
tokenNode.Value.Extent,
657+
token.Extent,
573658
GetName(),
574659
GetDiagnosticSeverity(),
575660
tokenOperations.Ast.Extent.File,
576661
null,
577662
GetCorrections(
578663
tokenNode.Previous.Value,
579-
tokenNode.Value,
664+
token,
580665
tokenNode.Next.Value,
581666
hasWhitespaceBefore,
582667
hasWhitespaceAfter));
@@ -614,7 +699,9 @@ private List<CorrectionExtent> GetCorrections(
614699

615700
var extent = new ScriptExtent(
616701
new ScriptPosition(e1.File, e1.EndLineNumber, e1.EndColumnNumber, null),
617-
new ScriptPosition(e2.File, e2.StartLineNumber, e2.StartColumnNumber, null));
702+
new ScriptPosition(e2.File, e2.StartLineNumber, e2.StartColumnNumber, null)
703+
);
704+
618705
return new List<CorrectionExtent>()
619706
{
620707
new CorrectionExtent(
@@ -630,6 +717,5 @@ private static bool IsPreviousTokenOnSameLine(LinkedListNode<Token> lparen)
630717
{
631718
return lparen.Previous.Value.Extent.EndLineNumber == lparen.Value.Extent.StartLineNumber;
632719
}
633-
634720
}
635-
}
721+
}

0 commit comments

Comments
 (0)