From a4c23ca8c72e6d2e5228930a1b47dc66634dfc5a Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 3 Dec 2017 23:04:32 +0000 Subject: [PATCH 1/3] Fix a nullreference that occured for '$MyObj | % { @{$_.Name = $_.Value} }' when the PSAlignAssignmentStatement setting CheckHashtable was turned on. The reason for it was because the previous implementation expected the key in a hashtable to not contain more than 1 token expression. --- Rules/AlignAssignmentStatement.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Rules/AlignAssignmentStatement.cs b/Rules/AlignAssignmentStatement.cs index 37566862e..ec9791746 100644 --- a/Rules/AlignAssignmentStatement.cs +++ b/Rules/AlignAssignmentStatement.cs @@ -301,17 +301,15 @@ private static List> GetExtents( { var keyStartOffset = kvp.Item1.Extent.StartOffset; var keyTokenNode = tokenOps.GetTokenNodes( - token => token.Extent.StartOffset == keyStartOffset).FirstOrDefault(); - if (keyTokenNode == null - || keyTokenNode.Next == null - || keyTokenNode.Next.Value.Kind != TokenKind.Equals) + token => token.Kind == TokenKind.Equals).FirstOrDefault(); + if (keyTokenNode == null || keyTokenNode.Value == null) { - return null; + continue; } + var assignmentToken = keyTokenNode.Value.Extent; nodeTuples.Add(new Tuple( - kvp.Item1.Extent, - keyTokenNode.Next.Value.Extent)); + kvp.Item1.Extent, assignmentToken)); } return nodeTuples; From 39bf29276c1eb9235eb60f2d2ab6d2100841addd Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 4 Dec 2017 00:12:35 +0000 Subject: [PATCH 2/3] fix regression in previous commit to traverse the tokenlist at least until the keyStartOffset and then search for the equal token. Works mainly fine except that I get an off by one error, which leads to a false positive warning when analysing the settings file provided by issue 828. --- Rules/AlignAssignmentStatement.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Rules/AlignAssignmentStatement.cs b/Rules/AlignAssignmentStatement.cs index ec9791746..a17b4afd6 100644 --- a/Rules/AlignAssignmentStatement.cs +++ b/Rules/AlignAssignmentStatement.cs @@ -300,8 +300,20 @@ private static List> GetExtents( foreach (var kvp in hashtableAst.KeyValuePairs) { var keyStartOffset = kvp.Item1.Extent.StartOffset; + bool keyStartOffSetReached = false; var keyTokenNode = tokenOps.GetTokenNodes( - token => token.Kind == TokenKind.Equals).FirstOrDefault(); + token => + { + if (keyStartOffSetReached) + { + return token.Kind == TokenKind.Equals; + } + if (token.Extent.StartOffset == keyStartOffset) + { + keyStartOffSetReached = true; + } + return false; + }).FirstOrDefault(); if (keyTokenNode == null || keyTokenNode.Value == null) { continue; From f4669d57202cf9d8fd54a29f34be449338be0013 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 20 Jan 2018 22:04:58 +0000 Subject: [PATCH 3/3] Add test for fix of issue 828 --- .../SettingsTest/Issue828/Issue828.tests.ps1 | 27 ++++++++ .../Issue828/PSScriptAnalyzerSettings.psd1 | 63 +++++++++++++++++++ Tests/Engine/SettingsTest/Issue828/script.ps1 | 2 + 3 files changed, 92 insertions(+) create mode 100644 Tests/Engine/SettingsTest/Issue828/Issue828.tests.ps1 create mode 100644 Tests/Engine/SettingsTest/Issue828/PSScriptAnalyzerSettings.psd1 create mode 100644 Tests/Engine/SettingsTest/Issue828/script.ps1 diff --git a/Tests/Engine/SettingsTest/Issue828/Issue828.tests.ps1 b/Tests/Engine/SettingsTest/Issue828/Issue828.tests.ps1 new file mode 100644 index 000000000..851256485 --- /dev/null +++ b/Tests/Engine/SettingsTest/Issue828/Issue828.tests.ps1 @@ -0,0 +1,27 @@ +Describe "Issue 828: No NullReferenceExceptionin AlignAssignmentStatement rule when CheckHashtable is enabled" { + It "Should not throw" { + # For details, see here: https://github.com/PowerShell/PSScriptAnalyzer/issues/828 + # The issue states basically that calling 'Invoke-ScriptAnalyzer .' with a certain settings file being in the same location that has CheckHashtable enabled + # combined with a script contatining the command '$MyObj | % { @{$_.Name = $_.Value} }' could make it throw a NullReferencException. + $cmdletThrewError = $false + $initialErrorActionPreference = $ErrorActionPreference + $initialLocation = Get-Location + try + { + Set-Location $PSScriptRoot + $ErrorActionPreference = 'Stop' + Invoke-ScriptAnalyzer . + } + catch + { + $cmdletThrewError = $true + } + finally + { + $ErrorActionPreference = $initialErrorActionPreference + Set-Location $initialLocation + } + + $cmdletThrewError | Should Be $false + } +} \ No newline at end of file diff --git a/Tests/Engine/SettingsTest/Issue828/PSScriptAnalyzerSettings.psd1 b/Tests/Engine/SettingsTest/Issue828/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 000000000..9ac432ef9 --- /dev/null +++ b/Tests/Engine/SettingsTest/Issue828/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,63 @@ +@{ + Severity = @( + 'Error', + 'Warning', + 'Information' + ) + ExcludeRules = @( + 'PSUseOutputTypeCorrectly', + 'PSUseShouldProcessForStateChangingFunctions' + ) + Rules = @{ + PSAlignAssignmentStatement = @{ + Enable = $true + CheckHashtable = $true + } + PSAvoidUsingCmdletAliases = @{ + # only whitelist verbs from *-Object cmdlets + Whitelist = @( + '%', + '?', + 'compare', + 'foreach', + 'group', + 'measure', + 'select', + 'sort', + 'tee', + 'where' + ) + } + PSPlaceCloseBrace = @{ + Enable = $true + NoEmptyLineBefore = $false + IgnoreOneLineBlock = $true + NewLineAfter = $false + } + PSPlaceOpenBrace = @{ + Enable = $true + OnSameLine = $true + NewLineAfter = $true + IgnoreOneLineBlock = $true + } + PSProvideCommentHelp = @{ + Enable = $true + ExportedOnly = $true + BlockComment = $true + VSCodeSnippetCorrection = $true + Placement = "before" + } + PSUseConsistentIndentation = @{ + Enable = $true + IndentationSize = 4 + Kind = "space" + } + PSUseConsistentWhitespace = @{ + Enable = $true + CheckOpenBrace = $true + CheckOpenParen = $true + CheckOperator = $false + CheckSeparator = $true + } + } +} \ No newline at end of file diff --git a/Tests/Engine/SettingsTest/Issue828/script.ps1 b/Tests/Engine/SettingsTest/Issue828/script.ps1 new file mode 100644 index 000000000..95d3f7edd --- /dev/null +++ b/Tests/Engine/SettingsTest/Issue828/script.ps1 @@ -0,0 +1,2 @@ +# This script has to be like that in order to reproduce the issue +$MyObj | % { @{$_.Name = $_.Value} } \ No newline at end of file