Skip to content

Commit 759c712

Browse files
committed
Begin implementation of visitor to find token in AST
1 parent b9aa6e7 commit 759c712

File tree

3 files changed

+104
-16
lines changed

3 files changed

+104
-16
lines changed

Engine/TokenOperations.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,5 +232,85 @@ private bool OnSameLine(Token token1, Token token2)
232232
{
233233
return token1.Extent.StartLineNumber == token2.Extent.EndLineNumber;
234234
}
235+
236+
/// <summary>
237+
/// Finds the position of a given token in the AST.
238+
/// </summary>
239+
/// <param name="token">The <see cref="Token"/> to search for.</param>
240+
/// <returns>The Ast node directly containing the provided <see cref="Token"/>.</returns>
241+
public Ast GetAstPosition(Token token)
242+
{
243+
FindAstPostitionVisitor findAstVisitor = new FindAstPostitionVisitor(token.Extent.StartScriptPosition);
244+
ast.Visit(findAstVisitor);
245+
return findAstVisitor.AstPosition;
246+
}
247+
248+
}
249+
250+
/// <summary>
251+
/// Provides an efficient way to find the position in the AST corresponding to a given script position.
252+
/// </summary>
253+
public class FindAstPostitionVisitor : AstVisitor2
254+
{
255+
private IScriptPosition searchPosition;
256+
257+
/// <summary>
258+
/// Contains the position in the AST corresponding to the provided <see cref="IScriptPosition"/> upon completion of the <see cref="Ast.Visit(AstVisitor)"/> method.
259+
/// </summary>
260+
public Ast AstPosition { get; private set; }
261+
262+
/// <summary>
263+
/// Initializes a new instance of the <see cref="FindAstPostitionVisitor"/> class with the postition to search for.
264+
/// </summary>
265+
/// <param name="position">The script position to search for.</param>
266+
public FindAstPostitionVisitor(IScriptPosition position)
267+
{
268+
this.searchPosition = position;
269+
}
270+
271+
/// <summary>
272+
/// Traverses the AST based on offests to find the leaf node which contains the provided <see cref="IScriptPosition"/>.
273+
/// This method implements the entire functionality of this visitor. All <see cref="AstVisitor2"/> methods are overridden to simply invoke this one.
274+
/// </summary>
275+
/// <param name="ast">Current AST node to process.</param>
276+
/// <returns>An <see cref="AstVisitAction"/> indicating whether to visit children of the current node.</returns>
277+
private AstVisitAction Visit(Ast ast)
278+
{
279+
if (ast.Extent.StartOffset > searchPosition.Offset || ast.Extent.EndOffset < searchPosition.Offset)
280+
{
281+
return AstVisitAction.SkipChildren;
282+
}
283+
else
284+
{
285+
AstPosition = ast;
286+
return AstVisitAction.Continue;
287+
}
288+
}
289+
290+
public override AstVisitAction VisitScriptBlock(ScriptBlockAst scriptBlockAst)
291+
{
292+
return Visit(scriptBlockAst);
293+
}
294+
295+
public override AstVisitAction VisitNamedBlock(NamedBlockAst namedBlockAst)
296+
{
297+
return Visit(namedBlockAst);
298+
}
299+
300+
public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst)
301+
{
302+
return Visit(assignmentStatementAst);
303+
}
304+
305+
public override AstVisitAction VisitCommandExpression(CommandExpressionAst commandExpressionAst)
306+
{
307+
return Visit(commandExpressionAst);
308+
}
309+
310+
public override AstVisitAction VisitHashtable(HashtableAst hashtableAst)
311+
{
312+
return Visit(hashtableAst);
313+
}
314+
235315
}
236316
}

Rules/UseConsistentWhitespace.cs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -536,23 +536,10 @@ private IEnumerable<DiagnosticRecord> FindOperatorViolations(TokenOperations tok
536536
// exclude assignment operator inside of multi-line hash tables if requested
537537
if (IgnoreAssignmentOperatorInsideHashTable && tokenNode.Value.Kind == TokenKind.Equals)
538538
{
539-
var enclosingHashTables = tokenOperations.Ast.FindAll(a => a.Extent.StartOffset <= tokenNode.Value.Extent.StartOffset && a.Extent.EndOffset >= tokenNode.Value.Extent.EndOffset && a is HashtableAst, true);
540-
if (enclosingHashTables.Count() > 0)
539+
Ast containingAst = tokenOperations.GetAstPosition(tokenNode.Value);
540+
if (containingAst is HashtableAst && containingAst.Extent.EndLineNumber != containingAst.Extent.StartLineNumber)
541541
{
542-
Ast innermostEnclosingHashTable = enclosingHashTables.First();
543-
int smallestSizeSoFar = int.MaxValue;
544-
foreach (var hashTable in enclosingHashTables){
545-
int currentHashTableSize = hashTable.Extent.EndOffset - hashTable.Extent.StartOffset;
546-
if (currentHashTableSize < smallestSizeSoFar)
547-
{
548-
innermostEnclosingHashTable = hashTable;
549-
smallestSizeSoFar = currentHashTableSize;
550-
}
551-
}
552-
if (innermostEnclosingHashTable.Extent.StartLineNumber != innermostEnclosingHashTable.Extent.EndLineNumber)
553-
{
554-
continue;
555-
}
542+
continue;
556543
}
557544
}
558545

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
Describe "TokenOperations" {
5+
It "Should return correct AST position for assignment operator in hash table" {
6+
$scriptText = @'
7+
$h = @{
8+
a = 72
9+
b = @{ z = "hi" }
10+
}
11+
'@
12+
$tokens = $null
13+
$parseErrors = $null
14+
$scriptAst = [System.Management.Automation.Language.Parser]::ParseInput($scriptText, [ref] $tokens, [ref] $parseErrors)
15+
$tokenOperations = New-Object Microsoft.Windows.PowerShell.ScriptAnalyzer.TokenOperations -ArgumentList @($tokens, $scriptAst)
16+
$operatorToken = $tokens | Where-Object { $_.Extent.StartOffset -eq 32 }
17+
$hashTableAst = $tokenOperations.GetAstPosition($operatorToken)
18+
$hashTableAst | Should -BeOfType [System.Management.Automation.Language.HashTableAst]
19+
$hashTableAst.Extent.Text | Should -Be '@{ z = "hi" }'
20+
}
21+
}

0 commit comments

Comments
 (0)