Skip to content

Treat -Switch:$false the same as omitting a switch when selecting a parameter set #25027

@mklement0

Description

@mklement0

Summary of the new feature / enhancement

Note:

  • The problem affects cmdlets that use [switch] parameters as mandatory in the context of an explicit parameter set, such as Get-UpTime

    • See Get-Uptime -Since ignores an explicit $false value #25015
    • In short: What this issue proposes is to treat -Switch:$false as if -Switch had not been specified, as these two forms should generally be equivalent, with the former often being used for programmatically building arguments, especially via splatting.
      • Indeed, checking $Switch.IsPresent yields $false in both cases (for thoughts about the naming of this property, see this comment).
    • Currently, -SomeSwitch:$false acts the same as passing -SomeSwitch (i.e., implied :$true) in terms of parameter-set selection, which causes all cmdlets that only check whether the relevant parameter set was selected to in effect ignore the $false argument.
      • @MartinGC94's analysis here shows that quite a few existing cmdlets misbehave in this way, and the proposal at hand would presumably automatically fix that.
  • It is debatable whether this proposal should be considered a feature request or a bug report.

  • Either way, it is technically a breaking change, but - given the counterintuitive behavior and the presumed rarity of passing negated switches (-SomeSwitch:$false), it hopefully falls into bucket 3

    • In the vast majority of cases, omitting -SomeSwitch and passing -SomeSwitch:$false (which is the only way to emulate omitting the switch when splatting is used, short of conditionally constructing the splatting hashtable iteratively) should be considered equivalent.
    • (In edge cases, where -SomeSwitch:$false is designed to override a default value set elsewhere, notably via a preference variable (e.g., -Confirm:$false overriding the $ConfirmPreference variable), a different approach is already necessary, namely if, in addition to $Switch.IsPresent returning $false (which is the case when -Switch:$false is passed), whether this "non-presence" was explicitly signaled via a -Switch:$false argument rather than being implied by omission.)

To illustrate the problem:

function foo {
  [CmdletBinding(DefaultParameterSetName='NoSwitchSet')]
  param(
    [Parameter(ParameterSetName='SwitchSet')]
    [switch] $Switch
  )
  [pscustomobject] @{
    SwitchValue = $Switch
    ParameterSetName = $PSCmdlet.ParameterSetName
  }
}

# Invocation without -Switch
foo 
# Invocation with negated -Switch, which _should_ be equivalent.
foo -Switch:$false
# Ditto, via splatting
$splat = @{ Switch = $false }
foo @splat

Expected / proposed behavior:

SwitchValue ParameterSetName
----------- ----------------
False       NoSwitchSet
False       NoSwitchSet
False       NoSwitchSet

Current behavior:

SwitchValue ParameterSetName
----------- ----------------
False       NoSwitchSet
False       SwitchSet
False       SwitchSet

That is, the -Switch:$false invocations (whether directly or via splatting) unexpectedly still selected the SwitchSet parameter set.

Proposed technical implementation details (optional)

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugResolution-Won't FixThe issue won't be fixed, possibly due to compatibility reason.WG-Enginecore PowerShell engine, interpreter, and runtimeWG-Engine-ParameterBinder

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions