PowerShell / PowerShell/PSScriptAnalyzer

PSUseConsistentIndentation double-indents attribute bodies that open a scriptblock (`[Attr({ … })]`)

オープン
#2,216 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

主要言語
C#
スター
2.2k
フォーク
414
平均マージ
13時間 1分
マージ済み PR(30日)
2

説明

Prerequisites
  • I have read the documentation and my issue is not covered there.
  • I have searched the existing issues and my issue is not already reported.
Summary

PSUseConsistentIndentation (and therefore Invoke-Formatter) double-indents the body of an attribute that opens a scriptblock, e.g. [ArgumentCompleter({...})]. The body gets IndentationSize * 2 and the closing })] gets IndentationSize * 1.

This looks like the same root cause as #2159 (hashtable inside a method call: the LParen and the AtCurly each add an indentation level). Here the two adjacent openers are ( and { from a type/attribute literal instead of @{, so [AttributeName({ hits it too. #2159 was fixed on main by #2173 ("only the last unclosed opener on a line affects indentation"), but neither that fix nor #2159 covers the attribute form, and it is still broken in the latest release 1.25.0.

Steps to reproduce
$code = @'
[ArgumentCompleter({
	Param($commandName, $parameterName)
	$validKeys = @('a', 'b')
	$validKeys | Where-Object { $_ } | ForEach-Object { "$_=" }
})]
'@

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

Invoke-Formatter -ScriptDefinition $code -Settings $settings
Expected behavior

One level of indentation for the body, matching the opener line, and the closing })] at the opener's level:

[ArgumentCompleter({
	Param($commandName, $parameterName)
	$validKeys = @('a', 'b')
	$validKeys | Where-Object { $_ } | ForEach-Object { "$_=" }
})]
Actual behavior

Two levels for the body and one level for the closing })]:

[ArgumentCompleter({
		Param($commandName, $parameterName)
		$validKeys = @('a', 'b')
		$validKeys | Where-Object { $_ } | ForEach-Object { "$_=" }
	})]

The same happens with spaces (Kind = 'space', IndentationSize = 4) and with NewLineAfterOpenBrace/PSPlaceOpenBrace not involved at all — PSUseConsistentIndentation alone is enough.

It also affects other attribute forms that open a scriptblock, e.g.

[ValidateScript({
	$_ -gt 0
})]

and the buggy output is not idempotent in a harmless way: because the opener line itself is not re-indented, re-running the formatter keeps the body at the wrong level, and any tool that re-applies formatting sees a permanent diff.

Environment
PSVersion: 7.6.6
PSEdition: Core
OS: Windows 11 (26100)
PSScriptAnalyzer: 1.25.0

Also reproduced with PSScriptAnalyzer 1.24.0 (bundled with ms-vscode.powershell 2025.4.0) and with Windows PowerShell 5.1 / 26100 (Desktop edition), so it is not host- or edition-specific.

Related
  • #2159 — PSUseConsistentIndentation: Hashtable inside method call gets double-indented (same LParen+XCurly double count; fixed by #2173)
  • #2173 — the fix that pops the level when an opener is not the last unclosed opener on the line; it does not catch the attribute opening ({
  • #1168 — "Formatting .where and .foreach methods is incorrect" (adjacent-opener family)

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、PSUseConsistentIndentation を有効にした状態で、提供されている Invoke-Formatter の再現手順を実行し、次に、属性または型リテラルが scriptblock を開く場合の PSUseConsistentIndentation ルールのインデント処理を追跡します。[ArgumentCompleter({ ... })] と [ValidateScript({ ... })] を対象とする回帰テストを追加し、本文が 1 レベルインデントされ、閉じる })] が開始位置のレベルになることを検証します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
csharp, powershell
領域
tooling
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
活発
明瞭さ
明確に書かれている
初心者へのやさしさ
70/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。