X Tutup
The Wayback Machine - https://web.archive.org/web/20200918095610/https://github.com/PowerShell/PSScriptAnalyzer/pull/1566
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UseConsistentWhitespace - Create option to ignore assignment operator inside hash table #1566

Open
wants to merge 6 commits into
base: master
from

Conversation

@daviesj
Copy link

daviesj commented Aug 7, 2020

PR Summary

Fix #769 instead of creating a workaround. This creates an option on the UseConsistentWhitespace rule to ignore the assignment operator inside a multi-line hash table, making it compatible with the AlignAssignmentStatement rule. Also this enables the new option in the code formatting settings presets since they all enable the AlignAssignmentStatement rule. If I were a bit smarter, I would also automatically enable this option if AlignAssignmentStatement is enabled but I do not see how to do that.

PR Checklist`

@microsoft-cla
Copy link

microsoft-cla bot commented Aug 7, 2020

CLA assistant check
All CLA requirements met.

Enable = $true
CheckInnerBrace = $true
CheckOpenBrace = $true
CheckOpenParen = $true
CheckOperator = $true
CheckPipe = $true
CheckPipeForRedundantWhitespace = $false
CheckSeparator = $true
CheckParameter = $false
IgnoreAssignmentOperatorInsideHashTable = $true
Comment on lines 34 to 43

This comment has been minimized.

@rjmholt

rjmholt Aug 7, 2020 Member

Please revert the changes to the other lines here

// exclude assignment operator inside of multi-line hash tables if requested
if (IgnoreAssignmentOperatorInsideHashTable && tokenNode.Value.Kind == TokenKind.Equals)
{
var enclosingHashTables = tokenOperations.Ast.FindAll(a => a.Extent.StartOffset <= tokenNode.Value.Extent.StartOffset && a.Extent.EndOffset >= tokenNode.Value.Extent.EndOffset && a is HashtableAst, true);

This comment has been minimized.

@rjmholt

rjmholt Aug 7, 2020 Member

Please use explicit type here rather than var

This comment has been minimized.

@rjmholt

rjmholt Aug 7, 2020 Member

Please label the true parameter

{
Ast innermostEnclosingHashTable = enclosingHashTables.First();
int smallestSizeSoFar = int.MaxValue;
foreach (var hashTable in enclosingHashTables){

This comment has been minimized.

@rjmholt

rjmholt Aug 7, 2020 Member

Please use explicit type here rather than var

innermostEnclosingHashTable = hashTable;
smallestSizeSoFar = currentHashTableSize;
}
}

This comment has been minimized.

@rjmholt

rjmholt Aug 7, 2020 Member

Suggested change
}
}
if (IgnoreAssignmentOperatorInsideHashTable && tokenNode.Value.Kind == TokenKind.Equals)
{
var enclosingHashTables = tokenOperations.Ast.FindAll(a => a.Extent.StartOffset <= tokenNode.Value.Extent.StartOffset && a.Extent.EndOffset >= tokenNode.Value.Extent.EndOffset && a is HashtableAst, true);
if (enclosingHashTables.Count() > 0)
{
Ast innermostEnclosingHashTable = enclosingHashTables.First();
int smallestSizeSoFar = int.MaxValue;
foreach (var hashTable in enclosingHashTables){
int currentHashTableSize = hashTable.Extent.EndOffset - hashTable.Extent.StartOffset;
if (currentHashTableSize < smallestSizeSoFar)
{
innermostEnclosingHashTable = hashTable;
smallestSizeSoFar = currentHashTableSize;
}
}
if (innermostEnclosingHashTable.Extent.StartLineNumber != innermostEnclosingHashTable.Extent.EndLineNumber)
{
continue;
}
}
}
Comment on lines 537 to 557

This comment has been minimized.

@rjmholt

rjmholt Aug 7, 2020 Member

So with this strategy, we collect all the hashtable ASTs that contain the token offset, and then try to find the smallest one. That works, but I think a better strategy is to:

  • Find the smallest AST that contains the token offset
  • Test whether that AST is a HashtableAst (since the = in HashtableAst key-value pairs is directly subordinate to that AST)

That way there's no need to sift through multiple ASTs reflecting on them to work out whether they're HashtableAsts, only the one containing the = operator.

One way to find the smallest containing AST is to do as you've done with the FindAll() method and sift through the returned ASTs looking for the one with the smallest extent. A slightly more efficient way is to implement a visitor like the one in PowerShell/vscode-powershell#2860 (comment).

@daviesj daviesj force-pushed the daviesj:master branch from 4d1b67d to b9aa6e7 Aug 10, 2020
@daviesj daviesj changed the title UseConsistentWhitespace - Create option to ignore assignment operator inside hash table WIP: UseConsistentWhitespace - Create option to ignore assignment operator inside hash table Aug 11, 2020
/// <summary>
/// Provides an efficient way to find the position in the AST corresponding to a given script position.
/// </summary>
public class FindAstPostitionVisitor : AstVisitor2

This comment has been minimized.

@rjmholt

rjmholt Aug 11, 2020 Member

This class should be in its own file and should probably be internal

This comment has been minimized.

@daviesj

daviesj Aug 11, 2020 Author

Wow you are quick. Was just going to add a comment asking for a bit of direction but I am working on about 6 different things today.

Wasn't sure about the following:

  • Whether I had put the visitor in the right spot in the code. The structure and conventions of this repository are a bit hard for me to puzzle out. Will go ahead put this in a separate file.
  • How much testing is needed for the visitor functionality? I only created one test that it does what I need for the UseConsistentWhitespace rule. Seems to me that adding a generic function like this with as much code as it is going to be should have more tests than that but no idea where to go with that.

This comment has been minimized.

@rjmholt

rjmholt Aug 11, 2020 Member

but I am working on about 6 different things today.

No worries! There's no rush at all -- feel free to take your time 🙂

The structure and conventions of this repository are a bit hard for me to puzzle out.

Agreed, they're not great. Almost anywhere in Engine should be fine. We can work out where specifically before merging.

How much testing is needed for the visitor functionality? I only created one test that it does what I need for the UseConsistentWhitespace rule. Seems to me that adding a generic function like this with as much code as it is going to be should have more tests than that but no idea where to go with that.

Yeah, ideally if it's a general purpose visitor it should have a bunch of tests, but all our testing is in Pester today which makes that harder. For now, if it's only used in the one rule, it should be enough to make sure that rule is thoroughly tested

public override AstVisitAction VisitScriptBlock(ScriptBlockAst scriptBlockAst)
{
return Visit(scriptBlockAst);
}

public override AstVisitAction VisitNamedBlock(NamedBlockAst namedBlockAst)
{
return Visit(namedBlockAst);
}

public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst)
{
return Visit(assignmentStatementAst);
}

public override AstVisitAction VisitCommandExpression(CommandExpressionAst commandExpressionAst)
{
return Visit(commandExpressionAst);
}

public override AstVisitAction VisitHashtable(HashtableAst hashtableAst)
{
return Visit(hashtableAst);
}
Comment on lines 290 to 313

This comment has been minimized.

@rjmholt

rjmholt Aug 11, 2020 Member

Given its general name, this visitor should probably have overrides for all AST types so that it will reliably work for all position visits. The alternative is to implement this class as a private nested class for the relevant rule.

This comment has been minimized.

@daviesj

daviesj Aug 11, 2020 Author

Fully intend to implement all overrides. Just didn't want to produce that much code before I knew I was barking up the right tree. It flummoxed me a bit when I first decided to work on this that there is no good way to search a PowerShell AST based on script position without writing 200+ lines of code. That is why I had tried the simpler solution first (that I agree with you was not ideal by a long shot).

This comment has been minimized.

@rjmholt

rjmholt Aug 11, 2020 Member

Well there is another way, which is to use FindAll() instead:

IEnumerable<Ast> astsContainingToken = scriptAst.FindAll(foundAst => ContainsTokenExtent(token, foundAst), /* search through nested ASTs -- I can't remember the correct parameter name */ true);

// Find the containing AST with the smallest extent
Ast smallestContainingAst = astsContainingToken.First();
foreach (Ast containingAst in astsContainingToken)
{
    if (HasSmallerExtent(smallestContainingAst, containingAst))
    {
        smallestContainingAst = containingAst;
    }
}

Naturally you'd need to write the methods I've used there, but they basically contain logic you've already written

This comment has been minimized.

@daviesj

daviesj Aug 12, 2020 Author

I did try something like that but ran into a problem because apparently Ast's can contain another of exactly the same size so there isn't always a smallest. At that point I figured as many issues as I have run into so far trying to use FindAll(), it would be better just to go the visitor route.

@rjmholt rjmholt marked this pull request as draft Aug 12, 2020
@daviesj
Copy link
Author

daviesj commented Aug 12, 2020

Not sure if I have the preprocessor version directives just right in FindAstPositionVisitor.cs. I was puzzled that there isn't a PSV5 constant and building for PowerShell 4 sets both PSV3 and PSV4.

daviesj added 2 commits Aug 12, 2020
@daviesj daviesj force-pushed the daviesj:master branch from 54351c0 to 94329b0 Aug 25, 2020
@daviesj daviesj changed the title WIP: UseConsistentWhitespace - Create option to ignore assignment operator inside hash table UseConsistentWhitespace - Create option to ignore assignment operator inside hash table Sep 16, 2020
@daviesj daviesj marked this pull request as ready for review Sep 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

2 participants
You can’t perform that action at this time.
X Tutup