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

Cmdlets that run in current scope (like ForEach-Object) not accounted for by PSUseDeclaredVarsMoreThanAssignments #1163

Open
iRon7 opened this issue Mar 4, 2019 · 9 comments

Comments

@iRon7
Copy link

iRon7 commented Mar 4, 2019

Although issue #1031 and #1129 probably have the same cause, I have added a new bug report as it is (afaik) not directly related to the Begin, Process and End function blocks as suggested in there which brings the issue in a different perspective.

Apparently for some cmdlets, PowerShell is invoked in the current scope but that is apparently not respected by the PSScriptAnalyzer.

Steps to reproduce

$Test = $False
1..3 | ForEach-Object {$Test = $True}
$Test

note that the result of the above is $True, meaning that the value of $Test is actually assigned and changed to $True within the ForEach-Object cmdlet.

Expected behavior

No warning.

Actual behavior

RuleName                            Severity     ScriptName Line  Message
--------                            --------     ---------- ----  -------
PSUseDeclaredVarsMoreThanAssignment Warning      Test.ps1   2     The variable 'Test' is assigned but never used.

Environment data

Name                           Value
----                           -----
PSVersion                      5.1.17134.590
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17134.590
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

PSScriptAnalyzer.Version:    1.17.1

Will this eventually be resolved?
How can I nicely avoid the incorrect warning in an example like above?

@VonRegi
Copy link

VonRegi commented Mar 20, 2019

Hello, @RobFaie and I been looking into this issue.
The problem is that assignments made in script blocks for functions like Where-Object and Select-Object
are accessible after the fact but similar blocks for functions like Invoke-Command are not. I put together a little PoC that skips processing
for Where-Object script blocks. I wanted to touch base before putting more time into this to discuss the approach.

image

@iRon7
Copy link
Author

iRon7 commented Mar 21, 2019

I do not have enough C# knowledge to comment on the specific code, but I am a little confused that the focus shifted to the Where-Object cmdlet instead of the ForEach-Object cmdlet example.
Although, the issue also exist in the Where-Object cmdlet, the Where-Object cmdlet is mainly used for comparison and therefore unlikely contains a variable assignment (or do I miss something?).
For the ForEach-Object cmdlet, I can give you used case, that might occur (to my opinion) more often:
I have written a PowerShell Join-Object cmdlet (https://www.powershellgallery.com/packages/Join).
This cmdlet has a -Property parameter which similar to the Select-Object -Property parameter where it accepts one or more property name, calculated properties and/or a asterisks for all know properties.
Basically, something like:

$All = $False
$Property | ForEach-Object {
	If ($_ -eq "*") {$All = $True}
	ElseIf ($_ -eq a calcultated property...
}
If ($All) {
	Add any known property to output object that is not filled by a calculated property
}
...

Besides this example, I also imaging that a state variable like a $Dirty flag could be quiet often set in a ForEach-Object cmdlet.

@bergmeister
Copy link
Collaborator

Currently the rule is limited to analysis within a scriptblock. Extending this functionality would require more thinking how to handle the scoping behaviour that PowerShell exhibits.

@pcgeek86
Copy link

pcgeek86 commented Oct 1, 2019

I'm seeing the same issue. Here is a contrived repro:

Get-Process | ForEach-Object -Begin { $ProcessCount = 0 } -Process { $ProcessCount += 1 } -End { $ProcessCount }

Screen Shot 2019-09-30 at 8 20 26 PM

@FireInWinter
Copy link

Regardless if this is easy to detect or not, having warnings all over the place because the variable is "never used" is less than useless. I would almost suggest removing this rule until it is better at detection. Either that or assume everything is in the same scope for now, which would only cause things not in scope to get a false "pass". I think this would be a much better situation than what we currently have.

@bergmeister
Copy link
Collaborator

bergmeister commented Mar 5, 2020

This is a known issue due to the rule being limited to the scope of a scriptblock. If you look for issues with the Area - PSUseDeclaredVarsMoreThanAssignments label, you will find similar reports. Specifically, #1031 already covers Begin/Process/End blocks, therefore I suggest to close as duplicate.
@FireInWinter I disagree with that. The rule definitely has value, in some cases you have to explicitly look at the situation and suppress it on a case by case basis. If it is too much for you, you can globally suppress it using the -ExcludeRule switch or by using the equivalent in a PSSA settings file, which VS-Code would be able to use.

@amis92
Copy link

amis92 commented Apr 17, 2020

Another cmdlet I've stumbled upon that's impacted by this is Measure-Command. The -Expression scriptblock it takes in is also executed in current scope.

@rjmholt rjmholt changed the title "assigned but never used" for cmdlets that run in the current scope (like ForEach-Object) Cmdlets that implicitly dot-source (like ForEach-Object) not accounted for by PSUseDeclaredVarsMoreThanAssignments Feb 9, 2021
@rjmholt rjmholt changed the title Cmdlets that implicitly dot-source (like ForEach-Object) not accounted for by PSUseDeclaredVarsMoreThanAssignments Cmdlets that run in current scope (like ForEach-Object) not accounted for by PSUseDeclaredVarsMoreThanAssignments Aug 11, 2021
@nascentt
Copy link

Why does this fire on $global:variables if it only accounts for a single scriptblock?
Makes the check entirely useless.

@Peter-76
Copy link

Issue is 5 years old and still no progress :-(
Is there any chance that this gets solved anytime soon?

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

No branches or pull requests

9 participants