-
Notifications
You must be signed in to change notification settings - Fork 395
New rule (disabled by default): AvoidUsingDoubleQuotesForConstantString #1470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
bergmeister
merged 12 commits into
PowerShell:master
from
bergmeister:AvoidUsingDoubleQuotesForConstantString
Jun 11, 2020
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
b7d8f00
AvoidUsingDoubleQuotesForConstantString rule. TODO: tests and docs
50da832
cleanup
40acc95
add tests
13663c3
tidy up and don't warn on strings containing single quote or @'
1e00461
exclude escape sequences and add docs
306c36e
fix tests
3362ff9
fix last tests
2c93230
Apply suggestions from code review
bergmeister 73b8a71
update comment
43c8d1b
Apply suggestions from code review
bergmeister 8bb6f03
add code documentation, add formatter tests and make test file itself…
53c82a5
Merge branch 'master' of https://github.com/powershell/psscriptanalyz…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
RuleDocumentation/AvoidUsingDoubleQuotesForConstantString.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# AvoidUsingDoubleQuotesForConstantString | ||
|
||
**Severity Level: Information** | ||
|
||
## Description | ||
|
||
When the value of a string is constant (i.e. not being interpolated by injecting variables or expression into such as e.g. `"$PID-$(hostname)"`), then single quotes should be used to express the constant nature of the string. This is not only to make the intent clearer that the string is a constant and makes it easier to use some special characters such as e.g. `$` within that string expression without the need to escape them. There are exceptions to that when double quoted strings are more readable though, e.g. when the string value itself has to contain a single quote (which would require a double single quotes to escape the character itself) or certain very special characters such as e.g. `"\n"` are already being escaped, the rule would not warn in this case as it is up to the author to decide on what is more readable in those cases. | ||
|
||
## Example | ||
|
||
### Wrong | ||
|
||
``` PowerShell | ||
$constantValue = "I Love PowerShell" | ||
``` | ||
|
||
### Correct | ||
|
||
``` PowerShell | ||
$constantValue = 'I Love PowerShell' | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
#if !CORECLR | ||
using System.ComponentModel.Composition; | ||
#endif | ||
using System.Globalization; | ||
using System.Management.Automation.Language; | ||
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; | ||
|
||
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules | ||
{ | ||
/// <summary> | ||
/// AvoidUsingDoubleQuotesForConstantStrings: Checks if a string that uses double quotes contains a constant string, which could be simplified to a single quote. | ||
/// </summary> | ||
#if !CORECLR | ||
[Export(typeof(IScriptRule))] | ||
#endif | ||
public class AvoidUsingDoubleQuotesForConstantString : ConfigurableRule | ||
{ | ||
/// <summary> | ||
/// Construct an object of type <seealso cref="AvoidUsingDoubleQuotesForConstantStrings"/>. | ||
/// </summary> | ||
public AvoidUsingDoubleQuotesForConstantString() | ||
{ | ||
Enable = false; | ||
} | ||
|
||
/// <summary> | ||
/// Analyzes the given ast to find occurences of StringConstantExpressionAst where double quotes are used | ||
/// and could be replaced with single quotes. | ||
/// </summary> | ||
/// <param name="ast">AST to be analyzed. This should be non-null</param> | ||
/// <param name="fileName">Name of file that corresponds to the input AST.</param> | ||
/// <returns>A an enumerable type containing the violations</returns> | ||
public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName) | ||
{ | ||
if (ast == null) | ||
{ | ||
throw new ArgumentNullException(nameof(ast)); | ||
} | ||
|
||
var stringConstantExpressionAsts = ast.FindAll(testAst => testAst is StringConstantExpressionAst, searchNestedScriptBlocks: true); | ||
foreach (StringConstantExpressionAst stringConstantExpressionAst in stringConstantExpressionAsts) | ||
{ | ||
switch (stringConstantExpressionAst.StringConstantType) | ||
{ | ||
/* | ||
If an interpolated string (i.e. using double quotes) uses single quotes (or @' in case of a here-string), then do not warn. | ||
This because one would then have to escape this single quote, which would make the string less readable. | ||
If an interpolated string contains a backtick character, then do not warn as well. | ||
This is because we'd have to replace the backtick in some cases like `$ and in others like `a it would not be possible at all. | ||
Therefore to keep it simple, all interpolated strings with a backtick are being excluded. | ||
*/ | ||
case StringConstantType.DoubleQuoted: | ||
if (stringConstantExpressionAst.Value.Contains("'") || stringConstantExpressionAst.Extent.Text.Contains("`")) | ||
{ | ||
break; | ||
} | ||
yield return GetDiagnosticRecord(stringConstantExpressionAst, | ||
$"'{stringConstantExpressionAst.Value}'"); | ||
break; | ||
|
||
case StringConstantType.DoubleQuotedHereString: | ||
if (stringConstantExpressionAst.Value.Contains("@'") || stringConstantExpressionAst.Extent.Text.Contains("`")) | ||
{ | ||
break; | ||
} | ||
yield return GetDiagnosticRecord(stringConstantExpressionAst, | ||
$"@'{Environment.NewLine}{stringConstantExpressionAst.Value}{Environment.NewLine}'@"); | ||
break; | ||
|
||
default: | ||
break; | ||
} | ||
} | ||
} | ||
|
||
private DiagnosticRecord GetDiagnosticRecord(StringConstantExpressionAst stringConstantExpressionAst, | ||
string suggestedCorrection) | ||
{ | ||
return new DiagnosticRecord( | ||
Strings.AvoidUsingDoubleQuotesForConstantStringError, | ||
stringConstantExpressionAst.Extent, | ||
GetName(), | ||
GetDiagnosticSeverity(), | ||
stringConstantExpressionAst.Extent.File, | ||
suggestedCorrections: new[] { | ||
new CorrectionExtent( | ||
stringConstantExpressionAst.Extent, | ||
suggestedCorrection, | ||
stringConstantExpressionAst.Extent.File | ||
) | ||
} | ||
); | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the common name of this rule. | ||
/// </summary> | ||
public override string GetCommonName() | ||
{ | ||
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingDoubleQuotesForConstantStringCommonName); | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the description of this rule. | ||
/// </summary> | ||
public override string GetDescription() | ||
{ | ||
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingDoubleQuotesForConstantStringDescription); | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the name of this rule. | ||
/// </summary> | ||
public override string GetName() | ||
{ | ||
return string.Format( | ||
CultureInfo.CurrentCulture, | ||
Strings.NameSpaceFormat, | ||
GetSourceName(), | ||
Strings.AvoidUsingDoubleQuotesForConstantStringName); | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the severity of the rule: error, warning or information. | ||
/// </summary> | ||
public override RuleSeverity GetSeverity() | ||
{ | ||
return RuleSeverity.Information; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the severity of the returned diagnostic record: error, warning, or information. | ||
/// </summary> | ||
/// <returns></returns> | ||
public DiagnosticSeverity GetDiagnosticSeverity() | ||
{ | ||
return DiagnosticSeverity.Information; | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the name of the module/assembly the rule is from. | ||
/// </summary> | ||
public override string GetSourceName() | ||
{ | ||
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName); | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the type of the rule, Builtin, Managed or Module. | ||
/// </summary> | ||
public override SourceType GetSourceType() | ||
{ | ||
return SourceType.Builtin; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
Tests/Rules/AvoidUsingDoubleQuotesForConstantString.tests.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
$settings = @{ | ||
IncludeRules = @('PSAvoidUsingDoubleQuotesForConstantString') | ||
Rules = @{ | ||
PSAvoidUsingDoubleQuotesForConstantString = @{ | ||
Enable = $true | ||
} | ||
} | ||
} | ||
|
||
Describe 'AvoidUsingDoubleQuotesForConstantString' { | ||
Context 'One line string' { | ||
It 'Should warn if string is constant and double quotes are used' { | ||
(Invoke-ScriptAnalyzer -ScriptDefinition '$item = "value"' -Settings $settings).Count | Should -Be 1 | ||
} | ||
|
||
It 'Should correctly format if string is constant and double quotes are used' { | ||
Invoke-Formatter -ScriptDefinition '$item = "value"' -Settings $settings | Should -Be "`$item = 'value'" | ||
} | ||
|
||
It 'Should not warn if string is interpolated and double quotes are used but single quotes are in value' { | ||
Invoke-ScriptAnalyzer -ScriptDefinition "`$item = 'value'" -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
|
||
It 'Should not warn if string is interpolated and double quotes are used but backtick character is in value' { | ||
Invoke-ScriptAnalyzer -ScriptDefinition '$item = "foo-`$-bar"' -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
|
||
It 'Should not warn if string is constant and single quotes are used' { | ||
Invoke-ScriptAnalyzer -ScriptDefinition "`$item = 'value'" -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
|
||
It 'Should not warn if string is interpolated and double quotes are used' { | ||
Invoke-ScriptAnalyzer -ScriptDefinition '$item = "$(Get-Item)"' -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
|
||
It 'Should not warn if string is interpolated and double quotes are used' { | ||
Invoke-ScriptAnalyzer -ScriptDefinition '$item = "$(Get-Item)"' -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
} | ||
|
||
# TODO: check escape strings | ||
|
||
Context 'Here string' { | ||
It 'Should warn if string is constant and double quotes are used' { | ||
$scriptDefinition = @' | ||
$item=@" | ||
value | ||
"@ | ||
'@ | ||
(Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings).Count | Should -Be 1 | ||
} | ||
|
||
It 'Should correctly format if string is constant and double quotes are used' { | ||
$scriptDefinition = @' | ||
$item=@" | ||
value | ||
"@ | ||
'@ | ||
Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings | Should -Be @" | ||
`$item=@' | ||
value | ||
'@ | ||
"@ | ||
} | ||
|
||
It 'Should not warn if string is constant and double quotes are used but @'' is used in value' { | ||
$scriptDefinition = @' | ||
$item=@" | ||
value1@'value2 | ||
"@ | ||
'@ | ||
Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
It 'Should not warn if string is constant and double quotes are used but backtick is used in value' { | ||
$scriptDefinition = @' | ||
$item=@" | ||
foo-`$-bar | ||
"@ | ||
'@ | ||
Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
|
||
It 'Should not warn if string is constant and single quotes are used' { | ||
$scriptDefinition = @" | ||
`$item=@' | ||
value | ||
'@ | ||
"@ | ||
Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
|
||
It 'Should not warn if string is interpolated' { | ||
$scriptDefinition = @' | ||
$item=@" | ||
$(Get-Process) | ||
"@ | ||
'@ | ||
Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings | Should -BeNullOrEmpty | ||
} | ||
} | ||
|
||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.