Skip to content

Commit 2260653

Browse files
thomasraynerbergmeister
authored andcommitted
Added rule: AvoidLongLines (#1329)
* Added rule AvoidLongLines * cleaning up typos * Made line length configurable and disabled rule by default a/p @bergmeister suggestion * Updated tests for AvoidLongLines * Fix test failures due to added rule and missing entry in main README.md of rule docs * Add documentation * Added rule AvoidLongLines * cleaning up typos * Made line length configurable and disabled rule by default a/p @bergmeister suggestion * Updated tests for AvoidLongLines * Fix test failures due to added rule and missing entry in main README.md of rule docs * Add documentation * changes a/p feedback from rjmholt * Added test to ensure the rule gets the right extent
1 parent e37daeb commit 2260653

File tree

7 files changed

+316
-1
lines changed

7 files changed

+316
-1
lines changed

Diff for: RuleDocumentation/AvoidLongLines.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# AvoidLongLines
2+
3+
**Severity Level: Warning**
4+
5+
## Description
6+
7+
Lines should be no longer than a configured number of characters (default: 120), including leading whitespace (indentation).
8+
9+
**Note**: This rule is not enabled by default. The user needs to enable it through settings.
10+
11+
## Configuration
12+
13+
```powershell
14+
Rules = @{
15+
PSAvoidLongLines = @{
16+
Enable = $true
17+
LineLength = 120
18+
}
19+
}
20+
```
21+
22+
### Parameters
23+
24+
#### Enable: bool (Default value is `$false`)
25+
26+
Enable or disable the rule during ScriptAnalyzer invocation.
27+
28+
#### MaximumLineLength: int (Default value is 120)
29+
30+
Optional parameter to override the default maximum line length.

Diff for: RuleDocumentation/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
|[AvoidGlobalFunctions](./AvoidGlobalFunctions.md) | Warning | |
1313
|[AvoidGlobalVars](./AvoidGlobalVars.md) | Warning | |
1414
|[AvoidInvokingEmptyMembers](./AvoidInvokingEmptyMembers.md) | Warning | |
15+
|[AvoidLongLines](./AvoidLongLines.md) | Warning | |
1516
|[AvoidNullOrEmptyHelpMessageAttribute](./AvoidNullOrEmptyHelpMessageAttribute.md) | Warning | |
1617
|[AvoidShouldContinueWithoutForce](./AvoidShouldContinueWithoutForce.md) | Warning | |
1718
|[AvoidUsingCmdletAliases](./AvoidUsingCmdletAliases.md) | Warning | Yes |

Diff for: Rules/AvoidLongLines.cs

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Text.RegularExpressions;
7+
#if !CORECLR
8+
using System.ComponentModel.Composition;
9+
#endif
10+
using System.Globalization;
11+
using System.Management.Automation.Language;
12+
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
13+
14+
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
15+
{
16+
/// <summary>
17+
/// AvoidLongLines: Checks for lines longer than 120 characters
18+
/// </summary>
19+
#if !CORECLR
20+
[Export(typeof(IScriptRule))]
21+
#endif
22+
public class AvoidLongLines : ConfigurableRule
23+
{
24+
/// <summary>
25+
/// Construct an object of AvoidLongLines type.
26+
/// </summary>
27+
public AvoidLongLines()
28+
{ }
29+
30+
[ConfigurableRuleProperty(defaultValue: 120)]
31+
public int MaximumLineLength { get; set; }
32+
33+
private readonly string[] s_lineSeparators = new[] { "\r\n", "\n" };
34+
35+
/// <summary>
36+
/// Analyzes the given ast to find violations.
37+
/// </summary>
38+
/// <param name="ast">AST to be analyzed. This should be non-null</param>
39+
/// <param name="fileName">Name of file that corresponds to the input AST.</param>
40+
/// <returns>A an enumerable type containing the violations</returns>
41+
public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
42+
{
43+
if (ast == null)
44+
{
45+
throw new ArgumentNullException(nameof(ast));
46+
}
47+
48+
var diagnosticRecords = new List<DiagnosticRecord>();
49+
50+
string[] lines = ast.Extent.Text.Split(s_lineSeparators, StringSplitOptions.None);
51+
52+
for (int lineNumber = 0; lineNumber < lines.Length; lineNumber++)
53+
{
54+
string line = lines[lineNumber];
55+
56+
if (line.Length <= MaximumLineLength)
57+
{
58+
continue;
59+
}
60+
61+
int startLine = lineNumber + 1;
62+
int endLine = startLine;
63+
int startColumn = 1;
64+
int endColumn = line.Length;
65+
66+
var violationExtent = new ScriptExtent(
67+
new ScriptPosition(
68+
ast.Extent.File,
69+
startLine,
70+
startColumn,
71+
line
72+
),
73+
new ScriptPosition(
74+
ast.Extent.File,
75+
endLine,
76+
endColumn,
77+
line
78+
));
79+
80+
var record = new DiagnosticRecord(
81+
String.Format(CultureInfo.CurrentCulture,
82+
String.Format(Strings.AvoidLongLinesError, MaximumLineLength)),
83+
violationExtent,
84+
GetName(),
85+
GetDiagnosticSeverity(),
86+
ast.Extent.File,
87+
null
88+
);
89+
diagnosticRecords.Add(record);
90+
}
91+
92+
return diagnosticRecords;
93+
}
94+
95+
/// <summary>
96+
/// Retrieves the common name of this rule.
97+
/// </summary>
98+
public override string GetCommonName()
99+
{
100+
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidLongLinesCommonName);
101+
}
102+
103+
/// <summary>
104+
/// Retrieves the description of this rule.
105+
/// </summary>
106+
public override string GetDescription()
107+
{
108+
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidLongLinesDescription);
109+
}
110+
111+
/// <summary>
112+
/// Retrieves the name of this rule.
113+
/// </summary>
114+
public override string GetName()
115+
{
116+
return string.Format(
117+
CultureInfo.CurrentCulture,
118+
Strings.NameSpaceFormat,
119+
GetSourceName(),
120+
Strings.AvoidLongLinesName);
121+
}
122+
123+
/// <summary>
124+
/// Retrieves the severity of the rule: error, warning or information.
125+
/// </summary>
126+
public override RuleSeverity GetSeverity()
127+
{
128+
return RuleSeverity.Warning;
129+
}
130+
131+
/// <summary>
132+
/// Gets the severity of the returned diagnostic record: error, warning, or information.
133+
/// </summary>
134+
/// <returns></returns>
135+
public DiagnosticSeverity GetDiagnosticSeverity()
136+
{
137+
return DiagnosticSeverity.Warning;
138+
}
139+
140+
/// <summary>
141+
/// Retrieves the name of the module/assembly the rule is from.
142+
/// </summary>
143+
public override string GetSourceName()
144+
{
145+
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
146+
}
147+
148+
/// <summary>
149+
/// Retrieves the type of the rule, Builtin, Managed or Module.
150+
/// </summary>
151+
public override SourceType GetSourceType()
152+
{
153+
return SourceType.Builtin;
154+
}
155+
}
156+
}

Diff for: Rules/Strings.Designer.cs

+55
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Rules/Strings.resx

+12
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,18 @@
897897
<data name="AvoidTrailingWhitespaceError" xml:space="preserve">
898898
<value>Line has trailing whitespace</value>
899899
</data>
900+
<data name="AvoidLongLinesName" xml:space="preserve">
901+
<value>AvoidLongLines</value>
902+
</data>
903+
<data name="AvoidLongLinesCommonName" xml:space="preserve">
904+
<value>Avoid long lines</value>
905+
</data>
906+
<data name="AvoidLongLinesDescription" xml:space="preserve">
907+
<value>Line lengths should be less than the configured maximum</value>
908+
</data>
909+
<data name="AvoidLongLinesError" xml:space="preserve">
910+
<value>Line exceeds the configured maximum length of {0} characters</value>
911+
</data>
900912
<data name="PlaceOpenBraceName" xml:space="preserve">
901913
<value>PlaceOpenBrace</value>
902914
</data>

Diff for: Tests/Engine/GetScriptAnalyzerRule.tests.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Describe "Test Name parameters" {
5959

6060
It "get Rules with no parameters supplied" {
6161
$defaultRules = Get-ScriptAnalyzerRule
62-
$expectedNumRules = 59
62+
$expectedNumRules = 60
6363
if ((Test-PSEditionCoreClr) -or (Test-PSVersionV3) -or (Test-PSVersionV4))
6464
{
6565
# for PSv3 PSAvoidGlobalAliases is not shipped because

Diff for: Tests/Rules/AvoidLongLines.tests.ps1

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
$ruleName = "PSAvoidLongLines"
2+
3+
$ruleSettings = @{
4+
Enable = $true
5+
}
6+
$settings = @{
7+
IncludeRules = @($ruleName)
8+
Rules = @{ $ruleName = $ruleSettings }
9+
}
10+
11+
Describe "AvoidLongLines" {
12+
it 'Should be off by default' {
13+
$def = "a" * 500
14+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $def
15+
$violations.Count | Should -Be 0
16+
}
17+
18+
it 'Should find a violation when a line is longer than 120 characters (no whitespace)' {
19+
$def = @"
20+
$("a" * 500)
21+
this line is short enough
22+
"@
23+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings
24+
$violations.Count | Should -Be 1
25+
}
26+
27+
it 'Should get the correct extent of the violation' {
28+
$def = @"
29+
this line is short enough
30+
$("a" * 500)
31+
"@
32+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings
33+
$violations[0].Extent.StartLineNumber | Should -Be 2
34+
$violations[0].Extent.EndLineNumber | Should -Be 2
35+
$violations[0].Extent.StartColumnNumber | Should -Be 1
36+
$violations[0].Extent.EndColumnNumber | Should -Be 500
37+
}
38+
39+
it 'Should find a violation when a line is longer than 120 characters (leading whitespace)' {
40+
$def = @"
41+
$(" " * 100 + "a" * 25)
42+
this line is short enough
43+
"@
44+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings
45+
$violations.Count | Should -Be 1
46+
}
47+
48+
it 'Should not find a violation for lines under 120 characters' {
49+
$def = "a" * 120
50+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings
51+
$violations.Count | Should -Be 0
52+
}
53+
54+
it 'Should find a violation with a configured line length' {
55+
$ruleSettings.Add('MaximumLineLength', 10)
56+
$settings['Rules'] = @{ $ruleName = $ruleSettings }
57+
$def = "a" * 15
58+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings
59+
$violations.Count | Should -Be 1
60+
}
61+
}

0 commit comments

Comments
 (0)