fix: handle multi-line param() attributes in parameter detection

Switch from single multiline regex to line-by-line scanning that skips
PowerShell attribute decorators like [Parameter(Mandatory=$true)] and
[ValidateSet(...)]. This correctly detects parameters with attribute
lines above them (the most common real-world pattern).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-14 19:16:53 -04:00
parent 5efe42583a
commit 75c48eab9e

View File

@@ -178,27 +178,36 @@ function extractParamBlockCandidates(
block: { start: number; end: number }
): ParameterCandidate[] {
const lines = script.split('\n')
const blockText = lines.slice(block.start, block.end + 1).join('\n')
const candidates: ParameterCandidate[] = []
const paramRegex = /(?:\[(\w+)\])?\s*\$(\w+)(?:\s*=\s*(.+?))?(?:\s*,\s*$|\s*$|\s*\))/gm
let match: RegExpExecArray | null
// Scan each line in the param block for $VarName patterns.
// Lines with [Parameter(...)], [ValidateSet(...)], etc. don't contain $VarName
// so they are naturally skipped. The type annotation [string], [int], etc.
// appears on the same line as $VarName.
const varLineRegex = /(?:\[(\w+)\])?\s*\$(\w+)(?:\s*=\s*(.+?))?(?:\s*,?\s*$)/
for (let i = block.start; i <= block.end; i++) {
const trimmed = lines[i].trim()
// Skip attribute lines like [Parameter(...)], [ValidateSet(...)], etc.
if (/^\[(?:Parameter|ValidateSet|ValidateRange|ValidatePattern|ValidateScript|ValidateLength|ValidateCount|Alias|AllowNull|AllowEmptyString|AllowEmptyCollection)\s*\(/i.test(trimmed)) {
continue
}
const match = trimmed.match(varLineRegex)
if (!match) continue
while ((match = paramRegex.exec(blockText)) !== null) {
const typeAnnotation = match[1] || null
const varName = match[2]
const rawDefault = match[3]?.trim() ?? null
// Skip if type looks like an attribute we didn't catch above
if (typeAnnotation && /^Parameter$/i.test(typeAnnotation)) continue
const key = toSnakeCase(varName)
const { type, sensitive, reason } = inferType(typeAnnotation, rawDefault, varName)
const defaultValue = parseDefault(rawDefault, type)
const lineIndex = lines.findIndex((line, idx) =>
idx >= block.start && idx <= block.end && line.includes(`$${varName}`)
)
candidates.push({
variableName: `$${varName}`,
suggestedKey: key,
@@ -207,8 +216,8 @@ function extractParamBlockCandidates(
sensitive,
defaultValue,
source: 'param_block',
lineNumber: lineIndex !== -1 ? lineIndex + 1 : block.start + 1,
matchedLine: lineIndex !== -1 ? lines[lineIndex].trim() : `$${varName}`,
lineNumber: i + 1,
matchedLine: trimmed,
inferenceReason: reason,
})
}