Skip to content

Commit c94632c

Browse files
author
quoctruong
committed
Merge pull request #9 from PowerShell/dscresource-quoc
Implement Return Correct Types For DSC Functions rule
2 parents aa9f440 + 1772ad9 commit c94632c

12 files changed

+1096
-77
lines changed

Engine/Helper.cs

Lines changed: 895 additions & 32 deletions
Large diffs are not rendered by default.

Engine/VariableAnalysisBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,7 @@ public AssignmentTarget(AssignmentStatementAst ast)
12051205
_rightHandSideVariable = new VariableTarget(cmExAst.Expression as VariableExpressionAst);
12061206
if (String.Equals((exprAst as VariableExpressionAst).VariablePath.UserPath, "this", StringComparison.OrdinalIgnoreCase))
12071207
{
1208-
1208+
Constant = SpecialVars.ThisVariable;
12091209
}
12101210
}
12111211
//Store the type info for variable assignment from .Net type

Rules/ReturnCorrectTypesForDSCFunctions.cs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,55 @@ public class ReturnCorrectTypesForDSCFunctions : IDSCResourceRule
3030
/// <returns>The results of the analysis</returns>
3131
public IEnumerable<DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName)
3232
{
33+
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
34+
3335
// TODO: Add logic for DSC Resources
34-
return Enumerable.Empty<DiagnosticRecord>();
36+
37+
IEnumerable<Ast> functionDefinitionAsts = Helper.Instance.DscResourceFunctions(ast);
38+
39+
IEnumerable<TypeDefinitionAst> classes = ast.FindAll(item =>
40+
item is TypeDefinitionAst
41+
&& ((item as TypeDefinitionAst).IsClass), true).Cast<TypeDefinitionAst>();
42+
43+
foreach (FunctionDefinitionAst func in functionDefinitionAsts)
44+
{
45+
Helper.Instance.InitializeVariableAnalysis(func);
46+
List<Tuple<string, StatementAst>> outputTypes = FindPipelineOutput.OutputTypes(func, classes);
47+
48+
if (String.Equals(func.Name, "Set-TargetResource", StringComparison.OrdinalIgnoreCase))
49+
{
50+
foreach (Tuple<string, StatementAst> outputType in outputTypes)
51+
{
52+
yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReturnCorrectTypesForSetTargetResourceFunctionsDSCError),
53+
outputType.Item2.Extent, GetName(), DiagnosticSeverity.Strict, fileName);
54+
}
55+
}
56+
else
57+
{
58+
Dictionary<string, string> returnTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
59+
returnTypes["Get-TargetResource"] = typeof(System.Collections.Hashtable).FullName;
60+
returnTypes["Test-TargetResource"] = typeof(bool).FullName;
61+
62+
foreach (Tuple<string, StatementAst> outputType in outputTypes)
63+
{
64+
string type = outputType.Item1;
65+
66+
if (String.IsNullOrEmpty(type)
67+
|| String.Equals(typeof(Unreached).FullName, type, StringComparison.OrdinalIgnoreCase)
68+
|| String.Equals(typeof(Undetermined).FullName, type, StringComparison.OrdinalIgnoreCase)
69+
|| String.Equals(typeof(object).FullName, type, StringComparison.OrdinalIgnoreCase)
70+
|| String.Equals(type, returnTypes[func.Name], StringComparison.OrdinalIgnoreCase))
71+
{
72+
continue;
73+
}
74+
else
75+
{
76+
yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReturnCorrectTypesForGetTestTargetResourceFunctionsDSCResourceError,
77+
func.Name, returnTypes[func.Name], type), outputType.Item2.Extent, GetName(), DiagnosticSeverity.Strict, fileName);
78+
}
79+
}
80+
}
81+
}
3582
}
3683

3784
/// <summary>
@@ -44,7 +91,6 @@ public IEnumerable<DiagnosticRecord> AnalyzeDSCClass(Ast ast, string fileName)
4491
{
4592
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
4693

47-
4894
IEnumerable<TypeDefinitionAst> classes = ast.FindAll(item =>
4995
item is TypeDefinitionAst
5096
&& ((item as TypeDefinitionAst).IsClass), true).Cast<TypeDefinitionAst>();
@@ -56,8 +102,8 @@ item is TypeDefinitionAst
56102
foreach (TypeDefinitionAst dscClass in dscClasses)
57103
{
58104
Dictionary<string, string> returnTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
59-
returnTypes["Get"] = dscClass.Name;
60105
returnTypes["Test"] = typeof(bool).FullName;
106+
returnTypes["Get"] = dscClass.Name;
61107

62108
foreach (var member in dscClass.Members)
63109
{
@@ -103,7 +149,7 @@ item is TypeDefinitionAst
103149
ret.Extent, GetName(), DiagnosticSeverity.Strict, fileName);
104150
}
105151

106-
string typeName = Helper.Instance.GetTypeFromReturnStatementAst(funcAst, ret, classes, ast);
152+
string typeName = Helper.Instance.GetTypeFromReturnStatementAst(funcAst, ret, classes);
107153

108154
// This also includes the case of return $this because the type of this is unreached.
109155
if (String.IsNullOrEmpty(typeName)

Rules/Strings.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rules/Strings.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,10 @@
648648
<data name="ReturnCorrectTypesForSetFunctionsDSCError" xml:space="preserve">
649649
<value>Set function in DSC Class {0} should not return anything</value>
650650
</data>
651+
<data name="ReturnCorrectTypesForGetTestTargetResourceFunctionsDSCResourceError" xml:space="preserve">
652+
<value>{0} function in DSC Resource should return object of type {1} instead of {2}</value>
653+
</data>
654+
<data name="ReturnCorrectTypesForSetTargetResourceFunctionsDSCError" xml:space="preserve">
655+
<value>Set-TargetResource function in DSC Resource should not output anything to the pipeline.</value>
656+
</data>
651657
</root>

Rules/UseIdenticalMandatoryParametersDSC.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ public IEnumerable<DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName
2626
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
2727

2828
// Expected TargetResource functions in the DSC Resource module
29-
List<string> expectedTargetResourceFunctionNames = new List<string>(new string[] { "Set-TargetResource", "Test-TargetResource", "Get-TargetResource" });
30-
31-
IEnumerable<Ast> functionDefinitionAsts = ast.FindAll(item => item is FunctionDefinitionAst && expectedTargetResourceFunctionNames.Contains((item as FunctionDefinitionAst).Name, StringComparer.OrdinalIgnoreCase), true);
29+
List<string> expectedTargetResourceFunctionNames = new List<string>(new string[] { "Set-TargetResource", "Test-TargetResource", "Get-TargetResource" });
30+
31+
IEnumerable<Ast> functionDefinitionAsts = Helper.Instance.DscResourceFunctions(ast);
3232

3333
// Dictionary to keep track of Mandatory parameters and their presence in Get/Test/Set TargetResource cmdlets
3434
Dictionary<string, List<string>> mandatoryParameters = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

Rules/UseIdenticalParametersDSC.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,10 @@ public IEnumerable<DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName
3232
{
3333
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
3434

35-
// Expected TargetResource functions in the DSC Resource module
36-
List<string> expectedTargetResourceFunctionNames = new List<string>(new string[] { "Set-TargetResource", "Test-TargetResource" });
37-
3835
// parameters
3936
Dictionary<string, ParameterAst> paramNames = new Dictionary<string, ParameterAst>(StringComparer.OrdinalIgnoreCase);
4037

41-
IEnumerable<Ast> functionDefinitionAsts = ast.FindAll(item => item is FunctionDefinitionAst
42-
&& expectedTargetResourceFunctionNames.Contains((item as FunctionDefinitionAst).Name, StringComparer.OrdinalIgnoreCase), true);
38+
IEnumerable<Ast> functionDefinitionAsts = Helper.Instance.DscResourceFunctions(ast);
4339

4440
if (functionDefinitionAsts.Count() == 2)
4541
{

Tests/Rules/DSCResources/BadDscResource/BadDscResource.psm1

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,14 @@ class FileResource
103103
{
104104
return $present
105105
}
106-
else
106+
elseif ($true)
107107
{
108108
return -not $present
109109
}
110+
else
111+
{
112+
return 5
113+
}
110114
}
111115

112116

@@ -119,20 +123,27 @@ class FileResource
119123
[FileResource] Get()
120124
{
121125
$present = $this.TestFilePath($this.Path)
126+
127+
$hashtable = @{"3"="4"}
122128

123129
if ($present)
124130
{
125131
$file = Get-ChildItem -LiteralPath $this.Path
126132
$this.CreationTime = $file.CreationTime
127133
$this.Ensure = [Ensure]::Present
134+
return $this.TestFilePath($this.Path)
128135
}
129-
else
136+
elseif ($true)
130137
{
131138
$this.CreationTime = $null
132139
$this.Ensure = [Ensure]::Absent
133-
}
140+
return $present
141+
}
142+
else
143+
{
144+
return $hashtable
145+
}
134146

135-
return $this
136147
}
137148

138149
<#

Tests/Rules/DSCResources/MSFT_WaitForAll/MSFT_WaitForAll.psm1

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ function Set-TargetResource
9494
-RetryCount $RetryCount `
9595
-ThrottleLimit $ThrottleLimit
9696
}
97+
98+
$true -and $false
99+
100+
if ($true) {
101+
return 4;
102+
}
97103
}
98104

99105
#
@@ -118,14 +124,22 @@ function Test-TargetResource
118124

119125
Import-Module $PSScriptRoot\..\..\PSDSCxMachine.psm1
120126

121-
return PSDSCxMachine\Test-_InternalPSDscXMachineTR `
122-
-RemoteResourceId $ResourceName `
123-
-RemoteMachine $NodeName `
124-
-RemoteCredential $Credential `
125-
-MinimalNumberOfMachineInState $NodeName.Count `
126-
-RetryIntervalSec $RetryIntervalSec `
127-
-RetryCount $RetryCount `
128-
-ThrottleLimit $ThrottleLimit
127+
$b = @{"Test"=3}
128+
$b
129+
130+
$c = [Math]::Sin(3)
131+
$c
132+
133+
$d = [bool[]]@($true, $false)
134+
135+
$d
136+
137+
foreach ($d in $b)
138+
{
139+
$test
140+
}
141+
142+
return $true
129143
}
130144

131145

Tests/Rules/DSCResources/MSFT_WaitForAny/MSFT_WaitForAny.psm1

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,22 @@ function Get-TargetResource
3030
)
3131

3232
Import-Module $PSScriptRoot\..\..\PSDSCxMachine.psm1
33+
34+
$b = @{"hash" = "table"}
3335

34-
return PSDSCxMachine\Get-_InternalPSDscXMachineTR `
35-
-RemoteResourceId $ResourceName `
36-
-RemoteMachine $NodeName `
37-
-RemoteCredential $Credential `
38-
-MinimalNumberOfMachineInState 1 `
39-
-RetryIntervalSec $RetryIntervalSec `
40-
-RetryCount $RetryCount `
41-
-ThrottleLimit $ThrottleLimit
36+
if ($true)
37+
{
38+
return $b;
39+
}
40+
elseif ($c)
41+
{
42+
return @{"hash2"="table2"}
43+
}
44+
else
45+
{
46+
# can't determine type of c so error should not be raised as we're trying to be conservative
47+
return $c;
48+
}
4249
}
4350

4451
#
@@ -125,14 +132,25 @@ function Test-TargetResource
125132

126133
Import-Module $PSScriptRoot\..\..\PSDSCxMachine.psm1
127134

128-
return PSDSCxMachine\Test-_InternalPSDscXMachineTR `
129-
-RemoteResourceId $ResourceName `
130-
-RemoteMachine $NodeName `
131-
-RemoteCredential $Credential `
132-
-MinimalNumberOfMachineInState 1 `
133-
-RetryIntervalSec $RetryIntervalSec `
134-
-RetryCount $RetryCount `
135-
-ThrottleLimit $ThrottleLimit
135+
$a = $true
136+
$a
137+
138+
if ($true)
139+
{
140+
$false;
141+
}
142+
elseif ($b)
143+
{
144+
return $a -or $true
145+
}
146+
elseif ($c)
147+
{
148+
return $false;
149+
}
150+
else
151+
{
152+
return $true
153+
}
136154
}
137155

138156

0 commit comments

Comments
 (0)