Skip to content
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

Add rule for missing process block when command supports the pipeline #1373

Merged
merged 23 commits into from
Dec 9, 2019
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1ef888d
Add rule for missing process block #1368
mattmcnabb Nov 16, 2019
1ad72e7
Fix build by regenerating strongly typed files by running '.\New-Stro…
bergmeister Nov 16, 2019
794366b
Update strongly typed resources via Visual Studio
bergmeister Nov 16, 2019
86673bf
minimise diff in resx file
bergmeister Nov 16, 2019
dd42829
refactor UseProcessBlockForPipelineCommands rule
mattmcnabb Nov 16, 2019
32916c2
add tests and docs for UseProcessBlockForPipelineCommands rule
mattmcnabb Nov 16, 2019
fe2c8e7
Fix logic non-pipeline function and add test case
mattmcnabb Nov 17, 2019
585a735
fixing some test failures
mattmcnabb Nov 17, 2019
b499d9e
incrememt number of expected rules
mattmcnabb Nov 17, 2019
2df5e70
fix style suggestions for PR #1373
mattmcnabb Nov 19, 2019
fbe8bc4
fix rule readme.md
mattmcnabb Nov 19, 2019
5921add
clean diff for Strings.resx to avoid future merge conflicts
Nov 22, 2019
79aaf0f
Fix last 2 rule documentation test failures by fixing markdown link
Nov 22, 2019
850b457
Update Rules/Strings.resx
mattmcnabb Nov 22, 2019
61220e7
Update Rules/Strings.resx
mattmcnabb Nov 22, 2019
fcae260
Update Rules/Strings.resx
mattmcnabb Nov 22, 2019
504fdd6
empty-commit to re-trigger CI due to sporadic failure
Nov 22, 2019
a593ff7
Update Rules/Strings.resx
mattmcnabb Nov 25, 2019
4a358a0
Update Rules/Strings.resx
mattmcnabb Nov 25, 2019
707f02d
corrections to use process block rule and tests - candidate for merge
mattmcnabb Dec 4, 2019
c7a2531
More style fixes for #1373
mattmcnabb Dec 4, 2019
a7ed122
update localized strings
mattmcnabb Dec 5, 2019
a28b4d2
replace implicitly typed vars
mattmcnabb Dec 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RuleDocumentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
|[UseDeclaredVarsMoreThanAssignments](./UseDeclaredVarsMoreThanAssignments.md) | Warning | |
|[UseLiteralInitializerForHashtable](./UseLiteralInitializerForHashtable.md) | Warning | |
|[UseOutputTypeCorrectly](./UseOutputTypeCorrectly.md) | Information | |
|[UseProcessBlockForPipelineCommand](./UseProcessBlockForPipelineCommand.md) | Warning | |
|[UsePSCredentialType](./UsePSCredentialType.md) | Warning | |
|[UseShouldProcessForStateChangingFunctions](./UseShouldProcessForStateChangingFunctions.md) | Warning | |
|[UseSingularNouns<sup>*</sup>](./UseSingularNouns.md) | Warning | |
Expand Down
62 changes: 62 additions & 0 deletions RuleDocumentation/UseProcessBlockForPipelineCommand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# UseProcessBlockForPipelineCommand

**Severity Level: Warning**

## Description

Functions that support pipeline input should always handle parameter input in a process block. Unexpected behavior can result if input is handled directly in the body of a function where parameters declare pipeline support.

## Example

### Wrong

``` PowerShell
Function Get-Number
{
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline)]
[int]
$Number
)

$Number
}
```

#### Result

```
PS C:\> 1..5 | Get-Number
5
```

### Correct

``` PowerShell
Function Get-Number
{
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline)]
[int]
$Number
)

process
{
$Number
}
}
```

#### Result

```
PS C:\> 1..5 | Get-Number
1
2
3
4
5
```
129 changes: 73 additions & 56 deletions Rules/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Rules/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1080,4 +1080,16 @@
<data name="UseCorrectCasingName" xml:space="preserve">
<value>UseCorrectCasing</value>
</data>
<data name="UseProcessBlockForPipelineCommandCommonName" xml:space="preserve">
<value>Use process block for command that accepts input from pipeline.</value>
</data>
<data name="UseProcessBlockForPipelineCommandDescription" xml:space="preserve">
<value>If a function parameter takes its value from the pipeline, the function must use a process block to bind the input objects from the pipeline to that parameter.</value>
</data>
<data name="UseProcessBlockForPipelineCommandError" xml:space="preserve">
<value>Command accepts pipeline input but has not defined a process block.</value>
</data>
<data name="UseProcessBlockForPipelineCommandName" xml:space="preserve">
<value>UseProcessBlockForPipelineCommand</value>
</data>
</root>
98 changes: 98 additions & 0 deletions Rules/UseProcessBlockForPipelineCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Management.Automation.Language;
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
#if !CORECLR
using System.ComponentModel.Composition;
#endif
using System.Globalization;

namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
{
#if !CORECLR
[Export(typeof(IScriptRule))]
#endif
public class UseProcessBlockForPipelineCommand : IScriptRule
{
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
{
if (ast == null)
{
throw new ArgumentNullException(Strings.NullAstErrorMessage);
}

IEnumerable<Ast> scriptblockAsts = ast.FindAll(testAst => testAst is ScriptBlockAst, true);

foreach (ScriptBlockAst scriptblockAst in scriptblockAsts)
{
if (scriptblockAst.ProcessBlock != null
|| scriptblockAst.ParamBlock?.Attributes == null
|| scriptblockAst.ParamBlock.Parameters == null)
{
continue;
}

foreach (var paramAst in scriptblockAst.ParamBlock.Parameters)
mattmcnabb marked this conversation as resolved.
Show resolved Hide resolved
{
foreach (var paramAstAttribute in paramAst.Attributes)
mattmcnabb marked this conversation as resolved.
Show resolved Hide resolved
{
if (!(paramAstAttribute is AttributeAst paramAttributeAst)) { continue; }

if (paramAttributeAst.NamedArguments == null) { continue; }

foreach (NamedAttributeArgumentAst namedArgument in paramAttributeAst.NamedArguments)
{
if (!namedArgument.ArgumentName.Equals("valuefrompipeline", StringComparison.OrdinalIgnoreCase)
&& !namedArgument.ArgumentName.Equals("valuefrompipelinebypropertyname", StringComparison.OrdinalIgnoreCase))
{
continue;
}

yield return new DiagnosticRecord(
string.Format(CultureInfo.CurrentCulture, Strings.UseProcessBlockForPipelineCommandError, paramAst.Name.VariablePath.UserPath),
paramAst.Name.Extent,
GetName(),
DiagnosticSeverity.Warning,
fileName,
paramAst.Name.VariablePath.UserPath
);
}
}
}
}
}

public string GetName()
{
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.UseProcessBlockForPipelineCommandName);
}

public string GetCommonName()
{
return string.Format(CultureInfo.CurrentCulture, Strings.UseProcessBlockForPipelineCommandCommonName);
}

public string GetDescription()
{
return string.Format(CultureInfo.CurrentCulture, Strings.UseProcessBlockForPipelineCommandDescription);
}

public SourceType GetSourceType()
{
return SourceType.Builtin;
}

public RuleSeverity GetSeverity()
{
return RuleSeverity.Warning;
}

public string GetSourceName()
{
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
}
}
}
2 changes: 1 addition & 1 deletion Tests/Engine/GetScriptAnalyzerRule.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Describe "Test Name parameters" {

It "get Rules with no parameters supplied" {
$defaultRules = Get-ScriptAnalyzerRule
$expectedNumRules = 60
$expectedNumRules = 61
if ((Test-PSEditionCoreClr) -or (Test-PSVersionV3) -or (Test-PSVersionV4))
{
# for PSv3 PSAvoidGlobalAliases is not shipped because
Expand Down
Loading