@@ -224,84 +224,167 @@ function Measure-RequiresModules
224224 }
225225}
226226
227- <#
228- . SYNOPSIS
229- You can store the type name in a variable or using -f operator to reduce the amount of redundant information in your script.
230- . DESCRIPTION
231- When interacting with classes that have long type names, you want to reduce the amount of redundant information in your script.
232- To fix a violation of this rule, please store the type name in a variable or using -f operator. For example:
233- $namespace = "System.Collections.{0}"; $arrayList = New-Object ($namespace -f "ArrayList"); $queue = New-Object ($namespace -f "Queue")
234- . EXAMPLE
235- Measure-LongClassName -CommandAst $CommandAst
236- . INPUTS
237- [System.Management.Automation.Language.CommandAst]
238- . OUTPUTS
239- [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
240- . NOTES
241- Reference: 3.11. Reduce Typying for Long Class Names, Windows PowerShell Cookbook, Third Edition
242- #>
243- function Measure-LongClassName
244- {
245- [CmdletBinding ()]
246- [OutputType ([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord []])]
247- Param
248- (
249- [Parameter (Mandatory = $true )]
250- [ValidateNotNullOrEmpty ()]
251- [System.Management.Automation.Language.CommandAst ]
252- $CommandAst
253- )
254-
255- Process
256- {
257- $results = @ ()
258227
259- # The StaticParameterBinder help us to find the argument of TypeName.
260- $spBinder = [System.Management.Automation.Language.StaticParameterBinder ]
261-
262- # Checks New-Object without ComObject parameter command only.
263- if ($null -ne $CommandAst.GetCommandName ())
264- {
265- if ($CommandAst.GetCommandName () -ne " new-object" )
266- {
267- return $results
268- }
269- }
270- else
271- {
272- return $results
273- }
274-
275- try
276- {
277- [System.Management.Automation.Language.StaticBindingResult ]$sbResults = $spBinder ::BindCommand($CommandAst , $true )
278- foreach ($sbResult in $sbResults )
279- {
280- # TypeName cannot be found if user run command like, New-Object -ComObject Scripting.FileSystemObject.
281- if ($null -eq $sbResult.BoundParameters [" TypeName" ].ConstantValue) { continue }
282-
283- if ($sbResult.BoundParameters [" TypeName" ].ConstantValue.ToString().Split(' .' ).Length -ge 3 )
284- {
285- # $sbResult.BoundParameters["TypeName"].Value is a CommandElementAst, so we can return an extent.
286- $result = New-Object `
287- - Typename " Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" `
288- - ArgumentList $Messages.MeasureLongClassName , $sbResult.BoundParameters [" TypeName" ].Value.Extent, $PSCmdlet.MyInvocation.InvocationName , Information, $null
289-
290- $results += $result
291- }
292- }
293-
294- return $results
295- }
296- catch
297- {
298- $PSCmdlet.ThrowTerminatingError ($PSItem )
299- }
228+ # The two rules in the following if block use StaticParameterBinder class.
229+ # StaticParameterBinder class was introduced in PSv4.
230+ if ($PSVersionTable.PSVersion -ge [Version ]' 4.0' )
231+ {
232+ <#
233+ . SYNOPSIS
234+ You can store the type name in a variable or using -f operator to reduce the amount of redundant information in your script.
235+ . DESCRIPTION
236+ When interacting with classes that have long type names, you want to reduce the amount of redundant information in your script.
237+ To fix a violation of this rule, please store the type name in a variable or using -f operator. For example:
238+ $namespace = "System.Collections.{0}"; $arrayList = New-Object ($namespace -f "ArrayList"); $queue = New-Object ($namespace -f "Queue")
239+ . EXAMPLE
240+ Measure-LongClassName -CommandAst $CommandAst
241+ . INPUTS
242+ [System.Management.Automation.Language.CommandAst]
243+ . OUTPUTS
244+ [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
245+ . NOTES
246+ Reference: 3.11. Reduce Typying for Long Class Names, Windows PowerShell Cookbook, Third Edition
247+ #>
248+ function Measure-LongClassName
249+ {
250+ [CmdletBinding ()]
251+ [OutputType ([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord []])]
252+ Param
253+ (
254+ [Parameter (Mandatory = $true )]
255+ [ValidateNotNullOrEmpty ()]
256+ [System.Management.Automation.Language.CommandAst ]
257+ $CommandAst
258+ )
259+
260+ Process
261+ {
262+ $results = @ ()
263+
264+ # The StaticParameterBinder help us to find the argument of TypeName.
265+ $spBinder = [System.Management.Automation.Language.StaticParameterBinder ]
266+
267+ # Checks New-Object without ComObject parameter command only.
268+ if ($null -ne $CommandAst.GetCommandName ())
269+ {
270+ if ($CommandAst.GetCommandName () -ne " new-object" )
271+ {
272+ return $results
273+ }
274+ }
275+ else
276+ {
277+ return $results
278+ }
279+
280+ try
281+ {
282+ [System.Management.Automation.Language.StaticBindingResult ]$sbResults = $spBinder ::BindCommand($CommandAst , $true )
283+ foreach ($sbResult in $sbResults )
284+ {
285+ # TypeName cannot be found if user run command like, New-Object -ComObject Scripting.FileSystemObject.
286+ if ($null -eq $sbResult.BoundParameters [" TypeName" ].ConstantValue) { continue }
287+
288+ if ($sbResult.BoundParameters [" TypeName" ].ConstantValue.ToString().Split(' .' ).Length -ge 3 )
289+ {
290+ # $sbResult.BoundParameters["TypeName"].Value is a CommandElementAst, so we can return an extent.
291+ $result = New-Object `
292+ - Typename " Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" `
293+ - ArgumentList $Messages.MeasureLongClassName , $sbResult.BoundParameters [" TypeName" ].Value.Extent, $PSCmdlet.MyInvocation.InvocationName , Information, $null
294+
295+ $results += $result
296+ }
297+ }
298+
299+ return $results
300+ }
301+ catch
302+ {
303+ $PSCmdlet.ThrowTerminatingError ($PSItem )
304+ }
305+
306+
307+ }
308+ }
309+
310+ <#
311+ . SYNOPSIS
312+ Please do not use COM objects when calling New-Object.
313+ . DESCRIPTION
314+ If you can't use just PowerShell, use .NET, external commands or COM objects, in that order of preference. COM objects are rarely well-documented, making them harder for someone else to research and understand.
315+ They do not always work flawlessly in PowerShell, as they must be used through .NET's Interop layer, which isn't 100% perfect.
316+ To fix a violation of this rule, please do not use COM objects when calling New-Object.
317+ . EXAMPLE
318+ Measure-ComObject -CommandAst $CommandAst
319+ . INPUTS
320+ [System.Management.Automation.Language.CommandAst]
321+ . OUTPUTS
322+ [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
323+ . NOTES
324+ Reference: The Purity Laws, The Community Book of PowerShell Practices.
325+ #>
326+ function Measure-ComObject
327+ {
328+ [CmdletBinding ()]
329+ [OutputType ([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord []])]
330+ Param
331+ (
332+ [Parameter (Mandatory = $true )]
333+ [ValidateNotNullOrEmpty ()]
334+ [System.Management.Automation.Language.CommandAst ]
335+ $CommandAst
336+ )
337+
338+ Process
339+ {
340+ $results = @ ()
341+
342+ # The StaticParameterBinder help us to find the argument of TypeName.
343+ $spBinder = [System.Management.Automation.Language.StaticParameterBinder ]
344+
345+ # Checks New-Object without ComObject parameter command only.
346+ if ($null -ne $CommandAst.GetCommandName ())
347+ {
348+ if ($CommandAst.GetCommandName () -ne " new-object" )
349+ {
350+ return $results
351+ }
352+ }
353+ else
354+ {
355+ return $results
356+ }
357+
358+ try
359+ {
360+ [System.Management.Automation.Language.StaticBindingResult ]$sbResults = $spBinder ::BindCommand($CommandAst , $true )
361+ foreach ($sbResult in $sbResults )
362+ {
363+ if ($sbResults.BoundParameters.ContainsKey (" ComObject" ))
364+ {
365+ # $sbResult.BoundParameters["TypeName"].Value is a CommandElementAst, so we can return an extent.
366+ $result = New-Object `
367+ - Typename " Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" `
368+ - ArgumentList $Messages.MeasureComObject , $sbResult.BoundParameters [" ComObject" ].Value.Extent, $PSCmdlet.MyInvocation.InvocationName , Warning, $null
369+
370+ $results += $result
371+ }
372+ }
373+
374+ return $results
375+ }
376+ catch
377+ {
378+ $PSCmdlet.ThrowTerminatingError ($PSItem )
379+ }
380+
381+
382+ }
383+ }
384+
385+ } # end if ($PSVersionTable.PSVersion -ge [Version]'4.0')
300386
301387
302- }
303- }
304-
305388<#
306389. SYNOPSIS
307390 Do not use deprecated WMI class in your script.
@@ -357,81 +440,6 @@ function Measure-DeprecatedWMIClass
357440 }
358441}
359442
360- <#
361- . SYNOPSIS
362- Please do not use COM objects when calling New-Object.
363- . DESCRIPTION
364- If you can't use just PowerShell, use .NET, external commands or COM objects, in that order of preference. COM objects are rarely well-documented, making them harder for someone else to research and understand.
365- They do not always work flawlessly in PowerShell, as they must be used through .NET's Interop layer, which isn't 100% perfect.
366- To fix a violation of this rule, please do not use COM objects when calling New-Object.
367- . EXAMPLE
368- Measure-ComObject -CommandAst $CommandAst
369- . INPUTS
370- [System.Management.Automation.Language.CommandAst]
371- . OUTPUTS
372- [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
373- . NOTES
374- Reference: The Purity Laws, The Community Book of PowerShell Practices.
375- #>
376- function Measure-ComObject
377- {
378- [CmdletBinding ()]
379- [OutputType ([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord []])]
380- Param
381- (
382- [Parameter (Mandatory = $true )]
383- [ValidateNotNullOrEmpty ()]
384- [System.Management.Automation.Language.CommandAst ]
385- $CommandAst
386- )
387-
388- Process
389- {
390- $results = @ ()
391-
392- # The StaticParameterBinder help us to find the argument of TypeName.
393- $spBinder = [System.Management.Automation.Language.StaticParameterBinder ]
394-
395- # Checks New-Object without ComObject parameter command only.
396- if ($null -ne $CommandAst.GetCommandName ())
397- {
398- if ($CommandAst.GetCommandName () -ne " new-object" )
399- {
400- return $results
401- }
402- }
403- else
404- {
405- return $results
406- }
407-
408- try
409- {
410- [System.Management.Automation.Language.StaticBindingResult ]$sbResults = $spBinder ::BindCommand($CommandAst , $true )
411- foreach ($sbResult in $sbResults )
412- {
413- if ($sbResults.BoundParameters.ContainsKey (" ComObject" ))
414- {
415- # $sbResult.BoundParameters["TypeName"].Value is a CommandElementAst, so we can return an extent.
416- $result = New-Object `
417- - Typename " Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" `
418- - ArgumentList $Messages.MeasureComObject , $sbResult.BoundParameters [" ComObject" ].Value.Extent, $PSCmdlet.MyInvocation.InvocationName , Warning, $null
419-
420- $results += $result
421- }
422- }
423-
424- return $results
425- }
426- catch
427- {
428- $PSCmdlet.ThrowTerminatingError ($PSItem )
429- }
430-
431-
432- }
433- }
434-
435443<#
436444. SYNOPSIS
437445 Adds end-of-line comment after closing curly bracket for deeply nested structures.
@@ -498,8 +506,8 @@ function Measure-CurlyBracket
498506 foreach ($ast in $asts )
499507 {
500508 # Checks nesting structures
501- $nestingASTs = $asts. Where ( {($PSItem.Extent.StartLineNumber -gt $ast.Extent.StartLineNumber ) -and
502- ($PSItem.Extent.EndLineNumber -lt $ast.Extent.EndLineNumber )})
509+ $nestingASTs = $asts | Where-Object {($PSItem.Extent.StartLineNumber -gt $ast.Extent.StartLineNumber ) -and
510+ ($PSItem.Extent.EndLineNumber -lt $ast.Extent.EndLineNumber )}
503511
504512 # If one AST have end-of-line comments, we should skip it.
505513 [bool ]$needComment = $ast.Extent.EndScriptPosition.Line.Trim ().EndsWith(" }" )
@@ -629,7 +637,7 @@ function Measure-Backtick
629637 try
630638 {
631639 # Finds LineContinuation tokens
632- $lcTokens = $Token. Where ( {$PSItem.Kind -eq [System.Management.Automation.Language.TokenKind ]::LineContinuation})
640+ $lcTokens = $Token | Where-Object {$PSItem.Kind -eq [System.Management.Automation.Language.TokenKind ]::LineContinuation}
633641
634642 foreach ($lcToken in $lcTokens )
635643 {
@@ -830,7 +838,7 @@ function Measure-QuestionVariable
830838 $sbAst = [System.Management.Automation.Language.Parser ]::ParseInput($ScriptBlockAst , [ref ]$tokens , [ref ]$errors )
831839
832840 # Gets question variables
833- $questionVariables = $tokens. Where ( {$PSItem.Name -eq ' ?' })
841+ $questionVariables = $tokens | Where-Object {$PSItem.Name -eq ' ?' }
834842
835843 foreach ($questionVariable in $questionVariables )
836844 {
0 commit comments