Skip to content

Commit c07412b

Browse files
committed
Merge pull request #425 from AndrewGaspar/development
Add rule for warning about backticks that look like line-continuations but are not
2 parents 9771645 + 05e3668 commit c07412b

8 files changed

+209
-2
lines changed

Rules/MisleadingBacktick.cs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
//
12+
13+
using System;
14+
using System.Collections.Generic;
15+
using System.Linq;
16+
using System.Management.Automation.Language;
17+
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
18+
using System.ComponentModel.Composition;
19+
using System.Globalization;
20+
using System.Text.RegularExpressions;
21+
22+
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
23+
{
24+
/// <summary>
25+
/// MisleadingBacktick: Checks that lines don't end with a backtick followed by whitespace
26+
/// </summary>
27+
[Export(typeof(IScriptRule))]
28+
public class MisleadingBacktick : IScriptRule
29+
{
30+
private static Regex TrailingEscapedWhitespaceRegex = new Regex(@"`\s+$");
31+
private static Regex NewlineRegex = new Regex("\r?\n");
32+
33+
/// <summary>
34+
/// MisleadingBacktick: Checks that lines don't end with a backtick followed by a whitespace
35+
/// </summary>
36+
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
37+
{
38+
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
39+
40+
string[] lines = NewlineRegex.Split(ast.Extent.Text);
41+
42+
if((ast.Extent.EndLineNumber - ast.Extent.StartLineNumber + 1) != lines.Length)
43+
{
44+
// Did not match the number of lines that the extent indicated
45+
yield break;
46+
}
47+
48+
foreach (int i in Enumerable.Range(0, lines.Length))
49+
{
50+
string line = lines[i];
51+
52+
Match match = TrailingEscapedWhitespaceRegex.Match(line);
53+
54+
if(match.Success)
55+
{
56+
int lineNumber = ast.Extent.StartLineNumber + i;
57+
58+
ScriptPosition start = new ScriptPosition(fileName, lineNumber, match.Index, line);
59+
ScriptPosition end = new ScriptPosition(fileName, lineNumber, match.Index + match.Length, line);
60+
61+
yield return new DiagnosticRecord(
62+
string.Format(CultureInfo.CurrentCulture, Strings.MisleadingBacktickError),
63+
new ScriptExtent(start, end), GetName(), DiagnosticSeverity.Warning, fileName);
64+
}
65+
}
66+
}
67+
68+
/// <summary>
69+
/// GetName: Retrieves the name of this rule.
70+
/// </summary>
71+
/// <returns>The name of this rule</returns>
72+
public string GetName()
73+
{
74+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.MisleadingBacktickName);
75+
}
76+
77+
/// <summary>
78+
/// GetCommonName: Retrieves the common name of this rule.
79+
/// </summary>
80+
/// <returns>The common name of this rule</returns>
81+
public string GetCommonName()
82+
{
83+
return string.Format(CultureInfo.CurrentCulture, Strings.MisleadingBacktickCommonName);
84+
}
85+
86+
/// <summary>
87+
/// GetDescription: Retrieves the description of this rule.
88+
/// </summary>
89+
/// <returns>The description of this rule</returns>
90+
public string GetDescription()
91+
{
92+
return string.Format(CultureInfo.CurrentCulture, Strings.MisleadingBacktickDescription);
93+
}
94+
95+
/// <summary>
96+
/// GetSourceType: Retrieves the type of the rule: builtin, managed or module.
97+
/// </summary>
98+
public SourceType GetSourceType()
99+
{
100+
return SourceType.Builtin;
101+
}
102+
103+
/// <summary>
104+
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
105+
/// </summary>
106+
/// <returns></returns>
107+
public RuleSeverity GetSeverity()
108+
{
109+
return RuleSeverity.Warning;
110+
}
111+
112+
/// <summary>
113+
/// GetSourceName: Retrieves the module/assembly name the rule is from.
114+
/// </summary>
115+
public string GetSourceName()
116+
{
117+
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
118+
}
119+
}
120+
}

Rules/ScriptAnalyzerBuiltinRules.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
<Compile Include="AvoidUsingWriteHost.cs" />
8888
<Compile Include="DscTestsPresent.cs" />
8989
<Compile Include="DscExamplesPresent.cs" />
90+
<Compile Include="MisleadingBacktick.cs" />
9091
<Compile Include="Strings.Designer.cs">
9192
<AutoGen>True</AutoGen>
9293
<DesignTime>True</DesignTime>
@@ -129,7 +130,7 @@
129130
<VisualStudio AllowExistingFolder="true" />
130131
</ProjectExtensions>
131132
<PropertyGroup>
132-
<PostBuildEvent>
133+
<PostBuildEvent>
133134
if "PSV3 Release" == "$(ConfigurationName)" (
134135
GOTO psv3Release
135136
) else if "PSV3 Debug" == "$(ConfigurationName)" (

Rules/Strings.Designer.cs

Lines changed: 36 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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,4 +774,16 @@
774774
<data name="ScriptDefinitionName" xml:space="preserve">
775775
<value>ScriptDefinition</value>
776776
</data>
777+
<data name="MisleadingBacktickCommonName" xml:space="preserve">
778+
<value>Misleading Backtick</value>
779+
</data>
780+
<data name="MisleadingBacktickDescription" xml:space="preserve">
781+
<value>Ending a line with an escaped whitepsace character is misleading. A trailing backtick is usually used for line continuation. Users typically don't intend to end a line with escaped whitespace.</value>
782+
</data>
783+
<data name="MisleadingBacktickName" xml:space="preserve">
784+
<value>MisleadingBacktick</value>
785+
</data>
786+
<data name="MisleadingBacktickError" xml:space="preserve">
787+
<value>This line has a backtick at the end trailed by a whitespace character. Did you mean for this to be a line continuation?</value>
788+
</data>
777789
</root>

Tests/Engine/GetScriptAnalyzerRule.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Describe "Test Name parameters" {
5656

5757
It "Get Rules with no parameters supplied" {
5858
$defaultRules = Get-ScriptAnalyzerRule
59-
$defaultRules.Count | Should be 38
59+
$defaultRules.Count | Should be 39
6060
}
6161
}
6262

Tests/Rules/MisleadingBacktick.ps1

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
New-NetLbfoTeam `
2+
-Name NetTeam `
3+
-TeamingMode SwitchIndependent `
4+
-TeamMembers Ethernet*
5+
6+
7+
"this ` backtick is just fine, though"
8+
9+
and so is this one `
10+
11+
But not this `
12+
or this `
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Import-Module PSScriptAnalyzer
2+
$writeHostName = "PSMisleadingBacktick"
3+
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
4+
$violations = Invoke-ScriptAnalyzer $directory\MisleadingBacktick.ps1 | Where-Object {$_.RuleName -eq $writeHostName}
5+
$noViolations = Invoke-ScriptAnalyzer $directory\NoMisleadingBacktick.ps1 | Where-Object {$_.RuleName -eq $clearHostName}
6+
7+
Describe "Avoid Misleading Backticks" {
8+
Context "When there are violations" {
9+
It "has 5 misleading backtick violations" {
10+
$violations.Count | Should Be 5
11+
}
12+
}
13+
14+
Context "When there are no violations" {
15+
It "returns no violations" {
16+
$noViolations.Count | Should Be 0
17+
}
18+
}
19+
}

Tests/Rules/NoMisleadingBacktick.ps1

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
New-NetLbfoTeam `
2+
-Name NetTeam `
3+
-TeamingMode SwitchIndependent `
4+
-TeamMembers Ethernet*
5+
6+
7+
"this ` backtick is just fine, though"

0 commit comments

Comments
 (0)