Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1eb5671

Browse files
authored
Make UseSingularNouns rule work on PowerShell Core (#1627)
1 parentec089c9 commit1eb5671

File tree

9 files changed

+158
-67
lines changed

9 files changed

+158
-67
lines changed

‎RuleDocumentation/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
|[UseProcessBlockForPipelineCommand](./UseProcessBlockForPipelineCommand.md)| Warning||
5858
|[UsePSCredentialType](./UsePSCredentialType.md)| Warning||
5959
|[UseShouldProcessForStateChangingFunctions](./UseShouldProcessForStateChangingFunctions.md)| Warning||
60-
|[UseSingularNouns<sup>*</sup>](./UseSingularNouns.md)| Warning||
60+
|[UseSingularNouns](./UseSingularNouns.md)| Warning||
6161
|[UseSupportsShouldProcess](./UseSupportsShouldProcess.md)| Warning||
6262
|[UseToExportFieldsInManifest](./UseToExportFieldsInManifest.md)| Warning||
6363
|[UseCompatibleCmdlets](./UseCompatibleCmdlets.md)| Warning| Yes|

‎Rules/Rules.csproj‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<AssemblyVersion>1.20.0</AssemblyVersion>
88
<PackageId>Rules</PackageId>
99
<RootNamespace>Microsoft.Windows.PowerShell.ScriptAnalyzer</RootNamespace><!-- Namespace needs to match Assembly name for ressource binding-->
10+
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies><!-- Needed in order for Pluralize.NET DLL to appear in bin folder - https://github.com/NuGet/Home/issues/4488-->
1011
</PropertyGroup>
1112

1213
<ItemGroup>
@@ -18,7 +19,7 @@
1819
<ItemGroupCondition=" '$(TargetFramework)' == 'netcoreapp3.1'">
1920
<PackageReferenceInclude="System.Reflection.TypeExtensions"Version="4.7.0" />
2021
<PackageReferenceInclude="Microsoft.Management.Infrastructure"Version="2.0.0" />
21-
<CompileRemove="UseSingularNouns.cs" />
22+
<PackageReferenceInclude="Pluralize.NET"Version="1.0.2" />
2223
</ItemGroup>
2324

2425
<ItemGroupCondition=" '$(TargetFramework)' == 'net452'">

‎Rules/UseSingularNouns.cs‎

Lines changed: 105 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,25 @@
1515
usingSystem.Linq;
1616
usingSystem.Management.Automation.Language;
1717
usingMicrosoft.Windows.PowerShell.ScriptAnalyzer.Generic;
18+
#ifCORECLR
19+
usingPluralize.NET;
20+
#else
1821
usingSystem.ComponentModel.Composition;
22+
usingSystem.Data.Entity.Design.PluralizationServices;
23+
#endif
1924
usingSystem.Globalization;
20-
usingSystem.Text.RegularExpressions;
2125

2226
namespaceMicrosoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2327
{
2428
/// <summary>
2529
/// CmdletSingularNoun: Analyzes scripts to check that all defined cmdlets use singular nouns.
2630
///
2731
/// </summary>
28-
[Export(typeof(IScriptRule))]
29-
publicclassCmdletSingularNoun:IScriptRule{
32+
#if!CORECLR
33+
[Export(typeof(IScriptRule))]
34+
#endif
35+
publicclassCmdletSingularNoun:IScriptRule
36+
{
3037

3138
privatereadonlystring[]nounAllowList=
3239
{
@@ -39,46 +46,49 @@ public class CmdletSingularNoun : IScriptRule {
3946
/// <param name="ast"></param>
4047
/// <param name="fileName"></param>
4148
/// <returns></returns>
42-
publicIEnumerable<DiagnosticRecord>AnalyzeScript(Astast,stringfileName){
49+
publicIEnumerable<DiagnosticRecord>AnalyzeScript(Astast,stringfileName)
50+
{
4351
if(ast==null)thrownewArgumentNullException(Strings.NullCommandInfoError);
4452

4553
IEnumerable<Ast>funcAsts=ast.FindAll(item=>itemisFunctionDefinitionAst,true);
4654

47-
char[]funcSeperator={'-'};
48-
string[]funcNamePieces=newstring[2];
55+
varpluralizer=newPluralizerProxy();
4956

5057
foreach(FunctionDefinitionAstfuncAstinfuncAsts)
5158
{
52-
if(funcAst.Name!=null&&funcAst.Name.Contains('-'))
59+
if(funcAst.Name==null||!funcAst.Name.Contains('-'))
60+
{
61+
continue;
62+
}
63+
64+
stringnoun=GetLastWordInCmdlet(funcAst.Name);
65+
66+
if(nounisnull)
5367
{
54-
funcNamePieces=funcAst.Name.Split(funcSeperator);
55-
StringnounPart=funcNamePieces[1];
56-
57-
// Convert the noun part of the function into a series of space delimited words
58-
// This helps the PluralizationService to provide an accurate determination about the plurality of the string
59-
nounPart=SplitCamelCaseString(nounPart);
60-
varwords=nounPart.Split(newchar[]{' '});
61-
varnoun=words.LastOrDefault();
62-
if(noun==null)
68+
continue;
69+
}
70+
71+
if(pluralizer.CanOnlyBePlural(noun))
72+
{
73+
if(nounAllowList.Contains(noun,StringComparer.OrdinalIgnoreCase))
6374
{
6475
continue;
6576
}
66-
varps=System.Data.Entity.Design.PluralizationServices.PluralizationService.CreateService(CultureInfo.GetCultureInfo("en-us"));
6777

68-
if(!ps.IsSingular(noun)&&ps.IsPlural(noun))
78+
IScriptExtentextent=Helper.Instance.GetScriptExtentForFunctionName(funcAst);
79+
80+
if(extentisnull)
6981
{
70-
IScriptExtentextent=Helper.Instance.GetScriptExtentForFunctionName(funcAst);
71-
if(nounAllowList.Contains(noun,StringComparer.OrdinalIgnoreCase))
72-
{
73-
continue;
74-
}
75-
if(null==extent)
76-
{
77-
extent=funcAst.Extent;
78-
}
79-
yieldreturnnewDiagnosticRecord(string.Format(CultureInfo.CurrentCulture,Strings.UseSingularNounsError,funcAst.Name),
80-
extent,GetName(),DiagnosticSeverity.Warning,fileName);
82+
extent=funcAst.Extent;
8183
}
84+
85+
yieldreturnnewDiagnosticRecord(
86+
string.Format(CultureInfo.CurrentCulture,Strings.UseSingularNounsError,funcAst.Name),
87+
extent,
88+
GetName(),
89+
DiagnosticSeverity.Warning,
90+
fileName,
91+
suggestedCorrections:newCorrectionExtent[]{GetCorrection(pluralizer,extent,funcAst.Name,noun)});
8292
}
8393
}
8494

@@ -106,7 +116,8 @@ public string GetCommonName()
106116
/// GetDescription: Retrieves the description of this rule.
107117
/// </summary>
108118
/// <returns>The description of this rule</returns>
109-
publicstringGetDescription(){
119+
publicstringGetDescription()
120+
{
110121
returnstring.Format(CultureInfo.CurrentCulture,Strings.UseSingularNounsDescription);
111122
}
112123

@@ -135,18 +146,77 @@ public string GetSourceName()
135146
returnstring.Format(CultureInfo.CurrentCulture,Strings.SourceName);
136147
}
137148

149+
privateCorrectionExtentGetCorrection(PluralizerProxypluralizer,IScriptExtentextent,stringcommandName,stringnoun)
150+
{
151+
stringsingularNoun=pluralizer.Singularize(noun);
152+
stringnewCommandName=commandName.Substring(0,commandName.Length-noun.Length)+singularNoun;
153+
returnnewCorrectionExtent(extent,newCommandName,extent.File,$"Singularized correction of '{extent.Text}'");
154+
}
155+
138156
/// <summary>
139-
/// SplitCamelCaseString: Splits a Camel Case'd string into individual words with space delimited
157+
/// Gets the last word in a standard syntax, CamelCase cmdlet.
158+
/// If the cmdlet name is non-standard, returns null.
140159
/// </summary>
141-
privatestringSplitCamelCaseString(stringinput)
160+
privatestringGetLastWordInCmdlet(stringcmdletName)
142161
{
143-
if(String.IsNullOrEmpty(input))
162+
if(string.IsNullOrEmpty(cmdletName))
144163
{
145-
returnString.Empty;
164+
returnnull;
146165
}
147166

148-
returnRegex.Replace(input,"([A-Z])"," $1",RegexOptions.Compiled).Trim();
167+
// Cmdlet doesn't use CamelCase, so assume it's something like an initialism that shouldn't be singularized
168+
if(!char.IsLower(cmdletName[cmdletName.Length-1]))
169+
{
170+
returnnull;
171+
}
172+
173+
for(inti=cmdletName.Length-1;i>=0;i--)
174+
{
175+
if(cmdletName[i]=='-')
176+
{
177+
// We got to the dash without seeing a CamelCase word, so nothing to singularize
178+
returnnull;
179+
}
180+
181+
// We just changed from lower case to upper, so we have the end word
182+
if(char.IsUpper(cmdletName[i]))
183+
{
184+
returncmdletName.Substring(i);
185+
}
186+
}
187+
188+
// We shouldn't ever get here since we should always eventually hit a '-'
189+
// But if we do, assume this isn't supported cmdlet name
190+
returnnull;
191+
}
192+
193+
#ifCORECLR
194+
privateclassPluralizerProxy
195+
{
196+
privatereadonlyPluralizer_pluralizer;
197+
198+
publicPluralizerProxy()
199+
{
200+
_pluralizer=newPluralizer();
201+
}
202+
203+
publicboolCanOnlyBePlural(stringnoun)=>
204+
!_pluralizer.IsSingular(noun)&&_pluralizer.IsPlural(noun);
205+
206+
publicstringSingularize(stringnoun)=>_pluralizer.Singularize(noun);
207+
}
208+
#else
209+
privateclassPluralizerProxy
210+
{
211+
privatestaticreadonlyPluralizationServices_pluralizationService=PluralizationService.CreateService(
212+
CultureInfo.GetCultureInfo("en-us"));
213+
214+
publicboolCanOnlyBePlural(stringnoun)=>
215+
!s_pluralizationService.IsSingular(noun)&&s_pluralizationService.IsPlural(noun);
216+
217+
publicstringSingularize(stringnoun)=>s_pluralizationService.Singularize(noun);
149218
}
219+
#endif
150220
}
151221

152222
}

‎Tests/Documentation/RuleDocumentation.tests.ps1‎

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,8 @@ Describe "Validate rule documentation files" {
1515
}}|
1616
Sort-Object
1717

18-
# Remove rules from the diff list that aren't supported on PSCore
19-
if (($PSVersionTable.PSVersion.Major-ge6)-and ($PSVersionTable.PSEdition-eq"Core"))
20-
{
21-
$RulesNotSupportedInNetstandard2=@("PSUseSingularNouns")
22-
$docs=$docs|Where-Object {$RulesNotSupportedInNetstandard2-notcontains$_}
23-
$readmeRules=$readmeRules|Where-Object {$RulesNotSupportedInNetstandard2-notcontains$_ }
24-
}
25-
elseif ($PSVersionTable.PSVersion.Major-eq4) {
18+
# Remove rules from the diff list that aren't supported on old PS version
19+
if ($PSVersionTable.PSVersion.Major-eq4) {
2620
$docs=$docs|Where-Object {$_-notmatch'^PSAvoidGlobalAliases$'}
2721
$readmeRules=$readmeRules|Where-Object {$_-notmatch'^PSAvoidGlobalAliases$' }
2822
}

‎Tests/Engine/GetScriptAnalyzerRule.tests.ps1‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,11 @@ Describe "Test Name parameters" {
6464
It"get Rules with no parameters supplied" {
6565
$defaultRules=Get-ScriptAnalyzerRule
6666
$expectedNumRules=65
67-
if ($IsCoreCLR-or ($PSVersionTable.PSVersion.Major-eq3)-or ($PSVersionTable.PSVersion.Major-eq4))
67+
if ($PSVersionTable.PSVersion.Major-le4)
6868
{
6969
# for PSv3 PSAvoidGlobalAliases is not shipped because
7070
# it uses StaticParameterBinder.BindCommand which is
7171
# available only on PSv4 and above
72-
# for PowerShell Core, PSUseSingularNouns is not
73-
# shipped because it uses APIs that are not present
74-
# in dotnet core.
7572

7673
$expectedNumRules--
7774
}

‎Tests/Engine/InvokeScriptAnalyzer.tests.ps1‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,7 @@ Describe "Test IncludeRule" {
270270
}
271271

272272
It"includes the given rules" {
273-
# CoreCLR version of PSScriptAnalyzer does not contain PSUseSingularNouns rule
274273
$expectedNumViolations=2
275-
if ($IsCoreCLR)
276-
{
277-
$expectedNumViolations=1
278-
}
279274
$violations=Invoke-ScriptAnalyzer$PSScriptRoot\..\Rules\BadCmdlet.ps1-IncludeRule$rules
280275
$violations.Count| Should-Be$expectedNumViolations
281276
}
@@ -295,12 +290,7 @@ Describe "Test IncludeRule" {
295290
}
296291

297292
It"includes 2 wildcardrules" {
298-
# CoreCLR version of PSScriptAnalyzer does not contain PSUseSingularNouns rule
299293
$expectedNumViolations=4
300-
if ($IsCoreCLR)
301-
{
302-
$expectedNumViolations=3
303-
}
304294
$includeWildcard=Invoke-ScriptAnalyzer$PSScriptRoot\..\Rules\BadCmdlet.ps1-IncludeRule$avoidRules
305295
$includeWildcard+=Invoke-ScriptAnalyzer$PSScriptRoot\..\Rules\BadCmdlet.ps1-IncludeRule$useRules
306296
$includeWildcard.Count| Should-Be$expectedNumViolations

‎Tests/Rules/UseSingularNounsReservedVerbs.tests.ps1‎

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) Microsoft Corporation. All rights reserved.
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

44
BeforeAll {
@@ -15,7 +15,7 @@ BeforeAll {
1515
}
1616

1717
# UseSingularNouns rule doesn't exist in the non desktop version of PSScriptAnalyzer due to missing .Net Pluralization API
18-
Describe"UseSingularNouns"-Skip:$IsCoreCLR{
18+
Describe"UseSingularNouns" {
1919
Context"When there are violations" {
2020
It"has a cmdlet singular noun violation" {
2121
$nounViolations.Count| Should-Be1
@@ -51,6 +51,41 @@ Write-Output "Adding some data"
5151
$nounNoViolations.Count| Should-Be0
5252
}
5353
}
54+
55+
Context"Inline tests" {
56+
It'Correctly diagnoses and corrects <Script>'-TestCases@(
57+
@{Script='function Get-Bananas { "bananas" }';Extent=@{StartCol=10;EndCol=21 };Correction='Get-Banana' }
58+
@{Script='function ConvertTo-StartingCriteria { "criteria" }';Extent=@{StartCol=10;EndCol=36 };Correction='ConvertTo-StartingCriterion' }
59+
@{Script='function Invoke-Data { "data" }' }
60+
@{Script='function Get-Banana { "bananas" }' }
61+
@{Script='function get-banana { "bananas" }' }
62+
@{Script='function banana { "bananas" }' }
63+
) {
64+
param([string]$Script,$Extent,$Correction)
65+
66+
$diagnostics=Invoke-ScriptAnalyzer-ScriptDefinition$Script
67+
68+
if (-not$Extent)
69+
{
70+
$diagnostics| Should-BeNullOrEmpty
71+
return
72+
}
73+
74+
$expectedStartLine=if ($Extent.StartLine) {$Extent.StartLine }else {1 }
75+
$expectedEndLine=if ($Extent.EndLine) {$Extent.EndLine }else {1 }
76+
77+
$diagnostics.Extent.StartLineNumber| Should-BeExactly$expectedStartLine
78+
$diagnostics.Extent.EndLineNumber| Should-BeExactly$expectedEndLine
79+
$diagnostics.Extent.StartColumnNumber| Should-BeExactly$Extent.StartCol
80+
$diagnostics.Extent.EndColumnNumber| Should-BeExactly$Extent.EndCol
81+
82+
$diagnostics.SuggestedCorrections.StartLineNumber| Should-BeExactly$expectedStartLine
83+
$diagnostics.SuggestedCorrections.EndLineNumber| Should-BeExactly$expectedEndLine
84+
$diagnostics.SuggestedCorrections.StartColumnNumber| Should-BeExactly$Extent.StartCol
85+
$diagnostics.SuggestedCorrections.EndColumnNumber| Should-BeExactly$Extent.EndCol
86+
$diagnostics.SuggestedCorrections.Text| Should-BeExactly$Correction
87+
}
88+
}
5489
}
5590

5691
Describe"UseApprovedVerbs" {

‎build.psm1‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -311,15 +311,18 @@ function Start-ScriptAnalyzerBuild
311311
$settingsFiles=Get-Childitem"$projectRoot\Engine\Settings"|ForEach-Object-MemberName FullName
312312
Publish-File$settingsFiles (Join-Path-Path$script:destinationDir-ChildPath Settings)
313313

314+
$rulesProjectOutputDir=if ($env:TF_BUILD) {
315+
"$projectRoot\bin\${buildConfiguration}\${framework}" }
316+
else {
317+
"$projectRoot\Rules\bin\${buildConfiguration}\${framework}"
318+
}
314319
if ($framework-eq'net452') {
315-
if ($env:TF_BUILD ) {
316-
$nsoft="$projectRoot\bin\${buildConfiguration}\${framework}\Newtonsoft.Json.dll"
317-
}
318-
else {
319-
$nsoft="$projectRoot\Rules\bin\${buildConfiguration}\${framework}\Newtonsoft.Json.dll"
320-
}
320+
$nsoft=Join-Path$rulesProjectOutputDir'Newtonsoft.Json.dll'
321321
Copy-Item-path$nsoft-Destination$destinationDirBinaries
322322
}
323+
else {
324+
Copy-Item-Path (Join-Path$rulesProjectOutputDir'Pluralize.NET.dll')-Destination$destinationDirBinaries
325+
}
323326

324327
Pop-Location
325328
}

‎tools/releaseBuild/signing.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<filesrc="__INPATHROOT__\out\PSScriptAnalyzer\1.19.1\PSv7\Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll"signType="Authenticode"dest="__OUTPATHROOT__\PSScriptAnalyzer\1.19.1\PSv7\Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll" />
2525
<filesrc="__INPATHROOT__\out\PSScriptAnalyzer\1.19.1\PSv7\Microsoft.Windows.PowerShell.ScriptAnalyzer.dll"signType="Authenticode"dest="__OUTPATHROOT__\PSScriptAnalyzer\1.19.1\PSv7\Microsoft.Windows.PowerShell.ScriptAnalyzer.dll" />
2626
<filesrc="__INPATHROOT__\out\PSScriptAnalyzer\1.19.1\PSv7\Microsoft.PowerShell.CrossCompatibility.dll"signType="Authenticode"dest="__OUTPATHROOT__\PSScriptAnalyzer\1.19.1\PSv7\Microsoft.PowerShell.CrossCompatibility.dll" />
27+
<filesrc="__INPATHROOT__\out\PSScriptAnalyzer\1.19.1\PSv7\Pluralize.NET.dll"signType="Authenticode"dest="__OUTPATHROOT__\PSScriptAnalyzer\1.19.1\PSv7\Pluralize.NET.dll" />
2728
</job>
2829
<jobplatform=""configuration=""dest="__OUTPATHROOT__\signed"jobname="PowerShell Script Analyzer PSv3"approvers="vigarg;gstolt">
2930
<filesrc="__INPATHROOT__\out\PSScriptAnalyzer\1.19.1\PSv3\Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll"signType="Authenticode"dest="__OUTPATHROOT__\PSScriptAnalyzer\1.19.1\PSv3\Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll" />

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp