Skip to content

Commit 4dc2ce4

Browse files
committed
Rule to detect the absence of Verbose statements in DSC Resource
1 parent 5908771 commit 4dc2ce4

File tree

5 files changed

+87
-112
lines changed

5 files changed

+87
-112
lines changed

Rules/ScriptAnalyzerBuiltinRules.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
<Compile Include="MissingModuleManifestField.cs" />
7676
<Compile Include="PossibleIncorrectComparisonWithNull.cs" />
7777
<Compile Include="ProvideCommentHelp.cs" />
78-
<Compile Include="ProvideVerboseMessage.cs" />
78+
<Compile Include="UseVerboseMessageInDSCResouce.cs" />
7979
<Compile Include="Strings.Designer.cs">
8080
<AutoGen>True</AutoGen>
8181
<DesignTime>True</DesignTime>

Rules/Strings.Designer.cs

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

Rules/Strings.resx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -276,17 +276,14 @@
276276
<data name="SourceName" xml:space="preserve">
277277
<value>PS</value>
278278
</data>
279-
<data name="ProvideVerboseMessageDescription" xml:space="preserve">
280-
<value>Checks that Write-Verbose is called at least once in every cmdlet or script. This is in line with the PowerShell best practices.</value>
279+
<data name="UseVerboseMessageInDSCResourceDescription" xml:space="preserve">
280+
<value>It is a best practice to emit informative, verbose messages in DSC resource functions. This helps in debugging issues when a DSC configuration is executed.</value>
281281
</data>
282-
<data name="ProvideVerboseMessageErrorFunction" xml:space="preserve">
283-
<value>There is no call to Write-Verbose in the function ‘{0}’.</value>
282+
<data name="UseVerboseMessageInDSCResourceErrorFunction" xml:space="preserve">
283+
<value>There is no call to Write-Verbose in DSC function ‘{0}’. If you are using Write-Verbose in a helper function, suppress this rule application.</value>
284284
</data>
285-
<data name="ProvideVerboseMessageCommonName" xml:space="preserve">
286-
<value>Verbose</value>
287-
</data>
288-
<data name="ProvideVerboseMessageScript" xml:space="preserve">
289-
<value>There is no call to Write-Verbose in the script.</value>
285+
<data name="UseVerboseMessageInDSCResourceCommonName" xml:space="preserve">
286+
<value>Use verbose message in DSC resource</value>
290287
</data>
291288
<data name="MissingModuleManifestFieldDescription" xml:space="preserve">
292289
<value>Some fields of the module manifest (such as ModuleVersion) are required.</value>
@@ -459,8 +456,8 @@
459456
<data name="MissingModuleManifestFieldName" xml:space="preserve">
460457
<value>MissingModuleManifestField</value>
461458
</data>
462-
<data name="ProvideVerboseMessageName" xml:space="preserve">
463-
<value>ProvideVerboseMessage</value>
459+
<data name="UseVerboseMessageInDSCResourceName" xml:space="preserve">
460+
<value>UseVerboseMessageInDSCResource</value>
464461
</data>
465462
<data name="CommandNotFoundCommonName" xml:space="preserve">
466463
<value>Command Not Found</value>

Rules/UseDeclaredVarsMoreThanAssignments.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
3535
{
3636
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
3737

38-
IEnumerable<Ast> assignmentAsts = ast.FindAll(testAst => testAst is AssignmentStatementAst, true);
39-
IEnumerable<Ast> assingmentVarAsts;
38+
IEnumerable<Ast> assignmentAsts = ast.FindAll(testAst => testAst is AssignmentStatementAst, true);
4039
IEnumerable<Ast> varAsts = ast.FindAll(testAst => testAst is VariableExpressionAst, true);
4140
IEnumerable<Ast> varsInAssignment;
4241

Rules/ProvideVerboseMessage.cs renamed to Rules/UseVerboseMessageInDSCResouce.cs

Lines changed: 40 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,78 +22,66 @@
2222
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2323
{
2424
/// <summary>
25-
/// ProvideVerboseMessage: Analyzes the ast to check that Write-Verbose is called at least once in every cmdlet or script.
25+
/// ProvideVerboseMessage: Analyzes the ast to check that Write-Verbose is called for DSC Resources
2626
/// </summary>
27-
[Export(typeof(IScriptRule))]
28-
public class ProvideVerboseMessage : SkipNamedBlock, IScriptRule
27+
[Export(typeof(IDSCResourceRule))]
28+
public class UseVerboseMessageInDSCResouce : SkipNamedBlock, IDSCResourceRule
2929
{
3030
/// <summary>
31-
/// AnalyzeScript: Analyzes the ast to check that Write-Verbose is called at least once in every cmdlet or script.
31+
/// AnalyzeDSCResource: Analyzes the ast to check that Write-Verbose is called for DSC Resources
3232
/// <param name="ast">The script's ast</param>
3333
/// <param name="fileName">The script's file name</param>
3434
/// </summary>
35-
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
35+
public IEnumerable<DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName)
3636
{
37-
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
38-
39-
ClearList();
40-
this.AddNames(new List<string>() { "Configuration", "Workflow" });
41-
DiagnosticRecords.Clear();
42-
43-
this.fileName = fileName;
44-
//We only check that advanced functions should have Write-Verbose
45-
ast.Visit(this);
46-
47-
return DiagnosticRecords;
48-
}
49-
50-
/// <summary>
51-
/// Visit function and checks that it has write verbose
52-
/// </summary>
53-
/// <param name="funcAst"></param>
54-
/// <returns></returns>
55-
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst funcAst)
56-
{
57-
if (funcAst == null)
37+
if (ast == null)
5838
{
59-
return AstVisitAction.SkipChildren;
39+
throw new ArgumentNullException(Strings.NullAstErrorMessage);
6040
}
6141

62-
//Write-Verbose is not required for non-advanced functions
63-
if (funcAst.Body == null || funcAst.Body.ParamBlock == null
64-
|| funcAst.Body.ParamBlock.Attributes == null ||
65-
funcAst.Body.ParamBlock.Parameters == null ||
66-
!funcAst.Body.ParamBlock.Attributes.Any(attr => attr.TypeName.GetReflectionType() == typeof(CmdletBindingAttribute)))
67-
{
68-
return AstVisitAction.Continue;
69-
}
42+
List<string> expectedTargetResourceFunctionNames = new List<string>(new string[] { "Set-TargetResource", "Test-TargetResource", "Get-TargetResource" });
7043

71-
var commandAsts = funcAst.Body.FindAll(testAst => testAst is CommandAst, false);
72-
bool hasVerbose = false;
44+
IEnumerable<Ast> functionDefinitionAsts = Helper.Instance.DscResourceFunctions(ast);
7345

74-
if (commandAsts != null)
46+
foreach (FunctionDefinitionAst functionDefinitionAst in functionDefinitionAsts)
7547
{
76-
foreach (CommandAst commandAst in commandAsts)
48+
var commandAsts = functionDefinitionAst.Body.FindAll(testAst => testAst is CommandAst, false);
49+
bool hasVerbose = false;
50+
51+
if (null != commandAsts)
7752
{
78-
hasVerbose |= String.Equals(commandAst.GetCommandName(), "Write-Verbose", StringComparison.OrdinalIgnoreCase);
53+
foreach (CommandAst commandAst in commandAsts)
54+
{
55+
hasVerbose |= String.Equals(commandAst.GetCommandName(), "Write-Verbose", StringComparison.OrdinalIgnoreCase);
56+
}
7957
}
80-
}
8158

82-
if (!hasVerbose)
83-
{
84-
DiagnosticRecords.Add(new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideVerboseMessageErrorFunction, funcAst.Name),
85-
funcAst.Extent, GetName(), DiagnosticSeverity.Information, fileName));
86-
}
59+
if (!hasVerbose)
60+
{
61+
yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UseVerboseMessageInDSCResourceErrorFunction, functionDefinitionAst.Name),
62+
functionDefinitionAst.Extent, GetName(), DiagnosticSeverity.Information, fileName);
63+
}
8764

88-
return AstVisitAction.Continue;
65+
}
66+
}
67+
68+
/// <summary>
69+
/// AnalyzeDSCClass: This function returns nothing in the case of dsc class.
70+
/// </summary>
71+
/// <param name="ast"></param>
72+
/// <param name="fileName"></param>
73+
/// <returns></returns>
74+
public IEnumerable<DiagnosticRecord> AnalyzeDSCClass(Ast ast, string fileName)
75+
{
76+
return Enumerable.Empty<DiagnosticRecord>();
8977
}
9078

9179
/// <summary>
9280
/// Method: Retrieves the name of this rule.
9381
/// </summary>
9482
public string GetName()
9583
{
96-
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.ProvideVerboseMessageName);
84+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.UseVerboseMessageInDSCResourceName);
9785
}
9886

9987
/// <summary>
@@ -102,7 +90,7 @@ public string GetName()
10290
/// <returns>The common name of this rule</returns>
10391
public string GetCommonName()
10492
{
105-
return string.Format(CultureInfo.CurrentCulture, Strings.ProvideVerboseMessageCommonName);
93+
return string.Format(CultureInfo.CurrentCulture, Strings.UseVerboseMessageInDSCResourceCommonName);
10694
}
10795

10896
/// <summary>
@@ -111,7 +99,7 @@ public string GetCommonName()
11199
/// <returns>The description of this rule</returns>
112100
public string GetDescription()
113101
{
114-
return string.Format(CultureInfo.CurrentCulture, Strings.ProvideVerboseMessageDescription);
102+
return string.Format(CultureInfo.CurrentCulture, Strings.UseVerboseMessageInDSCResourceDescription);
115103
}
116104

117105
/// <summary>
@@ -136,7 +124,7 @@ public RuleSeverity GetSeverity()
136124
/// </summary>
137125
public string GetSourceName()
138126
{
139-
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
127+
return string.Format(CultureInfo.CurrentCulture, Strings.DSCSourceName);
140128
}
141129
}
142-
}
130+
}

0 commit comments

Comments
 (0)