Skip to content

Question about PSUseCompatibleSyntax #1384

Description

I use this script with Powershell v3 ( Citrix farm with W 2008)

$h=@{}
$h.Test='Ok'
$controlName='Test'

writeLog "$($psversiontable|out-string)"
writeLog "Test compatibility"

if ($h.$controlName -eq 'ok')
{ writeLog "Test= $($h.$controlName )"}
else
{ writeLog "dont works"}

$a = 10,20,30
$property = "Length"
writeLog "Array =$($a.$property)"

$typeExpression=[int]
$staticMember='MaxValue'
writeLog "Static=$($typeExpression::$staticMember)"

$method = 'Trim'
$sb= { writeLog "Method =$($Method.$method())" } #Avoid ParseError
&$sb

I run PSSA version 1.8.3 with this settings :

@{
    ExcludeRules = @('PSAvoidGlobalVars','PSAvoidUsingWMICmdlet')

     # Check if the scripts uses cmdlets that are compatible PS 3.0,
    Rules = @{
        PSUseCompatibleSyntax = @{
            # This turns the rule on (setting it to false will turn it off)
            Enable = $true
    
            # List the targeted versions of PowerShell here
            TargetVersions = @(
                '3.0'
            )
        }

        PSUseCompatibleCommands = @{
            # Turns the rule on
            Enable = $true
    
            # Lists the PowerShell platforms we want to check compatibility with
            TargetProfiles = @(
                #PowerShell Version 3.0 Operating System Windows Server 2012
                'win-8_x64_6.2.9200.0_3.0_x64_4.0.30319.42000_framework'
            )
        }
    }
}

When I run PSSA under PS 5.v0 I get this result :

Invoke-ScriptAnalyzer -path  .\pssa.ps1 -Settings .\ScriptAnalyzerSettings.psd1

# RuleName                            Severity     ScriptName Line  Message
# --------                            --------     ---------- ----  -------
# PSUseCompatibleSyntax               Warning      pssa.ps1   6     The dynamic member invocation syntax
#                                                                   '$h.$controlName' is not available by default in
#                                                                   PowerShell versions 3
# PSUseCompatibleSyntax               Warning      pssa.ps1   7     The dynamic member invocation syntax
#                                                                   '$h.$controlName' is not available by default in
#                                                                   PowerShell versions 3
# PSUseCompatibleSyntax               Warning      pssa.ps1   13    The dynamic member invocation syntax '$a.$property'
#                                                                   is not available by default in PowerShell versions 3
# PSUseCompatibleSyntax               Warning      pssa.ps1   16    The dynamic member invocation syntax
#                                                                   '$typeExpression::$staticMember' is not available by
#                                                                   default in PowerShell versions 3
# PSUseCompatibleSyntax               Warning      pssa.ps1   19    The dynamic member invocation syntax '$x.$member' is
#                                                                   not available by default in PowerShell versions 3
# PSUseCompatibleSyntax               Warning      pssa.ps1   22    The dynamic method invocation syntax
#                                                                   '$Method.$method()' is not available by default in
#                                                                   PowerShell versions 3
# PSUseCompatibleSyntax               Warning      pssa.ps1   26    The dynamic member invocation syntax
#                                                                   '[EnvironmentVariableTarget]::$enumVal' is not
#                                                                   available by default in PowerShell versions 3

But when I read my logs, the rule message 'The dynamic member invocation syntax' seems incorrect and do not match the specifications in the 'Windows PowerShell Language Specification Version 3.0' file, chapter 7.1.2 Member access :

Name                           Value                                                                                               
----                           -----                                                                                               
PSVersion                      3.0                                                                                                 
WSManStackVersion              3.0                                                                                                 
SerializationVersion           1.1.0.1                                                                                             
CLRVersion                     4.0.30319.42000                                                                                     
BuildVersion                   6.2.9200.22198                                                                                      
PSCompatibleVersions           {1.0, 2.0, 3.0}                                                                                     
PSRemotingProtocolVersion      2.2                                                                                                 


Test compatibility
Test= Ok
 Array =3
Static=2147483647
Error:

The following Error (see $Method.$method() ), is saved by Export-CliXml:

$e=import-clixml "\\Server\Path\file.ps1xml"
$e
# invoke member w/ expression name
# Au caractère \\\\Server\Path\file.ps1:99 : 1
# + &$sb
# + ~~~~
#     + CategoryInfo          : OperationStopped: (:) [], NotImplementedException
#     + FullyQualifiedErrorId : System.NotImplementedException

$e.Exception|select *

# Message        : invoke member w/ expression name
# Data           : {System.Management.Automation.Interpreter.InterpretedFrameInfo}
# InnerException :
# TargetSite     : System.Object
#                  VisitInvokeMemberExpression(System.Management.Automation.Language.InvokeMemberExpressionAst)
# StackTrace     :    à
#                  System.Management.Automation.Language.Compiler.VisitInvokeMemberExpression(InvokeMemberExpressionAst
#                  invokeMemberExpressionAst)
#                     à System.Management.Automation.Language.Compiler.VisitCommandExpression(CommandExpressionAst
#                  commandExpressionAst)
#                     à System.Management.Automation.Language.Compiler.VisitPipeline(PipelineAst pipelineAst)
#                     à System.Management.Automation.Language.Compiler.CompileTrappableExpression(List`1 exprList,
#                  StatementAst stmt)
#                     à
#                  System.Management.Automation.Language.Compiler.CompileStatementListWithTraps(ReadOnlyCollection`1
#                  statements, ReadOnlyCollection`1 traps, List`1 exprs, List`1 temps)
#                     à System.Management.Automation.Language.Compiler.VisitStatementBlock(StatementBlockAst
#                  statementBlockAst)
#                     à System.Management.Automation.Language.Compiler.CaptureAstResults(Ast ast, CaptureAstContext
#                  context, MergeRedirectExprs generateRedirectExprs)
#                     à System.Management.Automation.Language.Compiler.<>c__DisplayClass5e.<VisitExpandableStringExpressi
#                  on>b__5d(ExpressionAst e)
#                     à System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
#                     à System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
#                     à System.Dynamic.Utils.CollectionExtensions.ToReadOnly[T](IEnumerable`1 enumerable)
#                     à System.Linq.Expressions.Expression.NewArrayInit(Type type, IEnumerable`1 initializers)
#                     à System.Management.Automation.Language.Compiler.VisitExpandableStringExpression(ExpandableStringEx
#                  pressionAst expandableStringExpressionAst)
#                     à System.Management.Automation.Language.Compiler.GetCommandArgumentExpression(CommandElementAst
#                  element)
#                     à System.Management.Automation.Language.Compiler.VisitCommand(CommandAst commandAst)
#                     à System.Management.Automation.Language.Compiler.VisitPipeline(PipelineAst pipelineAst)
#                     à System.Management.Automation.Language.Compiler.CompileTrappableExpression(List`1 exprList,
#                  StatementAst stmt)
#                     à
#                  System.Management.Automation.Language.Compiler.CompileStatementListWithTraps(ReadOnlyCollection`1
#                  statements, ReadOnlyCollection`1 traps, List`1 exprs, List`1 temps)
#                     à System.Management.Automation.Language.Compiler.CompileSingleLambda(ReadOnlyCollection`1
#                  statements, ReadOnlyCollection`1 traps, String funcName, IScriptExtent entryExtent, IScriptExtent
#                  exitExtent)
#                     à System.Management.Automation.Language.Compiler.CompileNamedBlock(NamedBlockAst namedBlockAst,
#                  String funcName)
#                     à System.Management.Automation.Language.Compiler.VisitScriptBlock(ScriptBlockAst scriptBlockAst)
#                     à System.Management.Automation.Language.Compiler.Compile(CompiledScriptBlockData scriptBlock,
#                  Boolean optimize)
#                     à System.Management.Automation.CompiledScriptBlockData.CompileUnoptimized()
#                     à System.Management.Automation.CompiledScriptBlockData.Compile(Boolean optimized)
#                     à System.Management.Automation.DlrScriptCommandProcessor.Init()
#                     à System.Management.Automation.CommandDiscovery.CreateCommandProcessorForScript(ScriptBlock
#                  scriptblock, ExecutionContext context, Boolean useNewScope, SessionStateInternal sessionState)
#                     à System.Management.Automation.PipelineOps.AddCommand(PipelineProcessor pipe,
#                  CommandParameterInternal[] commandElements, CommandBaseAst commandBaseAst, CommandRedirection[]
#                  redirections, ExecutionContext context)
#                     à System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput,
#                  CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][]
#                  commandRedirections, FunctionContext funcContext)
#                     à System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
#                     à System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame
#                  frame)
# HelpLink       :
# Source         : System.Management.Automation
# HResult        : -2147467263

Moreover the rule should be of severity 'Error' when I try to know if my code is compatible with Powershell v3, unless I did not understand the use and the behavior of this rule.

Why indicate that some syntax is problematic when it is not?

Activity

  1. rjmholt commented on Dec 12, 2019

    @rjmholt
    Contributor

    Why indicate that some syntax is problematic when it is not?

    It could be wrong. I was under the impression that that feature was added later, but if testing shows it works in PS 3, we should correct that.

    It should be fairly simple to take out if you'd like to open a PR:

    public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressionAst methodCallAst)
    {
    // Look for [typename]::new(...) and [typename]::$dynamicMethodName syntax
    if (!_targetVersions.Contains(s_v3) && !_targetVersions.Contains(s_v4))
    {
    return AstVisitAction.Continue;
    }
    if (_targetVersions.Contains(s_v3) && methodCallAst.Member is VariableExpressionAst)
    {
    string message = string.Format(
    CultureInfo.CurrentCulture,
    Strings.UseCompatibleSyntaxError,
    "dynamic method invocation",
    methodCallAst.Extent.Text,
    "3");
    _diagnosticAccumulator.Add(new DiagnosticRecord(
    message,
    methodCallAst.Extent,
    _rule.GetName(),
    _rule.Severity,
    _analyzedFilePath
    ));
    }
    if (!(methodCallAst.Expression is TypeExpressionAst typeExpressionAst))
    {
    return AstVisitAction.Continue;
    }
    if (!(methodCallAst.Member is StringConstantExpressionAst stringConstantAst))
    {
    return AstVisitAction.Continue;
    }
    if (stringConstantAst.Value.Equals("new", StringComparison.OrdinalIgnoreCase))
    {
    string typeName = typeExpressionAst.TypeName.FullName;
    CorrectionExtent suggestedCorrection = CreateNewObjectCorrection(
    _analyzedFilePath,
    methodCallAst.Extent,
    typeName,
    methodCallAst.Arguments);
    string message = string.Format(
    CultureInfo.CurrentCulture,
    Strings.UseCompatibleSyntaxError,
    "constructor",
    methodCallAst.Extent.Text,
    "3,4");
    _diagnosticAccumulator.Add(new DiagnosticRecord(
    message,
    methodCallAst.Extent,
    _rule.GetName(),
    _rule.Severity,
    _analyzedFilePath,
    ruleId: null,
    suggestedCorrections: new [] { suggestedCorrection }
    ));
    return AstVisitAction.Continue;
    }
    return AstVisitAction.Continue;
    }

  2. LaurentDardenne commented on Dec 14, 2019

    @LaurentDardenne
    Author

    Thanks.

  3. rjmholt commented on Dec 15, 2019

    @rjmholt
    Contributor

    Laurent Dardenne (@LaurentDardenne) is the rule incorrect? If so, we should keep this issue open to track the need to fix it

  4. LaurentDardenne commented on Dec 15, 2019

    @LaurentDardenne
    Author

    is the rule incorrect?

    Almost incorrect ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions