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

PSScriptAnalyzer fixes by category #4261

Merged

Conversation

bergmeister
Copy link
Contributor

This is the updated version of PR #4213, in which it was requested to split the commits on a per kind category.
The intend is to help with fixing #3791. The fixed PSScriptAnalyzer warnings are PSPossibleIncorrectComparisonWithNull and PSAvoidUsingCmdletAliases for ForEach-Object, Where-Object and Select-Object since those warning types were responsible for the majority of warnings.

…thNull. Essentially, $null has to be on the left hand side when using it for comparison.

A Test in ParameterBinding.Tests.ps1 needed adapting as this test used to rely on the wrong null comparison
It was also suggested in the initial PR PowerShell#4205 to replace a subset of tests of kind '($object -eq $null) | Should Be $true' with '($null -eq $object) | Should BeNullOrEmpty'
@msftclas
Copy link

@bergmeister,
Thanks for having already signed the Contribution License Agreement. Your agreement was validated by Microsoft. We will now review your pull request.
Thanks,
Microsoft Pull Request Bot

@daxian-dbw
Copy link
Member

@bergmeister Thank you so much for taking extra effort splitting the original PR by the fix categories!

build.psm1 Outdated
@@ -668,7 +668,7 @@ function Get-PesterTag {
$alltags = @{}
$warnings = @()

get-childitem -Recurse $testbase -File |?{$_.name -match "tests.ps1"}| ForEach-Object {
get-childitem -Recurse $testbase -File |Where-Object {$_.name -match "tests.ps1"}| ForEach-Object {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add a space before Where-Object and after "tests.ps1"}

@@ -2,7 +2,7 @@ Import-Module $PSScriptRoot/Apache/Apache.psm1

#list Apache Modules
Write-Host -Foreground Blue "Get installed Apache Modules like *proxy* and Sort by name"
Get-ApacheModule |Where {$_.ModuleName -like "*proxy*"}|Sort-Object ModuleName | Out-Host
Get-ApacheModule | Where-Object {$_.ModuleName -like "*proxy*"}|Sort-Object ModuleName | Out-Host
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add spaces in "*proxy*"}|Sort-Object.

{{
$script:PSSession = $PSSession

if ($createdByModule -and ($script:PSSession -ne $null))
if ($null -ne $createdByModule -and ($script:PSSession))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please correct.

@@ -417,7 +417,7 @@ Describe "Proxy module is usable when the original runspace is no longer around"

It "Verifies Remove-Module removed the runspace that was automatically created" -Pending {
Remove-Module $module -Force
((Get-PSSession -InstanceId $internalSession.InstanceId -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
($null -eq (Get-PSSession -InstanceId $internalSession.InstanceId -ErrorAction SilentlyContinue)) | Should Be $true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please change to Should BeNullOrEmpty.

@@ -452,7 +452,7 @@ Describe "Proxy module is usable when the original runspace is no longer around"
# removing the module should remove the implicitly/magically created runspace
It "Verifies Remove-Module removes automatically created runspace" -Pending {
Remove-Module $module -Force
((Get-PSSession -InstanceId $internalSession.InstanceId -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
($null -eq (Get-PSSession -InstanceId $internalSession.InstanceId -ErrorAction SilentlyContinue)) | Should Be $true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please change to Should BeNullOrEmpty.

@@ -767,7 +767,7 @@ Describe "Import-PSSession functional tests" -tags "Feature" {
}

It "Helper functions should not be imported" -Skip:$skipTest {
((Get-Item function:*PSImplicitRemoting* -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
($null -eq (Get-Item function:*PSImplicitRemoting* -ErrorAction SilentlyContinue)) | Should Be $true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please change to Should BeNullOrEmpty.

@@ -808,11 +808,11 @@ Describe "Import-PSSession functional tests" -tags "Feature" {
}

It "Temporary module should be automatically removed after runspace is closed" -Skip:$skipTest {
((Get-Module | Where-Object { $_.Path -eq $module.Path }) -eq $null) | Should Be $true
($null -eq (Get-Module | Where-Object { $_.Path -eq $module.Path })) | Should Be $true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please change to Should BeNullOrEmpty.

}

It "Temporary psm1 file should be automatically removed after runspace is closed" -Skip:$skipTest {
((Get-Item $module.Path -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
($null -eq (Get-Item $module.Path -ErrorAction SilentlyContinue)) | Should Be $true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please change to Should BeNullOrEmpty.

}

((Get-Item function:Get-MyVariable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
($null -eq (Get-Item function:Get-MyVariable -ErrorAction SilentlyContinue)) | Should Be $true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please change to Should BeNullOrEmpty.

@@ -117,9 +117,9 @@ Describe "SemanticVersion api tests" -Tags 'CI' {
$operand -eq $operand | Should Be $true
$operand -ne $operand | Should Be $false
$null -eq $operand | Should Be $false
$operand -eq $null | Should Be $false
$null -eq $operand | Should Be $false
$null -ne $operand | Should Be $true
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe it is special tests - please revert the changes in the file.

…riptAnalyzer fixes by category):

-Spacing in build.psm1 and apache-demo.ps1
-Correction of logical mistake in Implicit.Remoting.Tests.ps1
-Changing more tests from '$null -eq $object | Should Be $true' to '$object | Should BeNullOrEmpty'
-Revert a change initially made to SemanticVersion.Tests.ps1
@iSazonov
Copy link
Collaborator

LGTM.

@daxian-dbw
Copy link
Member

I don't think we should touch CSharp files and Markdown files. PSScriptAnalyzer warnings should be applicable only to powershell script files.

@iSazonov
Copy link
Collaborator

Should we fix CSharp files and Markdown files in separate PRs?

@daxian-dbw
Copy link
Member

daxian-dbw commented Jul 17, 2017

I don't think we need to make those changes in non-powershell-script files, since the goal is to avoid PSScriptAnalyzer warnings.
It's easy to revert changes in CSharp/Markdown files -- do a soft reset, and then cd into PowerShell\src and run git checkout .. It should reset all changes in PowerShell\src folder. And it's similar to Markdown files.

@bergmeister
Copy link
Contributor Author

@daxian-dbw If you say that the only goal is to avoid PSScriptAnalyzer warnings, then I'm not sure if you considered the reason why those rules exist. They are there best practices to avoid unintended/wrong behaviour and therefore apply to any file using PS syntax IMHO. PSPossibleIncorrectComparisonWithNull is the best example where it can result in e.g. an if condition that always evaluates to true. I can understand if you are concerned that in the case of PSPossibleIncorrectComparisonWithNull, changes in the code of the actual product could lead to a regression and should be excluded but in the case of PSAvoidUsingCmdletAliases, I do not see a reason as to why cs/md files should be excluded. WDYT?

@daxian-dbw
Copy link
Member

daxian-dbw commented Jul 20, 2017

@bergmeister Thanks for the comment, and you have very good points. Yes, my main concern about changing the .cs files is the potential regression. Given the size of changes, it could be easy for us to overlook something in the review. That being said, I think you are right that the scripts declared in C# code should follow the rules as well, but I would suggest that we leave the comments unchanged in C# code, especially those that still use other aliases.

As for the PSScriptAnalyzer rules, it's very useful to write easy-to-maintain and less error-prone PS script files, but it's not necessarily always applicable to any PSH syntax, for example, the script that serves as a one-liner command for you to run interactively, in which case aliases may be more preferred because they make the command concise and easy to type. The changes in KnownIssues.md would be a good example of it -- ls (gci *.txt | % name) is a command for a user to run interactively, and thus it's OK to keep the aliases. It's a similar idea for scripts in c# comments -- those simple scripts are only for demonstrating the scenarios, and thus it's OK to use the aliases to keep them concise.

@iSazonov
Copy link
Collaborator

Only we don't type samples from code and md files - we copy-paste 😄

@daxian-dbw
Copy link
Member

@iSazonov right :) my point is that for the one-liner scripts like those, it's OK to use aliases to keep them concise.
I will do a pass of review on the .cs files.

@@ -51,7 +51,7 @@ Removing the aliases exposes the native command experience to the PowerShell use

Currently, PowerShell only does wildcard expansion (globbing) for built-in cmdlets on Windows, and for external commands or binaries as well as cmdlets on Linux.
This means that a command like `ls *.txt` will fail because the asterisk will not be expanded to match file names.
You can work around this by doing `ls (gci *.txt | % name)` or, more simply, `gci *.txt` using the PowerShell built-in equivalent to `ls`.
You can work around this by doing `ls (gci *.txt | ForEach-Object name)` or, more simply, `gci *.txt` using the PowerShell built-in equivalent to `ls`.
Copy link
Member

Choose a reason for hiding this comment

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

As we discussed, this change is not necessary. This is a one-line script to be used interactively. It's OK to use aliases to keep it concise.

@@ -2138,9 +2138,9 @@ private Assembly LoadAssemblyHelper(string assemblyName)
// 10,000 iterations of this method takes ~800 ms for a string array, and ~ 200ms for the dictionary
//
// $dlls = ((dir c:\windows\Microsoft.NET\Framework\ -fi *.dll -rec) + (dir c:\windows\assembly -fi *.dll -rec)) + (dir C:\Windows\Microsoft.NET\assembly) |
// % { [Reflection.Assembly]::LoadFrom($_.FullName) }
// ForEach-Object { [Reflection.Assembly]::LoadFrom($_.FullName) }
Copy link
Member

Choose a reason for hiding this comment

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

These changes probably should be reverted, as dir is still used here and short form of parameters are also used such as -fi, -rec and -u. It's OK to use aliases in cases like this to make it concise.

@@ -1646,7 +1646,7 @@ internal static char SetDelimiter(PSCmdlet Cmdlet, string ParameterSetName, char
if (UseCulture == true)
{
// ListSeparator is apparently always a character even though the property returns a string, checked via:
// [CultureInfo]::GetCultures("AllCultures") | % { ([CultureInfo]($_.Name)).TextInfo.ListSeparator } | ? Length -ne 1
// [CultureInfo]::GetCultures("AllCultures") | ForEach-Object { ([CultureInfo]($_.Name)).TextInfo.ListSeparator } | Where-Object Length -ne 1
Copy link
Member

Choose a reason for hiding this comment

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

Same here.

@@ -183,7 +183,7 @@ Function GetParameterInfo
$validateSetAttributes = $parameter.Attributes | Where {
[ValidateSet].IsAssignableFrom($_.GetType())
}
if (($validateSetAttributes -ne $null) -and ($validateSetAttributes.Count -gt 0))
if (($null -ne $validateSetAttributes) -and ($validateSetAttributes.Count -gt 0))
Copy link
Member

Choose a reason for hiding this comment

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

Here, $validateSetAttributes is an array, but this change is good because of the -and ($validateSetAttributes.Count -gt 0) 😄

Copy link
Contributor Author

@bergmeister bergmeister Jul 21, 2017

Choose a reason for hiding this comment

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

Good. Although if I had written the code myself I would have rather declared $validateSetAttributes as [System.Array] because then one would not only avoid the problem that the variable can be null but also prevents gotchas where in the case of array size 1 the type would not be an array and therefore addressing it later as $validateSetAttributes[0] would result in undesired behaviour (in most cases PowerShell would return the first character of the string representation). Just my two cents.

@@ -1634,7 +1634,7 @@ function AddParametersCDXML
[Hashtable] $complexTypeMapping
)

$properties | ? { $_ -ne $null } | % {
$properties | Where-Object { $_ -ne $null } | ForEach-Object {
Copy link
Member

Choose a reason for hiding this comment

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

{ $_ -ne $null }

Should be { $null -ne $_ }


## BadVerb-Variable should be a function, not an alias (1)
((Get-Item Function:\BadVerb-Variable -ErrorAction SilentlyContinue) -ne $null) | Should Be $true
Get-Item Function:\BadVerb-Variable -ErrorAction SilentlyContinue | Should BeNullOrEmpty
Copy link
Member

Choose a reason for hiding this comment

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

Ditto


## BadVerb-Variable should be a function, not an alias (2)
((Get-Item Alias:\BadVerb-Variable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
Get-Item Alias:\BadVerb-Variable -ErrorAction SilentlyContinue | Should BeNullOrEmpty
Copy link
Member

Choose a reason for hiding this comment

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

Ditto


## BadVerb-Variable should be a function, not an alias (2)
((Get-Item Alias:\BadVerb-Variable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
Get-Item Alias:\BadVerb-Variable -ErrorAction SilentlyContinue | Should BeNullOrEmpty
Copy link
Member

Choose a reason for hiding this comment

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

Ditto


## BadVerb-Variable should be an alias, not a function (2)
((Get-Item Alias:\BadVerb-Variable -ErrorAction SilentlyContinue) -ne $null) | Should Be $true
Get-Item Alias:\BadVerb-Variable -ErrorAction SilentlyContinue | Should BeNullOrEmpty
Copy link
Member

Choose a reason for hiding this comment

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

Ditto

if ($rs2 -ne $null) { $rs2.Dispose() }
if ($remoteSession -ne $null) { Remove-PSSession $remoteSession -ErrorAction SilentlyContinue }
if ($null -ne $testDebugger) { $testDebugger.Release() }
if ($null -ne $psl) { $ps.Dispose() }
Copy link
Member

Choose a reason for hiding this comment

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

($null -ne $psl)

Should be $ps

@daxian-dbw
Copy link
Member

daxian-dbw commented Jul 20, 2017

@bergmeister Please push new commits to address the comments and resolve conflicts. Please DO NOT rebase your branch. This way, we can keep the review history which is very important for large size changes like this PR.

@daxian-dbw
Copy link
Member

@bergmeister I just resolved the 2 conflicts. I think we need faster review iterations on this PR, because there are many other PRs changing the script files covered in this PR and thus there will be more conflicts very soon.

…4261 by @daxian-dbw.

This includes a revert of a logical error in Microsoft.PowerShell.ODataV4Adapter.ps1 and reverts back some functions to their alias where the reviewer felt that they were still appropriate. Some minor cosmetic changes and test syntax improvements.
@bergmeister
Copy link
Contributor Author

@daxian-dbw I have applied your requested changes.

Copy link
Member

@daxian-dbw daxian-dbw left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks @bergmeister!

@daxian-dbw daxian-dbw merged commit ffd39b2 into PowerShell:master Jul 22, 2017
@iSazonov
Copy link
Collaborator

@bergmeister Thank you for your patience and hard work!

@bergmeister
Copy link
Contributor Author

@iSazonov @daxian-dbw Thank you both as well. I really had no idea it would turn out to be that complex. I am currently developing a -AutoFix switch for Invoke-PSScriptAnalyzer that will make it easier for everyone to fix some of the remaining warnings in the future.

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.

4 participants