DSCResource.Tests/DscResource.AnalyzerRules/DscResource.AnalyzerRules.Helper.psm1
<#
.SYNOPSIS Helper function to check if an Ast is part of a class. Returns true or false .EXAMPLE Test-IsInClass -Ast $ParameterBlockAst .INPUTS [System.Management.Automation.Language.Ast] .OUTPUTS [System.Boolean] .NOTES I initially just walked up the AST tree till I hit a TypeDefinitionAst that was a class But... That means it would throw false positives for things like class HasAFunctionInIt { [Func[int,int]] $MyFunc = { param ( [Parameter(Mandatory=$true)] [int] $Input ) $Input } } Where the param block and all its respective items ARE valid being in their own anonymous function definition that just happens to be inside a class property's assignment value So This check has to be a DELIBERATE step by step up the AST Tree ONLY far enough to validate if it is directly part of a class or not #> function Test-IsInClass { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Language.Ast] $Ast ) [System.Boolean] $inAClass = $false # Is a Named Attribute part of Class Property? if ($Ast -is [System.Management.Automation.Language.NamedAttributeArgumentAst]) { # Parent is an Attribute Ast AND $inAClass = $Ast.Parent -is [System.Management.Automation.Language.AttributeAst] -and # Grandparent is a Property Member Ast (This Ast Type ONLY shows up inside a TypeDefinitionAst) AND $Ast.Parent.Parent -is [System.Management.Automation.Language.PropertyMemberAst] -and # Great Grandparent is a Type Definition Ast AND $Ast.Parent.Parent.Parent -is [System.Management.Automation.Language.TypeDefinitionAst] -and # Great Grandparent is a Class $ast.Parent.Parent.Parent.IsClass } # Is a Parameter part of a Class Method? elseif ($Ast -is [System.Management.Automation.Language.ParameterAst]) { # Parent is a Function Definition Ast AND $inAClass = $Ast.Parent -is [System.Management.Automation.Language.FunctionDefinitionAst] -and # Grandparent is a Function Member Ast (This Ast Type ONLY shows up inside a TypeDefinitionAst) AND $Ast.Parent.Parent -is [System.Management.Automation.Language.FunctionMemberAst] -and # Great Grandparent is a Type Definition Ast AND $Ast.Parent.Parent.Parent -is [System.Management.Automation.Language.TypeDefinitionAst] -and # Great Grandparent is a Class $Ast.Parent.Parent.Parent.IsClass } $inAClass } <# .SYNOPSIS Helper function for the Test-Statement* helper functions. Returns the extent text as an array of strings. .EXAMPLE Get-StatementBlockAsRows -StatementBlock $ScriptBlockAst.Extent .INPUTS [System.String] .OUTPUTS [System.String[]] .NOTES None #> function Get-StatementBlockAsRows { [CmdletBinding()] [OutputType([System.String[]])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $StatementBlock ) <# Remove carriage return since the file is different depending if it's run in AppVeyor or locally. Locally it contains both '\r\n', but when cloned in AppVeyor it only contains '\n'. #> $statementBlockWithNewLine = $StatementBlock -replace '\r', '' return $statementBlockWithNewLine -split '\n' } <# .SYNOPSIS Helper function for the Measure-*Statement PSScriptAnalyzer rules. Test a single statement block for opening brace on the same line. .EXAMPLE Test-StatementOpeningBraceOnSameLine -StatementBlock $ScriptBlockAst.Extent .INPUTS [System.String] .OUTPUTS [System.Boolean] .NOTES None #> function Test-StatementOpeningBraceOnSameLine { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $StatementBlock ) $statementBlockRows = Get-StatementBlockAsRows -StatementBlock $StatementBlock if ($statementBlockRows.Count) { # Check so that an opening brace does not exist on the same line as the statement. if ($statementBlockRows[0] -match '{[\s]*$') { return $true } # if } # if return $false } <# .SYNOPSIS Helper function for the Measure-*Statement PSScriptAnalyzer rules. Test a single statement block for new line after opening brace. .EXAMPLE Test-StatementOpeningBraceIsNotFollowedByNewLine -StatementBlock $ScriptBlockAst.Extent .INPUTS [System.String] .OUTPUTS [System.Boolean] .NOTES None #> function Test-StatementOpeningBraceIsNotFollowedByNewLine { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $StatementBlock ) $statementBlockRows = Get-StatementBlockAsRows -StatementBlock $StatementBlock if ($statementBlockRows.Count -ge 2) { # Check so that an opening brace is followed by a new line. if ($statementBlockRows[1] -match '\{.+') { return $true } # if } # if return $false } <# .SYNOPSIS Helper function for the Measure-*Statement PSScriptAnalyzer rules. Test a single statement block for only one new line after opening brace. .EXAMPLE Test-StatementOpeningBraceIsFollowedByMoreThanOneNewLine -StatementBlock $ScriptBlockAst.Extent .INPUTS [System.String] .OUTPUTS [System.Boolean] .NOTES None #> function Test-StatementOpeningBraceIsFollowedByMoreThanOneNewLine { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $StatementBlock ) $statementBlockRows = Get-StatementBlockAsRows -StatementBlock $StatementBlock if ($statementBlockRows.Count -ge 3) { # Check so that an opening brace is followed by only one new line. if (-not $statementBlockRows[2].Trim()) { return $true } # if } # if return $false } |