Powershell: Custom class methods do not complain about an unassigned $args variable

Created on 20 Mar 2019  路  5Comments  路  Source: PowerShell/PowerShell

Related: #12915

By design, class definitions do not support the automatic $args variable for reporting unbound arguments.

However, a reference to $args in a method body is currently:

  • quietly accepted
  • always evaluates to an _empty_ array

This contrasts with references to variables with non-reserved variable names that haven't been initialized, which - sensibly - cause a parse-time error.

Steps to reproduce

class Foo {  [object] Bar() { return $Args } }; [Foo]::new().Bar().GetType().Name

Expected behavior

A parse-time "Variable is not assigned in the method." error.

Actual behavior

Object[]

That is, the class definition unexpectedly succeeds and calling the method yields an empty [object[]] array.

Environment data

PowerShell Core 6.2.0-rc.1
Windows PowerShell v5.1 
Issue-Question WG-Engine

Most helpful comment

There is a semantic check step and we could add new check there.

All 5 comments

This is probably because all reserved name variables exist at all times. Start a new session and dir variable: and $args will be listed as {}. Thus, it is already assigned a value, and no error occurs.

Note that classes generally don't see variables outside of it, so $args is receiving - unwanted - special treatment here:

E.g., the following attempt to $PSHOME - also present in pristine sessions - does _not_ work:

PS> class Foo {  [object] Bar() { return $PSHOME } }; [Foo]::new().Bar() # BREAKS
Variable is not assigned in the method.
...

Conversely, trying to use $args as a _parameter variable_ is quietly _accepted_, but doesn't work:

PS> class Foo {  [object] Bar([array] $args) { return $args.Count } }; [Foo]::new().Bar(@(1, 2))
0   # !! Arguments were quietly ignored.

There is a semantic check step and we could add new check there.

My guess for the reasoning is because this is valid:

class Test {
    static [void] Do() {
        & { $args } 1 2 3
    }
}

It's sort of hard to tell statically if $args is valid there. Well, I guess it could only throw a parse exception if $args is not inside a nested script block, but that's potentially a lot of extra logic going into the semantic check.

It's especially difficult for the other, similar variables which would have these requirements not to throw:

  • $ForEach - A parent foreach statement
  • $switch - A parent switch statement
  • $matches - Any sequence point before the variable access that used the -match operator
  • $PSCmdlet - In a nested scriptblock decorated with CmdletBinding or with a parameter decorated with Parameter (I think some other triggers as well)

As much as I'm all for strict checks in classes, I'm not sure it's worth it in this case.

Makes sense, @SeeminglyScience, thanks - I'm closing this issue.

Was this page helpful?
0 / 5 - 0 ratings