Skip to content

Warn when an if statement contains an assignment #859

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

Conversation

bergmeister
Copy link
Collaborator

@bergmeister bergmeister commented Jan 28, 2018

Closes #809
This is a simple prototype that checks if there is an assignment ast inside an if statement.
It currently works and catches cases like if( ($a = $b_){} and if( ($a = $b_){} but also warns if the execution block of the if statement contains an assignment like e.g. in if ($a -eq $b){$a=$b}. This needs to be fixed and is being tracked by the currently failing test.
The idea is to keep it simple to avoid false positives. For example the rule does not catch cases where there is an evalution inside the if statemenet as e.g. in if ( ($a == $b) ){}

Copy link
Contributor

@JamesWTruher JamesWTruher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is quite ready yet. There are a couple of problems which should be addressed first. There is a pattern which is very useful which will cause warnings to appear:

if ( $files = get-childitem ) {
... do something ...
}
else {
... do something else ...
}

I think if you can be more specific about the test of 2 variables in the ifstatementast (which should help you address the issue you found in the assignment outside of the test.

@bergmeister
Copy link
Collaborator Author

bergmeister commented Jan 29, 2018

Hmm, such a case was discussed in the issue as well but my thinking was that the helpfulness of the warning makes up for those cases where the warning can be ignored. But you have a fair point, I will have a think if it is possible to get more certainty about the use case of the assignment operator.
But thanks for the feedback, that's exactly why I made a WIP PR because it is better to fail early. :-)
When looking at the warning level, I was not really satisfied with the 3 currently available levels (error, warning, info). Maybe if we had a level that addresses smells or possibe problems, then a user could do a thorough scan of the script e.g. for new code but has to review each warning individually but only require e.g. info level to pass in CI. A good example of warnings where the user has to use its own intelligence on deciding whether the warning can be suppressed or not is the Possible multiple enumeration of IEnumerable warning of ReSharper where ReSharper cannot figure out how an IEnumerable is being used. What are your thoughts on this?

@JamesWTruher
Copy link
Contributor

The IsStatementAst has the Clauses member which should help. Here's a simple example in script:

using namespace System.Management.Automation.Language
param ( [scriptblock]$scriptblock = { if ( $a = $b ) { "the same" } } )
$If = $scriptblock.Ast.Find({$args[0] -is [IfStatementAst]}, $false)
if ( ! $If ) { write-verbose -verbose "no if" ; return }
foreach ( $clause in $If.Clauses ) {
    $assignment = $clause.Item1.Find({$args[0] -is [assignmentstatementast]}, $true)
    if ( $assignment ) {
        $msg =  ("Checking " + $assignment)
        if ( $assignment.Right.Expression -is [variableexpressionast] ) {
            Write-Error "${msg} : Don't assign a variable to a variable"
        }
        else {
            Write-Verbose -verbose "${msg} : Is OK!"
        }
    }
    else {
        Write-Verbose -verbose ("${msg} : no assignment in '" + $clause.item1 + "'")
    
}

when run, you can see what happens!

PS> /tmp/ast1 -scriptblock { if ( $a = get-date ) { 1 } }
VERBOSE: Checking $a = get-date : Is OK!
PS> /tmp/ast1 -scriptblock { if ( $a = $b ) { 1 } }
/tmp/ast1.ps1 : Checking $a = $b : Don't assign a variable to a variable
At line:1 char:1
+ /tmp/ast1 -scriptblock { if ( $a = $b ) { 1 } }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,ast1.ps1

PS> /tmp/ast1 -scriptblock { if ( $a = get-date ) { 1 } elseif ( $b = $c ) { 2 } else { 3 } }
VERBOSE: Checking $a = get-date : Is OK!
/tmp/ast1.ps1 : Checking $b = $c : Don't assign a variable to a variable
At line:1 char:1
+ /tmp/ast1 -scriptblock { if ( $a = get-date ) { 1 } elseif ( $b = $c  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,ast1.ps1

…e sure that the following case does not get flagged up (added a test case for it) 'if ( $files = get-childitem ) { }'
@bergmeister
Copy link
Collaborator Author

bergmeister commented Feb 1, 2018

Thanks for the pointers. I improved it but still need to make sure that the case which you pointed out does not get flagged up (I added a test for it in a TDD manner). I will let you know once the PR is ready for re-review.

@bergmeister
Copy link
Collaborator Author

bergmeister commented Feb 3, 2018

@JamesWTruher : The PR is ready for review now. I have improved it to not flag false positives like if ($a = (Get-ChildItem)){} and as far as I can see I can detect all positives cases of using = or == inside if statements. Also please feel free to comment on the rule name and the wording of the warning strings.

@bergmeister bergmeister changed the title WIP: Warn when an if statement contains an assignment Warn when an if statement contains an assignment Feb 3, 2018
{
// Check if someone used '==', which can easily happen when the person is used to coding a lot in C#.
// In most cases, this will be a runtime error because PowerShell will look for a cmdlet name starting with '=', which is technically possible to define
if (assignmentStatementAst.Right.Extent.Text.StartsWith("="))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will miss the case of
$a = = $b
but that's probably ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rule to warn against use of equal sign when comparator (-eq) should be used
2 participants