Skip to content

Commit e76b239

Browse files
authored
Merge pull request #66 from aaronparker/updates
Updates
2 parents 79159bc + ddb9d55 commit e76b239

18 files changed

Lines changed: 599 additions & 167 deletions

.rules/LowercaseKeyword.psm1

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic
2+
3+
function Measure-LowercaseKeyword {
4+
<#
5+
.SYNOPSIS
6+
PowerShell keywords and constants should be in lowercase.
7+
8+
.DESCRIPTION
9+
PowerShell keywords (function, if, foreach, etc.) and constants ($true, $false, $null)
10+
should use lowercase for consistency and best practices.
11+
12+
.EXAMPLE
13+
Measure-LowercaseKeyword -ScriptBlockAst $ScriptBlockAst
14+
15+
.INPUTS
16+
[System.Management.Automation.Language.ScriptBlockAst]
17+
18+
.OUTPUTS
19+
[Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
20+
#>
21+
[CmdletBinding()]
22+
[OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
23+
param (
24+
[Parameter(Mandatory = $true)]
25+
[ValidateNotNullOrEmpty()]
26+
[System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst
27+
)
28+
29+
process {
30+
$Results = @()
31+
32+
# Define keywords and constants we want to check
33+
$Keywords = @('function', 'foreach', 'if', 'else', 'elseif', 'return', 'switch', 'param',
34+
'begin', 'process', 'end', 'in', 'do', 'while', 'until', 'for', 'trap',
35+
'throw', 'catch', 'try', 'finally', 'data', 'dynamicparam', 'break',
36+
'continue', 'exit', 'class', 'enum', 'using')
37+
$Constants = @('$true', '$false', '$null')
38+
39+
try {
40+
# Get all tokens from the script
41+
$Tokens = @()
42+
$ParseErrors = @()
43+
[void][System.Management.Automation.Language.Parser]::ParseInput(
44+
$ScriptBlockAst.ToString(),
45+
[ref]$Tokens,
46+
[ref]$ParseErrors
47+
)
48+
49+
if ($ParseErrors.Count -gt 0) {
50+
return $Results
51+
}
52+
53+
# Track processed token positions to avoid duplicates
54+
$ProcessedTokens = @{}
55+
56+
foreach ($Token in $Tokens) {
57+
$TokenText = $Token.Text
58+
$LowerTokenText = $TokenText.ToLower()
59+
60+
# Create a unique key for this token position
61+
$TokenKey = "$($Token.Extent.StartLineNumber):$($Token.Extent.StartColumnNumber):$TokenText"
62+
63+
# Skip if we've already processed this exact token at this position
64+
if ($ProcessedTokens.ContainsKey($TokenKey)) {
65+
continue
66+
}
67+
68+
# Check keywords (check text content directly to be more reliable)
69+
if ($Keywords -contains $LowerTokenText -and $TokenText -cne $LowerTokenText) {
70+
$Results += [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{
71+
Message = "Keyword '$TokenText' should be lowercase ('$LowerTokenText')."
72+
Extent = $Token.Extent
73+
RuleName = $PSCmdlet.MyInvocation.InvocationName
74+
Severity = 'Warning'
75+
}
76+
$ProcessedTokens[$TokenKey] = $true
77+
}
78+
# Check constants
79+
elseif ($Constants -contains $LowerTokenText -and $TokenText -cne $LowerTokenText) {
80+
$Results += [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{
81+
Message = "Constant '$TokenText' should be lowercase ('$LowerTokenText')."
82+
Extent = $Token.Extent
83+
RuleName = $PSCmdlet.MyInvocation.InvocationName
84+
Severity = 'Warning'
85+
}
86+
$ProcessedTokens[$TokenKey] = $true
87+
}
88+
}
89+
90+
return $Results
91+
}
92+
catch {
93+
$PSCmdlet.ThrowTerminatingError($_)
94+
}
95+
}
96+
}
97+
98+
Export-ModuleMember -Function Measure-LowercaseKeyword
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
@{
2+
CustomRulePath = @(
3+
".rules/LowercaseKeyword.psm1"
4+
)
5+
IncludeDefaultRules = $true
6+
Severity = @("Error", "Warning")
7+
IncludeRules = @(
8+
"Measure-LowercaseKeyword"
9+
)
10+
Rules = @{
11+
PSUseCompatibleCmdlets = @{
12+
Compatibility = @(
13+
'desktop-5.1.14393.206-windows'
14+
'core-6.1.0-windows'
15+
'core-6.1.0-linux'
16+
'core-6.1.0-linux-arm'
17+
'core-6.1.0-macos'
18+
)
19+
}
20+
PSUseCompatibleSyntax = @{
21+
TargetedVersions = @(
22+
'7.0'
23+
'6.0'
24+
'5.1'
25+
)
26+
}
27+
}
28+
}

.rules/PascalCase.psm1

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic
2+
3+
function Measure-PascalCase {
4+
<#
5+
.SYNOPSIS
6+
The variables names should be in PascalCase.
7+
8+
.DESCRIPTION
9+
Variable names should use a consistent capitalization style, i.e. : PascalCase.
10+
In PascalCase, only the first letter is capitalized. Or, if the variable name is made of multiple concatenated words,
11+
only the first letter of each concatenated word is capitalized.
12+
To fix a violation of this rule, please consider using PascalCase for variable names.
13+
14+
.EXAMPLE
15+
Measure-PascalCase -ScriptBlockAst $ScriptBlockAst
16+
17+
.INPUTS
18+
[System.Management.Automation.Language.ScriptBlockAst]
19+
20+
.OUTPUTS
21+
[Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
22+
23+
.NOTES
24+
https://msdn.microsoft.com/en-us/library/dd878270(v=vs.85).aspx
25+
https://msdn.microsoft.com/en-us/library/ms229043(v=vs.110).aspx
26+
#>
27+
[CmdletBinding()]
28+
[OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
29+
param (
30+
[Parameter(Mandatory = $true)]
31+
[ValidateNotNullOrEmpty()]
32+
[System.Management.Automation.Language.ScriptBlockAst]
33+
$ScriptBlockAst
34+
)
35+
36+
process {
37+
$Results = @()
38+
try {
39+
#region Define predicates to find ASTs.
40+
[ScriptBlock]$Predicate = {
41+
param ([System.Management.Automation.Language.Ast]$Ast)
42+
[bool]$ReturnValue = $false
43+
if ($Ast -is [System.Management.Automation.Language.AssignmentStatementAst]) {
44+
[System.Management.Automation.Language.AssignmentStatementAst]$VariableAst = $Ast
45+
if ($VariableAst.Left.VariablePath.UserPath -cnotmatch '^([A-Z][a-z]+)+$') {
46+
$ReturnValue = $true
47+
}
48+
}
49+
return $ReturnValue
50+
}
51+
#endregion
52+
53+
#region Finds ASTs that match the predicates.
54+
[System.Management.Automation.Language.Ast[]]$Violations = $ScriptBlockAst.FindAll($Predicate, $true)
55+
if ($Violations.Count -ne 0) {
56+
foreach ($Violation in $Violations) {
57+
$Result = New-Object `
58+
-TypeName "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" `
59+
-ArgumentList "$((Get-Help $MyInvocation.MyCommand.Name).Description.Text)", $Violation.Extent, $PSCmdlet.MyInvocation.InvocationName, Information, $null
60+
$Results += $Result
61+
}
62+
}
63+
return $Results
64+
#endregion
65+
}
66+
catch {
67+
$PSCmdlet.ThrowTerminatingError($_)
68+
}
69+
}
70+
}
71+
72+
Export-ModuleMember -Function Measure-PascalCase
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@{
2+
Severity = @('Error', 'Warning')
3+
Rules = @{
4+
PSUseCompatibleCmdlets = @{
5+
Compatibility = @(
6+
'desktop-5.1.14393.206-windows'
7+
'core-6.1.0-windows'
8+
'core-6.1.0-linux'
9+
'core-6.1.0-linux-arm'
10+
'core-6.1.0-macos'
11+
)
12+
}
13+
PSUseCompatibleSyntax = @{
14+
TargetedVersions = @(
15+
'7.0'
16+
'6.0'
17+
'5.1'
18+
)
19+
}
20+
}
21+
ExcludeRules = @('PSAvoidUsingWriteHost')
22+
}

.vscode/launch.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "PowerShell Launch Current File",
9+
"type": "PowerShell",
10+
"request": "launch",
11+
"script": "${file}",
12+
"args": [],
13+
"cwd": "${file}"
14+
},
15+
{
16+
"name": "PowerShell Launch Current File in Temporary Console",
17+
"type": "PowerShell",
18+
"request": "launch",
19+
"script": "${file}",
20+
"args": [],
21+
"cwd": "${file}",
22+
"createTemporaryIntegratedConsole": true
23+
},
24+
{
25+
"name": "PowerShell Launch Current File w/Args Prompt",
26+
"type": "PowerShell",
27+
"request": "launch",
28+
"script": "${file}",
29+
"args": [
30+
"${command:SpecifyScriptArgs}"
31+
],
32+
"cwd": "${file}"
33+
},
34+
{
35+
"name": "PowerShell Attach to Host Process",
36+
"type": "PowerShell",
37+
"request": "attach"
38+
},
39+
{
40+
"name": "PowerShell Interactive Session",
41+
"type": "PowerShell",
42+
"request": "launch",
43+
"cwd": ""
44+
},
45+
{
46+
"name": "PowerShell Attach Interactive Session Runspace",
47+
"type": "PowerShell",
48+
"request": "attach",
49+
"processId": "current"
50+
}
51+
]
52+
}

0 commit comments

Comments
 (0)