diff --git a/Rules/UseDeclaredVarsMoreThanAssignments.cs b/Rules/UseDeclaredVarsMoreThanAssignments.cs index fc72e18ea..6d0f29fdf 100644 --- a/Rules/UseDeclaredVarsMoreThanAssignments.cs +++ b/Rules/UseDeclaredVarsMoreThanAssignments.cs @@ -9,6 +9,7 @@ #endif using System.Globalization; using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; +using System.Linq; namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { @@ -113,7 +114,7 @@ private IEnumerable AnalyzeScriptBlockAst(ScriptBlockAst scrip IEnumerable varAsts = scriptBlockAst.FindAll(testAst => testAst is VariableExpressionAst, true); IEnumerable varsInAssignment; - Dictionary assignments = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary assignmentsDictionary_OrdinalIgnoreCase = new Dictionary(StringComparer.OrdinalIgnoreCase); string varKey; bool inAssignment; @@ -148,9 +149,9 @@ private IEnumerable AnalyzeScriptBlockAst(ScriptBlockAst scrip { string variableName = Helper.Instance.VariableNameWithoutScope(assignmentVarAst.VariablePath); - if (!assignments.ContainsKey(variableName)) + if (!assignmentsDictionary_OrdinalIgnoreCase.ContainsKey(variableName)) { - assignments.Add(variableName, assignmentAst); + assignmentsDictionary_OrdinalIgnoreCase.Add(variableName, assignmentAst); } } } @@ -163,9 +164,9 @@ private IEnumerable AnalyzeScriptBlockAst(ScriptBlockAst scrip varKey = Helper.Instance.VariableNameWithoutScope(varAst.VariablePath); inAssignment = false; - if (assignments.ContainsKey(varKey)) + if (assignmentsDictionary_OrdinalIgnoreCase.ContainsKey(varKey)) { - varsInAssignment = assignments[varKey].Left.FindAll(testAst => testAst is VariableExpressionAst, true); + varsInAssignment = assignmentsDictionary_OrdinalIgnoreCase[varKey].Left.FindAll(testAst => testAst is VariableExpressionAst, true); // Checks if this variableAst is part of the logged assignment foreach (VariableExpressionAst varInAssignment in varsInAssignment) @@ -195,23 +196,41 @@ private IEnumerable AnalyzeScriptBlockAst(ScriptBlockAst scrip if (!inAssignment) { - assignments.Remove(varKey); + assignmentsDictionary_OrdinalIgnoreCase.Remove(varKey); } // Check if variable belongs to PowerShell built-in variables if (Helper.Instance.HasSpecialVars(varKey)) { - assignments.Remove(varKey); + assignmentsDictionary_OrdinalIgnoreCase.Remove(varKey); } } } } - foreach (string key in assignments.Keys) + // Detect usages of Get-Variable + var getVariableCmdletNamesAndAliases = Helper.Instance.CmdletNameAndAliases("Get-Variable"); + IEnumerable getVariableCommandAsts = scriptBlockAst.FindAll(testAst => testAst is CommandAst commandAst && + getVariableCmdletNamesAndAliases.Contains(commandAst.GetCommandName(), StringComparer.OrdinalIgnoreCase), true); + foreach (CommandAst getVariableCommandAst in getVariableCommandAsts) + { + var commandElements = getVariableCommandAst.CommandElements.ToList(); + // The following extracts the variable name only in the simplest possibe case 'Get-Variable variableName' + if (commandElements.Count == 2 && commandElements[1] is StringConstantExpressionAst constantExpressionAst) + { + var variableName = constantExpressionAst.Value; + if (assignmentsDictionary_OrdinalIgnoreCase.ContainsKey(variableName)) + { + assignmentsDictionary_OrdinalIgnoreCase.Remove(variableName); + } + } + } + + foreach (string key in assignmentsDictionary_OrdinalIgnoreCase.Keys) { yield return new DiagnosticRecord( string.Format(CultureInfo.CurrentCulture, Strings.UseDeclaredVarsMoreThanAssignmentsError, key), - assignments[key].Left.Extent, + assignmentsDictionary_OrdinalIgnoreCase[key].Left.Extent, GetName(), DiagnosticSeverity.Warning, fileName, diff --git a/Tests/Rules/UseDeclaredVarsMoreThanAssignments.tests.ps1 b/Tests/Rules/UseDeclaredVarsMoreThanAssignments.tests.ps1 index c35548241..972c5cf82 100644 --- a/Tests/Rules/UseDeclaredVarsMoreThanAssignments.tests.ps1 +++ b/Tests/Rules/UseDeclaredVarsMoreThanAssignments.tests.ps1 @@ -87,5 +87,10 @@ function MyFunc2() { $results = Invoke-ScriptAnalyzer -ScriptDefinition '$env:foo = 1; function foo(){ $env:bar = 42 }' $results.Count | Should -Be 0 } + + It "Using a variable via 'Get-Variable' does not trigger a warning" { + $noViolations = Invoke-ScriptAnalyzer -ScriptDefinition '$a=4; get-variable a' + $noViolations.Count | Should -Be 0 + } } }