diff --git a/Rules/AvoidAlias.cs b/Rules/AvoidAlias.cs index ba069453b..7b8a46c2f 100644 --- a/Rules/AvoidAlias.cs +++ b/Rules/AvoidAlias.cs @@ -348,6 +348,12 @@ private ScriptBlockExpressionAst GetDscResourceBody(CommandAst cmdAst) && bodyPipeline.PipelineElements.Count == 1 && bodyPipeline.PipelineElements[0] is CommandExpressionAst bodyExpression) { + if (HasSemicolonSeparator(pipeline, bodyPipeline) + || bodyExpression.Redirections.Count != 0) + { + return null; + } + return bodyExpression.Expression as ScriptBlockExpressionAst; } @@ -358,6 +364,26 @@ private ScriptBlockExpressionAst GetDscResourceBody(CommandAst cmdAst) return null; } + private bool HasSemicolonSeparator(Ast statement, Ast nextStatement) + { + int separatorStart = statement.Extent.EndOffset; + int separatorLength = nextStatement.Extent.StartOffset - separatorStart; + if (separatorLength <= 0) + { + return false; + } + + string script = statement.Extent.EndScriptPosition.GetFullScript(); + string separator = script.Substring(separatorStart, separatorLength); + if (separator.IndexOf(';') < 0) + { + return false; + } + + Parser.ParseInput(separator, out Token[] tokens, out ParseError[] errors); + return tokens.Any(token => token.Kind == TokenKind.Semi); + } + /// /// For a command like "gci -path c:", returns the extent of "gci" in the command /// diff --git a/Tests/Rules/AvoidUsingAlias.tests.ps1 b/Tests/Rules/AvoidUsingAlias.tests.ps1 index 2bdd55b33..731dc4137 100644 --- a/Tests/Rules/AvoidUsingAlias.tests.ps1 +++ b/Tests/Rules/AvoidUsingAlias.tests.ps1 @@ -232,6 +232,30 @@ Configuration PackageTest { $_.SuggestedCorrections[0].Text | Should -Be 'Get-Verb' } } + + It "does not pair following-line blocks with commands when the block has redirections or a statement terminator intervenes" { + $target = @' +{ + gci . + {} > $null + gci .; {} +} +'@ + $ast = [System.Management.Automation.Language.Parser]::ParseInput($target, [ref]$null, [ref]$null) + $body = $ast.EndBlock.Statements[0].PipelineElements[0].Expression.Copy() + $configuration = [System.Management.Automation.Language.ConfigurationDefinitionAst]::new( + $ast.Extent, $body, 'Resource', + [System.Management.Automation.Language.StringConstantExpressionAst]::new($ast.Extent, 'Test', 'BareWord')) + $rule = [Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.AvoidAlias]::new() + + $violations = @($rule.AnalyzeScript($configuration, $null)) + + $violations.Count | Should -Be 2 + $violations | ForEach-Object { + $_.Extent.Text | Should -Be 'gci' + $_.SuggestedCorrections[0].Text | Should -Be 'Get-ChildItem' + } + } } Context "Settings file provides allowlist" {