Skip to content

PSUseConsistentIndentation: Hashtable inside method call gets double-indented #2159

@quasarea

Description

@quasarea

Summary

PSUseConsistentIndentation incorrectly indents hashtable body content when the hashtable is inside a method call parenthesis (e.g., .Add(@{ ... })). The formatter treats the @{ opening as an additional indentation level relative to the method call position, producing excessive indentation instead of indenting consistently from the line start.

This was originally reported as PowerShell/vscode-powershell#5397, where a maintainer confirmed it belongs in PSScriptAnalyzer. That issue was auto-closed awaiting author feedback.

Steps to Reproduce

# PSScriptAnalyzer 1.24.0 / PowerShell 7.5.4

# Minimal repro - only PSUseConsistentIndentation enabled
$code = @'
$list.Add([PSCustomObject]@{
    Name = "Test"
    Value = 123
})
'@

$settings = @{
    IncludeRules = @("PSUseConsistentIndentation")
    Rules = @{
        PSUseConsistentIndentation = @{
            Enable = $true
            IndentationSize = 4
            Kind = "space"
        }
    }
}

Invoke-Formatter -ScriptDefinition $code -Settings $settings

Expected Behavior

Hashtable properties should be indented by one level (4 spaces) from the line start, regardless of where @{ appears on the line:

$list.Add([PSCustomObject]@{
    Name = "Test"
    Value = 123
})

Actual Behavior

Properties are indented to the @{ column position (8 spaces), and the closing }) gets its own indentation level too:

$list.Add([PSCustomObject]@{
        Name = "Test"
        Value = 123
    })

Additional Test Cases

The problem compounds with deeper nesting:

# Input:
foreach ($item in $items) {
    $results.Add([PSCustomObject]@{
        Name = $item.Name
        Value = $item.Value
        Status = "OK"
    })
}

# Formatted (actual) - 12 spaces for hashtable body:
foreach ($item in $items) {
    $results.Add([PSCustomObject]@{
            Name = $item.Name
            Value = $item.Value
            Status = "OK"
        })
}

# Expected - 8 spaces (one level deeper than the foreach body):
foreach ($item in $items) {
    $results.Add([PSCustomObject]@{
        Name = $item.Name
        Value = $item.Value
        Status = "OK"
    })
}

Standalone hashtables (not inside method calls) format correctly:

# This formats correctly with 4-space indent:
$obj = [PSCustomObject]@{
    Name = "Test"
    Value = 123
}

Root Cause Analysis

PSUseConsistentIndentation appears to count ( as an indentation-increasing token, then also counts @{ as another level. For standalone hashtables like $x = @{, the = doesn't increase indentation so only @{ counts — producing 4 spaces correctly. But inside .Add(@{, the ( adds one level and @{ adds another, producing 8 spaces (2 × 4).

Impact

This is a common PowerShell pattern — building lists of [PSCustomObject] via .Add() is idiomatic. The excessive indentation forces a choice between:

  1. Disabling PSUseConsistentIndentation entirely
  2. Accepting unintuitively deep indentation
  3. Disabling powershell.codeFormatting.alignPropertyValuePairs (which also disables desirable alignment on standalone hashtables)
  4. Disabling format-on-save for PowerShell files

When combined with PSAlignAssignmentStatement.CheckHashtable, the problem is amplified because value alignment is calculated from the already-wrong base indentation.

Workaround

Setting powershell.codeFormatting.alignPropertyValuePairs: false in VS Code mitigates the worst visual impact but also disables alignment on standalone hashtables where it's desirable.

Environment

  • PSScriptAnalyzer: 1.24.0
  • PowerShell: 7.5.4
  • OS: Linux (also reported on Windows 11 in vscode-powershell#5397)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions