Use the Abstract Syntax Tree (AST) to list parameters and variables in PowerShell functions
One thing I've missed during the past couple of years with virtual-only conferences is the hallway track. While at the PowerShell + DevOps Global Summit 2022, there was a discussion about using PascalCase for parameter names and camelCase for user-defined variables in your PowerShell functions.
Specifying different casings depending on the usage seems like a great idea. Determining where you defined your variables would be self-explanatory. The only problem is you need something to verify that you've specified them in the correct case.
This scenario is a perfect opportunity to use the Abstract Syntax Tree (AST) to verify the casing is correct depending on usage.
I started using the Get-MrAst
function in my
MrModuleTools PowerShell module from my
ModuleTools GitHub repo to retrieve the AST from a
file or script block.
1Get-MrAst -Path C:\MrGit\Inspector\MrInspector\public\Get-MrSyntax.ps1
I used another one of my functions named Get-MrAstType
in the same module to determine the
different options for filtering the AST. This part was straightforward since they're named
ParameterAst
and VariableExpressionAst
.
1Get-MrAstType | Format-Wide -Property {$_} -Column 3 -Force
There are two different ways to obtain the parameters and variables. One contains the dollar sign
and the other, only the variable name. I opted for the one with only the variable name. I filtered
out the underscore to prevent current object variables from showing up in the list unless you're
using PSItem
.
The list of variables also contains the parameters because they're specified in the body of the
function. I used Compare-Object
to determine which items are duplicated in both lists. An If
statement identifies the duplicates as parameters and the non-duplicated items as user-defined
variables.
I included the line and column numbers in the output to know where the variables are used. Sometimes they're specified more than once on the same line.
1#Requires -Version 4.0
2function Get-MrVariableType {
3
4<#
5.SYNOPSIS
6 List variables and whether they're defined as parameters or in the body of a function.
7
8.DESCRIPTION
9 Get-MrVariableType is an advanced function that returns a list of variables defined in a
10 function and whether they are parameters or user defined within the body of the function.
11
12 .PARAMETER Ast
13 Provide a ScriptBlockAst object via parameter or pipeline input. Use Get-MrAst to create this
14 object.
15
16.EXAMPLE
17 Get-MrAST -Path 'C:\Scripts' | Get-MrVariableType
18
19.EXAMPLE
20 Get-MrVariableType -Ast (Get-MrAST -Path 'C:\Scripts')
21
22.NOTES
23 Author: Mike F Robbins
24 Website: http://mikefrobbins.com
25 Twitter: @mikefrobbins
26#>
27
28 [CmdletBinding()]
29 param (
30 [Parameter(Mandatory,
31 ValueFromPipeline)]
32 [System.Management.Automation.Language.ScriptBlockAst]$Ast
33 )
34
35 PROCESS {
36
37 $variables = $Ast.FindAll({$args[0].GetType().Name -like 'VariableExpressionAst'}, $true).Where({$_.VariablePath.UserPath -ne '_'})
38
39 $parameters = $Ast.FindAll({$args[0].GetType().Name -like 'ParameterAst'}, $true)
40
41 $diff = Compare-Object -ReferenceObject $parameters.Name.VariablePath.UserPath -DifferenceObject $variables.VariablePath.UserPath -IncludeEqual
42
43 foreach ($variable in $variables) {
44
45 [pscustomobject]@{
46 Name = $variable.VariablePath.UserPath
47 Type = if ($variable.VariablePath.UserPath -in $diff.Where({$_.SideIndicator -eq '=='}).InputObject) {
48 'Parameter'
49 } else {
50 'UserDefined'
51 }
52 LineNumber = $variable.Extent.StartLineNumber
53 Column = $variable.Extent.StartColumnNumber
54 }
55
56 }
57
58 }
59
60}
You can pipe the output of Get-MrAst
to Get-MrVariableType
or provide the results via parameter
input with the Ast
parameter.
1Get-MrAst -Path C:\MrGit\Inspector\MrInspector\public\Get-MrSyntax.ps1 |
2Get-MrVariableType
You can also sort the output with Sort-Object
to see where you've specified variables in a
different case. Notice the differences with the file
and true
variables in the following results.
1Get-MrAst -Path C:\MrGit\Inspector\MrInspector\public\Get-MrSyntax.ps1 |
2Get-MrVariableType |
3Sort-Object -Property Name
You could write a unit test using Pester to identity when parameters and
variables don't adhere to your standards. You could also write code that uses the output of
Get-MrVariableType
to automate the remediation of variable casing.
The Get-MrVariableType
function shown in this blog article is part of my
MrModuleTools PowerShell module. You
can install it from the PowerShell Gallery using the following command:
1Install-Module -Name MrModuleTools
The video shown below demonstrates how to use the
Get-MrVariableType
function.
You can download the source code from my ModuleTools GitHub repo. Feel free to submit a GitHub issue if you have suggestions or find problems. I accept pull requests if you would like to contribute.
µ