Skip to content

Commit

Permalink
New PSSA custom rule for opening braces
Browse files Browse the repository at this point in the history
- Add Script Analyzer custom rule to test functions and statements so that
  opening braces are set according to the style guideline (issue PowerShell#27).
  • Loading branch information
johlju committed Aug 11, 2017
1 parent cf55874 commit 5921ca5
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 20 deletions.
304 changes: 284 additions & 20 deletions DscResource.AnalyzerRules/DscResource.AnalyzerRules.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -58,40 +58,304 @@ function Measure-ParameterBlockParameterAttribute
if ($parameterAst.Attributes.Typename.FullName -notcontains 'parameter')
{
$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.ParameterBlockParameterAttributeMissing, `
$parameterAst.Extent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning', `
$null
)
}
elseif ($parameterAst.Attributes[0].Typename.FullName -ne 'parameter')
{
$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.ParameterBlockParameterAttributeWrongPlace, `
$parameterAst.Extent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning', `
$null
)
}
elseif ($parameterAst.Attributes[0].Typename.FullName -cne 'Parameter')
{
$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.ParameterBlockParameterAttributeLowerCase, `
$parameterAst.Extent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning', `
$null
)
} # if
} # foreach parameter
} # foreach function

return $results
}
catch
{
$PSCmdlet.ThrowTerminatingError($PSItem)
}
}
}

<#
.SYNOPSIS
Validates the function block braces and new lines around braces.
.DESCRIPTION
Each function should have the opening brace on a separate line. Also, the
opening brace should be followed by a new line.
.EXAMPLE
Measure-FunctionBlockBraces -ScriptBlockAst $ScriptBlockAst
.INPUTS
[System.Management.Automation.Language.ScriptBlockAst]
.OUTPUTS
[Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
.NOTES
None
#>
function Measure-FunctionBlockBraces
{
[CmdletBinding()]
[OutputType([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
Param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.Language.ScriptBlockAst]
$ScriptBlockAst
)

Process
{
$results = @()

try
{
$diagnosticRecordType = 'Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord'

$findAllFunctionsFilter = {
$args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst]
}

[System.Management.Automation.Language.Ast[]] $functionsAst = $ScriptBlockAst.FindAll( $findAllFunctionsFilter, $true )

foreach ($functionAst in $functionsAst)
{
<#
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 contain '\n'.
#>
$functionExtentTextWithNewLine = $functionAst.Extent -replace '\r', ''

$functionExtentRows = $functionExtentTextWithNewLine -split '\n'

if ($functionExtentRows.Count)
{
# Check so that an opening brace does not exist on the same line as the function name.
if ($functionExtentRows[0] -match '\{')
{
$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.FunctionOpeningBraceNotOnSameLine, `
$functionAst.Extent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning', `
$null
)
} # if
} # if

if ($functionExtentRows.Count -ge 2)
{
# Check so that an opening brace is followed by a new line.
if ($functionExtentRows[1] -match '\{.+')
{
$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.FunctionOpeningBraceShouldBeFollowedByNewLine, `
$functionAst.Extent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning', `
$null
)
} # if
} # if

if ($functionExtentRows.Count -ge 3)
{
# Check so that an opening brace is followed by only one new line.
if ($functionExtentRows[2].Trim() -eq '')
{
$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.FunctionOpeningBraceShouldBeFollowedByOnlyOneNewLine, `
$functionAst.Extent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning', `
$null
)
} # if
} # if
}

return $results
}
catch
{
$PSCmdlet.ThrowTerminatingError($PSItem)
}
}
}

<#
.SYNOPSIS
Validates the statement block braces and new lines around braces.
.DESCRIPTION
Each statement should have the opening brace on a separate line. Also, the
opening brace should be followed by a new line.
.EXAMPLE
Measure-StatementBlockBraces -ScriptBlockAst $ScriptBlockAst
.INPUTS
[System.Management.Automation.Language.ScriptBlockAst]
.OUTPUTS
[Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
.NOTES
None
#>
function Measure-StatementBlockBraces
{
[CmdletBinding()]
[OutputType([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
Param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.Language.ScriptBlockAst]
$ScriptBlockAst
)

Process
{
$results = @()

try
{
$diagnosticRecordType = 'Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord'

$findAllFunctionsFilter = {
$args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst]
}

[System.Management.Automation.Language.Ast[]] $functionsAst = $ScriptBlockAst.FindAll( $findAllFunctionsFilter, $true )

foreach ($functionAst in $functionsAst)
{
$findAllStatementBlockFilter = {
$args[0] -is [System.Management.Automation.Language.StatementBlockAst]
}

[System.Management.Automation.Language.Ast[]] $statementBlocksAst = $functionAst.FindAll( $findAllStatementBlockFilter, $true )
if ($statementBlocksAst)
{
$statementParentExtents = $statementBlocksAst.Parent.Extent

foreach ($statementParentExtent in $statementParentExtents)
{
<#
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 contain '\n'.
#>
$statementParentExtentTextWithNewLine = $statementParentExtent.Text -replace '\r', ''

$statementParentExtentRows = $statementParentExtentTextWithNewLine -split '\n'

if ($statementParentExtentRows.Count)
{
# Check so that an opening brace does not exist on the same line as the statement.
if ($statementParentExtentRows[0] -match '\{')
{

$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.ParameterBlockParameterAttributeMissing, `
$parameterAst.Extent, `
$localizedData.StatementOpeningBraceNotOnSameLine, `
$statementParentExtent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning', `
$null
)
}
elseif ($parameterAst.Attributes[0].Typename.FullName -ne 'parameter')
)
} # if
} # if
} # foreach statement parent

$statementExtents = $statementBlocksAst.Extent

foreach ($statementExtent in $statementExtents)
{
$results += New-Object `
<#
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 contain '\n'.
#>
$statementExtentTextWithNewLine = $statementExtent.Text -replace '\r', ''

$statementExtentRows = $statementExtentTextWithNewLine -split '\n'

if ($statementExtentRows.Count)
{
# Check so that an opening brace is followed by a new line.
if ($statementExtentRows[0] -match '\{.+')
{
$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.ParameterBlockParameterAttributeWrongPlace, `
$parameterAst.Extent, `
$localizedData.StatementOpeningBraceShouldBeFollowedByNewLine, `
$statementExtent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning',`
'Warning', `
$null
)
}
elseif ($parameterAst.Attributes[0].Typename.FullName -cne 'Parameter')
{
$results += New-Object `
-Typename $diagnosticRecordType `
)
} # if
} # if

if ($statementExtentRows.Count -ge 2)
{
# Check so that an opening brace is followed by only one new line.
if ($statementExtentRows[1].Trim() -eq '')
{
$results += New-Object `
-Typename $diagnosticRecordType `
-ArgumentList @(
$localizedData.ParameterBlockParameterAttributeLowerCase, `
$parameterAst.Extent, `
$localizedData.StatementOpeningBraceShouldBeFollowedByOnlyOneNewLine, `
$statementExtent, `
$PSCmdlet.MyInvocation.InvocationName, `
'Warning', `
$null
)
} # if
} # foreach parameter
)
} # if
} # if
} # foreach statement
} # if
} # foreach function

return $results
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ ConvertFrom-StringData @'
ParameterBlockParameterAttributeMissing = A [Parameter()] attribute must be the first attribute of each parameter and be on its own line. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#correct-format-for-parameter-block
ParameterBlockParameterAttributeLowerCase = The [Parameter()] attribute must start with an upper case 'P'. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#correct-format-for-parameter-block
ParameterBlockParameterAttributeWrongPlace = The [Parameter()] attribute must be the first attribute of each parameter. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#correct-format-for-parameter-block
FunctionOpeningBraceNotOnSameLine = Functions should not have the open brace on the same line as the function name. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#one-newline-before-braces
FunctionOpeningBraceShouldBeFollowedByNewLine = Opening brace on function should be followed by a new line. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#one-newline-after-opening-brace
FunctionOpeningBraceShouldBeFollowedByOnlyOneNewLine = Opening brace on functions should only be followed by one new line. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#one-newline-after-opening-brace
StatementOpeningBraceNotOnSameLine = Statements should not have the open brace on the same line as the statement. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#one-newline-before-braces
StatementOpeningBraceShouldBeFollowedByNewLine = Opening brace on statements should be followed by a new line. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#one-newline-after-opening-brace
StatementOpeningBraceShouldBeFollowedByOnlyOneNewLine = Opening brace on statements should only be followed by one new line. See https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#one-newline-after-opening-brace
'@
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,8 @@ Invoke-AppveyorAfterTestTask `
variable for splatting.
* Enable so that missing required modules for integration tests is installed if
running in AppVeyor or show warning if run by user ([issue #168](https://github.com/PowerShell/DscResource.Tests/issues/168)).
* Add Script Analyzer custom rule to test functions and statements so that
opening braces are set according to the style guideline ([issue #27](https://github.com/PowerShell/DscResource.Tests/issues/27)).

### 0.2.0.0

Expand Down

0 comments on commit 5921ca5

Please sign in to comment.