Skip to content

Commit 5b0785b

Browse files
author
Quoc Truong
committed
Merge pull request #93 from PowerShell/writehostbug
Suppress write host for function that starts with show
2 parents 5eff3c4 + 49c0e1e commit 5b0785b

File tree

4 files changed

+84
-26
lines changed

4 files changed

+84
-26
lines changed

Rules/AvoidUsingWriteHost.cs

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,43 +23,90 @@ namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules
2323
/// AvoidUsingWriteHost: Check that Write-Host or Console.Write are not used
2424
/// </summary>
2525
[Export(typeof(IScriptRule))]
26-
public class AvoidUsingWriteHost : IScriptRule
26+
public class AvoidUsingWriteHost : AstVisitor, IScriptRule
2727
{
28+
List<DiagnosticRecord> records;
29+
string fileName;
30+
2831
/// <summary>
2932
/// AnalyzeScript: check that Write-Host or Console.Write are not used.
3033
/// </summary>
3134
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
3235
{
3336
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
3437

35-
// Finds all CommandAsts.
36-
IEnumerable<Ast> commandAsts = ast.FindAll(testAst => testAst is CommandAst, true);
38+
records = new List<DiagnosticRecord>();
39+
this.fileName = fileName;
40+
41+
ast.Visit(this);
42+
43+
return records;
44+
}
45+
46+
47+
/// <summary>
48+
/// Visit function and skips any function that starts with show
49+
/// </summary>
50+
/// <param name="funcAst"></param>
51+
/// <returns></returns>
52+
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst funcAst)
53+
{
54+
if (funcAst == null || funcAst.Name == null)
55+
{
56+
return AstVisitAction.SkipChildren;
57+
}
58+
59+
if (funcAst.Name.StartsWith("show", StringComparison.OrdinalIgnoreCase))
60+
{
61+
return AstVisitAction.SkipChildren;
62+
}
63+
64+
return AstVisitAction.Continue;
65+
}
66+
67+
/// <summary>
68+
/// Checks that write-host command is not used
69+
/// </summary>
70+
/// <param name="cmdAst"></param>
71+
/// <returns></returns>
72+
public override AstVisitAction VisitCommand(CommandAst cmdAst)
73+
{
74+
if (cmdAst == null)
75+
{
76+
return AstVisitAction.SkipChildren;
77+
}
3778

38-
// Iterrates all CommandAsts and check the command name.
39-
foreach (CommandAst cmdAst in commandAsts)
79+
if (cmdAst.GetCommandName() != null && String.Equals(cmdAst.GetCommandName(), "write-host", StringComparison.OrdinalIgnoreCase))
4080
{
41-
if (cmdAst.GetCommandName() != null && String.Equals(cmdAst.GetCommandName(), "write-host", StringComparison.OrdinalIgnoreCase))
42-
{
43-
yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWriteHostError, System.IO.Path.GetFileName(fileName)),
44-
cmdAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
45-
}
81+
records.Add(new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWriteHostError, System.IO.Path.GetFileName(fileName)),
82+
cmdAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName));
4683
}
4784

48-
// Finds all InvokeMemberExpressionAst
49-
IEnumerable<Ast> invokeAsts = ast.FindAll(testAst => testAst is InvokeMemberExpressionAst, true);
85+
return AstVisitAction.Continue;
86+
}
5087

51-
foreach (InvokeMemberExpressionAst invokeAst in invokeAsts)
88+
public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressionAst imeAst)
89+
{
90+
if (imeAst == null)
5291
{
53-
TypeExpressionAst typeAst = invokeAst.Expression as TypeExpressionAst;
54-
if (typeAst == null || typeAst.TypeName == null || typeAst.TypeName.FullName == null) continue;
55-
56-
if (typeAst.TypeName.FullName.EndsWith("console", StringComparison.OrdinalIgnoreCase)
57-
&& !String.IsNullOrWhiteSpace(invokeAst.Member.Extent.Text) && invokeAst.Member.Extent.Text.StartsWith("Write", StringComparison.OrdinalIgnoreCase))
58-
{
59-
yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingConsoleWriteError, System.IO.Path.GetFileName(fileName), invokeAst.Member.Extent.Text),
60-
invokeAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
61-
}
92+
return AstVisitAction.SkipChildren;
6293
}
94+
95+
TypeExpressionAst typeAst = imeAst.Expression as TypeExpressionAst;
96+
97+
if (typeAst == null || typeAst.TypeName == null || typeAst.TypeName.FullName == null)
98+
{
99+
return AstVisitAction.SkipChildren;
100+
}
101+
102+
if (typeAst.TypeName.FullName.EndsWith("console", StringComparison.OrdinalIgnoreCase)
103+
&& !String.IsNullOrWhiteSpace(imeAst.Member.Extent.Text) && imeAst.Member.Extent.Text.StartsWith("Write", StringComparison.OrdinalIgnoreCase))
104+
{
105+
records.Add(new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingConsoleWriteError, System.IO.Path.GetFileName(fileName), imeAst.Member.Extent.Text),
106+
imeAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName));
107+
}
108+
109+
return AstVisitAction.Continue;
63110
}
64111

65112
/// <summary>

Tests/Rules/AvoidUsingWriteHost.ps1

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,9 @@ cls
33
Write-Host "aaa"
44
clear
55
[System.Console]::Write("abcdefg");
6-
[System.Console]::WriteLine("No console.writeline plz!");
6+
[System.Console]::WriteLine("No console.writeline plz!");
7+
8+
function Test
9+
{
10+
Write-Host "aaaa"
11+
}

Tests/Rules/AvoidUsingWriteHost.tests.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ $noViolations = Invoke-ScriptAnalyzer $directory\AvoidUsingWriteHostNoViolations
88

99
Describe "AvoidUsingWriteHost" {
1010
Context "When there are violations" {
11-
It "has 3 Write-Host violations" {
12-
$violations.Count | Should Be 3
11+
It "has 4 Write-Host violations" {
12+
$violations.Count | Should Be 4
1313
}
1414

1515
It "has the correct description message for Write-Host" {
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
Write-Output "This is the correct way to write output"
1+
Write-Output "This is the correct way to write output"
2+
3+
# Even if write-host is used, error should not be raised in this function
4+
function Show-Something
5+
{
6+
Write-Host "show something on screen";
7+
}

0 commit comments

Comments
 (0)