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

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions