Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New rule to validate help comments #217

Open
yutingc opened this issue May 26, 2015 · 1 comment
Open

New rule to validate help comments #217

yutingc opened this issue May 26, 2015 · 1 comment

Comments

@yutingc
Copy link
Contributor

yutingc commented May 26, 2015

Say, if a parameter name is renamed, the help comment should also be updated.

@charlieschmidt
Copy link
Contributor

It's not awesome, but I use this in a custom rule module to find parameter/help mismatches. The dynamic param stuff could certainly be better, it gets the dynamic param statements, creates a scriptblock, executes that and parses the return'd dictionary for the dynamic params. I'm not aware of anything in the AST that will do the dynamic param stuff for you.

function Measure-MissingParameterDocumentation
{
<#
.SYNOPSIS
    Finds functions missing parameter documentation for any of their parameters
.EXAMPLE
    Measure-MissingParameterDocumentation -ScriptBlockAst $ScriptBlockAst
.PARAMETER ScriptBlockAst
    ScriptBlockAst to analyze
.INPUTS
    [System.Management.Automation.Language.ScriptBlockAst]
.OUTPUTS
    [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
.NOTES
    None
#>
    [CmdletBinding()]
    [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
    Param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.Language.ScriptBlockAst]
        $ScriptBlockAst
    )

    begin 
    {
    }

    Process
    {
        $results = @()
        try
        {
            # Finds param block
            [ScriptBlock]$ParamBlockPredicate = {
                param 
                (
                    [System.Management.Automation.Language.Ast]$Ast
                )
                [bool]$ReturnValue = $false

                if ($Ast -is [System.Management.Automation.Language.ParamBlockAst])
                {

                    $ReturnValue = $true;

                }
                return $ReturnValue
            }

            # Finds dynamicparam block
            [ScriptBlock]$DynamicParamBlockPredicate = {
                param 
                (
                    [System.Management.Automation.Language.Ast]$Ast
                )
                [bool]$ReturnValue = $false

                if ($Ast -is [System.Management.Automation.Language.NamedBlockAst])
                {
                    [System.Management.Automation.Language.NamedBlockAst]$NamedBlockAst = $Ast

                    if ($NamedBlockAst.BlockKind -eq [System.Management.Automation.Language.TokenKind]::Dynamicparam)
                    {
                        $ReturnValue = $true;
                    }

                }
                return $ReturnValue
            }

            # Finds command element block
            [ScriptBlock]$PipelineAstPredicate = {
                param 
                (
                    [System.Management.Automation.Language.Ast]$Ast
                )
                [bool]$ReturnValue = $false

                if ($Ast -is [System.Management.Automation.Language.PipelineAst])
                {
                    $ReturnValue = $true;
                }
                return $ReturnValue
            }

            # Finds function block
            [ScriptBlock]$FunctionPredicate = {
                param 
                (
                    [System.Management.Automation.Language.Ast]$Ast
                )
                [bool]$ReturnValue = $false

                if ($Ast -is [System.Management.Automation.Language.FunctionDefinitionAst])
                {
                    $ReturnValue = $true;
                }
                return $ReturnValue
            }


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

            foreach ($Ast in $FunctionBlockAsts)
            {
                [System.Management.Automation.Language.FunctionDefinitionAst]$FunctionAst = $Ast;

                # get parameters in help already
                [System.Management.Automation.Language.CommentHelpInfo]$Help = $FunctionAst.GetHelpContent()
                $ParametersInHelp = $Help.Parameters.Keys


                $ParametersInFunction = @()

                # get static params
                [System.Management.Automation.Language.Ast[]]$ParamBlockAsts = $FunctionAst.FindAll($ParamBlockPredicate,$true)

                foreach ($Ast2 in $ParamBlockAsts)
                {
                    [System.Management.Automation.Language.ParamBlockAst]$ParamBlockAst = $Ast2

                    foreach ($ParamAst in $ParamBlockAst.Parameters)
                    {
                        $ParametersInFunction += $ParamAst.Name.VariablePath.UserPath
                    }
                }

                # get dynamic params
                [System.Management.Automation.Language.Ast[]]$DynamicParamBlockAsts = $FunctionAst.FindAll($DynamicParamBlockPredicate,$true)

                foreach ($Ast3 in $DynamicParamBlockAsts)
                {
                    $Script = $Ast3.statements -join "`n"
                    $DynamicParamBlockScriptBlock = [ScriptBlock]::Create($script)
                    $RuntimeDictionary = Invoke-Command -Scriptblock $DynamicParamBlockScriptBlock
                    foreach ($Param in $RuntimeDictionary.Keys)
                    {
                        $ParametersInFunction += $Param
                    }

                }

                # check params against help params              
                foreach ($FunctionParam in $ParametersInFunction)
                {
                    if ($null -eq $ParametersInHelp -or $ParametersInHelp -inotcontains $FunctionParam)
                    {
                        $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{"Message"  = "Function missing .PARAMETER documentation for parameter '$FunctionParam'"; 
                                                    "Extent"   = $Ast.Extent;
                                                    "RuleName" = $PSCmdlet.MyInvocation.MyCommand.Name.Replace("Measure-","");
                                                    "Severity" = "Warning"}
                        $Results += $Result    
                    }
                }
            }

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants