Implement Null Coalescing and Null Coalescing assignment operators (#10636)
This commit is contained in:
parent
cb66974b25
commit
425bc36a6f
|
@ -120,7 +120,10 @@ namespace System.Management.Automation
|
|||
description: "New formatting for ErrorRecord"),
|
||||
new ExperimentalFeature(
|
||||
name: "PSUpdatesNotification",
|
||||
description: "Print notification message when new releases are available")
|
||||
description: "Print notification message when new releases are available"),
|
||||
new ExperimentalFeature(
|
||||
name: "PSCoalescingOperators",
|
||||
description: "Support the null coalescing operator and null coalescing assignment operator in PowerShell language")
|
||||
};
|
||||
EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);
|
||||
|
||||
|
|
|
@ -221,6 +221,8 @@ namespace System.Management.Automation.Language
|
|||
|
||||
internal static readonly MethodInfo LanguagePrimitives_GetInvalidCastMessages =
|
||||
typeof(LanguagePrimitives).GetMethod(nameof(LanguagePrimitives.GetInvalidCastMessages), staticFlags);
|
||||
internal static readonly MethodInfo LanguagePrimitives_IsNullLike =
|
||||
typeof(LanguagePrimitives).GetMethod(nameof(LanguagePrimitives.IsNullLike), staticPublicFlags);
|
||||
internal static readonly MethodInfo LanguagePrimitives_ThrowInvalidCastException =
|
||||
typeof(LanguagePrimitives).GetMethod(nameof(LanguagePrimitives.ThrowInvalidCastException), staticFlags);
|
||||
|
||||
|
@ -786,6 +788,7 @@ namespace System.Management.Automation.Language
|
|||
{
|
||||
IAssignableValue av = left.GetAssignableValue();
|
||||
ExpressionType et = ExpressionType.Extension;
|
||||
|
||||
switch (tokenKind)
|
||||
{
|
||||
case TokenKind.Equals: return av.SetValue(this, right);
|
||||
|
@ -794,15 +797,49 @@ namespace System.Management.Automation.Language
|
|||
case TokenKind.MultiplyEquals: et = ExpressionType.Multiply; break;
|
||||
case TokenKind.DivideEquals: et = ExpressionType.Divide; break;
|
||||
case TokenKind.RemainderEquals: et = ExpressionType.Modulo; break;
|
||||
case TokenKind.QuestionQuestionEquals when ExperimentalFeature.IsEnabled("PSCoalescingOperators"): et = ExpressionType.Coalesce; break;
|
||||
}
|
||||
|
||||
var exprs = new List<Expression>();
|
||||
var temps = new List<ParameterExpression>();
|
||||
var getExpr = av.GetValue(this, exprs, temps);
|
||||
exprs.Add(av.SetValue(this, DynamicExpression.Dynamic(PSBinaryOperationBinder.Get(et), typeof(object), getExpr, right)));
|
||||
|
||||
if(et == ExpressionType.Coalesce)
|
||||
{
|
||||
exprs.Add(av.SetValue(this, Coalesce(getExpr, right)));
|
||||
}
|
||||
else
|
||||
{
|
||||
exprs.Add(av.SetValue(this, DynamicExpression.Dynamic(PSBinaryOperationBinder.Get(et), typeof(object), getExpr, right)));
|
||||
}
|
||||
|
||||
return Expression.Block(temps, exprs);
|
||||
}
|
||||
|
||||
private static Expression Coalesce(Expression left, Expression right)
|
||||
{
|
||||
Type leftType = left.Type;
|
||||
|
||||
if (leftType.IsValueType)
|
||||
{
|
||||
return left;
|
||||
}
|
||||
else if(leftType == typeof(DBNull) || leftType == typeof(NullString) || leftType == typeof(AutomationNull))
|
||||
{
|
||||
return right;
|
||||
}
|
||||
else
|
||||
{
|
||||
Expression lhs = left.Cast(typeof(object));
|
||||
Expression rhs = right.Cast(typeof(object));
|
||||
|
||||
return Expression.Condition(
|
||||
Expression.Call(CachedReflectionInfo.LanguagePrimitives_IsNullLike, lhs),
|
||||
rhs,
|
||||
lhs);
|
||||
}
|
||||
}
|
||||
|
||||
internal Expression GetLocal(int tupleIndex)
|
||||
{
|
||||
Expression result = LocalVariablesParameter;
|
||||
|
@ -5231,6 +5268,8 @@ namespace System.Management.Automation.Language
|
|||
CachedReflectionInfo.ParserOps_SplitOperator,
|
||||
_executionContextParameter, Expression.Constant(binaryExpressionAst.ErrorPosition), lhs.Cast(typeof(object)), rhs.Cast(typeof(object)),
|
||||
ExpressionCache.Constant(false));
|
||||
case TokenKind.QuestionQuestion when ExperimentalFeature.IsEnabled("PSCoalescingOperators"):
|
||||
return Coalesce(lhs, rhs);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unknown token in binary operator.");
|
||||
|
|
|
@ -6555,8 +6555,12 @@ namespace System.Management.Automation.Language
|
|||
// G bitwise-expression '-bxor' new-lines:opt comparison-expression
|
||||
// G
|
||||
// G comparison-expression:
|
||||
// G nullcoalesce-expression
|
||||
// G comparison-expression comparison-operator new-lines:opt nullcoalesce-expression
|
||||
// G
|
||||
// G nullcoalesce-expression:
|
||||
// G additive-expression
|
||||
// G comparison-expression comparison-operator new-lines:opt additive-expression
|
||||
// G nullcoalesce-expression '??' new-lines:opt additive-expression
|
||||
// G
|
||||
// G additive-expression:
|
||||
// G multiplicative-expression
|
||||
|
|
|
@ -416,6 +416,12 @@ namespace System.Management.Automation.Language
|
|||
/// <summary>The ternary operator '?'.</summary>
|
||||
QuestionMark = 100,
|
||||
|
||||
/// <summary>The null conditional assignment operator '??='.</summary>
|
||||
QuestionQuestionEquals = 101,
|
||||
|
||||
/// <summary>The null coalesce operator '??'.</summary>
|
||||
QuestionQuestion = 102,
|
||||
|
||||
#endregion Operators
|
||||
|
||||
#region Keywords
|
||||
|
@ -592,46 +598,51 @@ namespace System.Management.Automation.Language
|
|||
/// <summary>
|
||||
/// The precedence of the logical operators '-and', '-or', and '-xor'.
|
||||
/// </summary>
|
||||
BinaryPrecedenceLogical = 1,
|
||||
BinaryPrecedenceLogical = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// The precedence of the bitwise operators '-band', '-bor', and '-bxor'
|
||||
/// </summary>
|
||||
BinaryPrecedenceBitwise = 2,
|
||||
BinaryPrecedenceBitwise = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// The precedence of comparison operators including: '-eq', '-ne', '-ge', '-gt', '-lt', '-le', '-like', '-notlike',
|
||||
/// '-match', '-notmatch', '-replace', '-contains', '-notcontains', '-in', '-notin', '-split', '-join', '-is', '-isnot', '-as',
|
||||
/// and all of the case sensitive variants of these operators, if they exists.
|
||||
/// </summary>
|
||||
BinaryPrecedenceComparison = 3,
|
||||
BinaryPrecedenceComparison = 0x5,
|
||||
|
||||
/// <summary>
|
||||
/// The precedence of null coalesce operator '??'.
|
||||
/// </summary>
|
||||
BinaryPrecedenceCoalesce = 0x7,
|
||||
|
||||
/// <summary>
|
||||
/// The precedence of the binary operators '+' and '-'.
|
||||
/// </summary>
|
||||
BinaryPrecedenceAdd = 4,
|
||||
BinaryPrecedenceAdd = 0x9,
|
||||
|
||||
/// <summary>
|
||||
/// The precedence of the operators '*', '/', and '%'.
|
||||
/// </summary>
|
||||
BinaryPrecedenceMultiply = 5,
|
||||
BinaryPrecedenceMultiply = 0xa,
|
||||
|
||||
/// <summary>
|
||||
/// The precedence of the '-f' operator.
|
||||
/// </summary>
|
||||
BinaryPrecedenceFormat = 6,
|
||||
BinaryPrecedenceFormat = 0xc,
|
||||
|
||||
/// <summary>
|
||||
/// The precedence of the '..' operator.
|
||||
/// </summary>
|
||||
BinaryPrecedenceRange = 7,
|
||||
BinaryPrecedenceRange = 0xd,
|
||||
|
||||
#endregion Precedence Values
|
||||
|
||||
/// <summary>
|
||||
/// A bitmask to get the precedence of binary operators.
|
||||
/// </summary>
|
||||
BinaryPrecedenceMask = 0x00000007,
|
||||
BinaryPrecedenceMask = 0x0000000f,
|
||||
|
||||
/// <summary>
|
||||
/// The token is a keyword.
|
||||
|
@ -669,7 +680,7 @@ namespace System.Management.Automation.Language
|
|||
SpecialOperator = 0x00001000,
|
||||
|
||||
/// <summary>
|
||||
/// The token is one of the assignment operators: '=', '+=', '-=', '*=', '/=', or '%='
|
||||
/// The token is one of the assignment operators: '=', '+=', '-=', '*=', '/=', '%=' or '??='
|
||||
/// </summary>
|
||||
AssignmentOperator = 0x00002000,
|
||||
|
||||
|
@ -854,8 +865,8 @@ namespace System.Management.Automation.Language
|
|||
/* Shr */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold,
|
||||
/* Colon */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
|
||||
/* QuestionMark */ TokenFlags.TernaryOperator | TokenFlags.DisallowedInRestrictedMode,
|
||||
/* Reserved slot 3 */ TokenFlags.None,
|
||||
/* Reserved slot 4 */ TokenFlags.None,
|
||||
/* QuestionQuestionEquals */ TokenFlags.AssignmentOperator,
|
||||
/* QuestionQuestion */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceCoalesce,
|
||||
/* Reserved slot 5 */ TokenFlags.None,
|
||||
/* Reserved slot 6 */ TokenFlags.None,
|
||||
/* Reserved slot 7 */ TokenFlags.None,
|
||||
|
@ -1052,8 +1063,8 @@ namespace System.Management.Automation.Language
|
|||
/* Shr */ "-shr",
|
||||
/* Colon */ ":",
|
||||
/* QuestionMark */ "?",
|
||||
/* Reserved slot 3 */ string.Empty,
|
||||
/* Reserved slot 4 */ string.Empty,
|
||||
/* QuestionQuestionEquals */ "??=",
|
||||
/* QuestionQuestion */ "??",
|
||||
/* Reserved slot 5 */ string.Empty,
|
||||
/* Reserved slot 6 */ string.Empty,
|
||||
/* Reserved slot 7 */ string.Empty,
|
||||
|
|
|
@ -4994,6 +4994,25 @@ namespace System.Management.Automation.Language
|
|||
return this.NewToken(TokenKind.Colon);
|
||||
|
||||
case '?' when InExpressionMode():
|
||||
if (ExperimentalFeature.IsEnabled("PSCoalescingOperators"))
|
||||
{
|
||||
c1 = PeekChar();
|
||||
|
||||
if (c1 == '?')
|
||||
{
|
||||
SkipChar();
|
||||
c1 = PeekChar();
|
||||
|
||||
if (c1 == '=')
|
||||
{
|
||||
SkipChar();
|
||||
return this.NewToken(TokenKind.QuestionQuestionEquals);
|
||||
}
|
||||
|
||||
return this.NewToken(TokenKind.QuestionQuestion);
|
||||
}
|
||||
}
|
||||
|
||||
return this.NewToken(TokenKind.QuestionMark);
|
||||
|
||||
case '\0':
|
||||
|
|
266
test/powershell/Language/Operators/NullConditional.Tests.ps1
Normal file
266
test/powershell/Language/Operators/NullConditional.Tests.ps1
Normal file
|
@ -0,0 +1,266 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
Describe 'NullConditionalOperations' -Tags 'CI' {
|
||||
BeforeAll {
|
||||
|
||||
$skipTest = -not $EnabledExperimentalFeatures.Contains('PSCoalescingOperators')
|
||||
|
||||
if ($skipTest) {
|
||||
Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSCoalescingOperators' to be enabled." -Verbose
|
||||
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
|
||||
$PSDefaultParameterValues["it:skip"] = $true
|
||||
} else {
|
||||
$someGuid = New-Guid
|
||||
$typesTests = @(
|
||||
@{ name = 'string'; valueToSet = 'hello' }
|
||||
@{ name = 'dotnetType'; valueToSet = $someGuid }
|
||||
@{ name = 'byte'; valueToSet = [byte]0x94 }
|
||||
@{ name = 'intArray'; valueToSet = 1..2 }
|
||||
@{ name = 'stringArray'; valueToSet = 'a'..'c' }
|
||||
@{ name = 'emptyArray'; valueToSet = @(1, 2, 3) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
if ($skipTest) {
|
||||
$global:PSDefaultParameterValues = $originalDefaultParameterValues
|
||||
}
|
||||
}
|
||||
|
||||
Context "Null conditional assignment operator ??=" {
|
||||
It 'Variable doesnot exist' {
|
||||
|
||||
Remove-Variable variableDoesNotExist -ErrorAction SilentlyContinue -Force
|
||||
|
||||
$variableDoesNotExist ??= 1
|
||||
$variableDoesNotExist | Should -Be 1
|
||||
|
||||
$variableDoesNotExist ??= 2
|
||||
$variableDoesNotExist | Should -Be 1
|
||||
}
|
||||
|
||||
It 'Variable exists and is null' {
|
||||
$variableDoesNotExist = $null
|
||||
|
||||
$variableDoesNotExist ??= 2
|
||||
$variableDoesNotExist | Should -Be 2
|
||||
}
|
||||
|
||||
It 'Validate types - <name> can be set' -TestCases $typesTests {
|
||||
param ($name, $valueToSet)
|
||||
|
||||
$x = $null
|
||||
$x ??= $valueToSet
|
||||
$x | Should -Be $valueToSet
|
||||
}
|
||||
|
||||
It 'Validate hashtable can be set' {
|
||||
$x = $null
|
||||
$x ??= @{ 1 = '1' }
|
||||
$x.Keys | Should -Be @(1)
|
||||
}
|
||||
|
||||
It 'Validate lhs is returned' {
|
||||
$x = 100
|
||||
$x ??= 200
|
||||
$x | Should -Be 100
|
||||
}
|
||||
|
||||
It 'Rhs is a cmdlet' {
|
||||
$x = $null
|
||||
$x ??= (Get-Alias -Name 'where')
|
||||
$x.Definition | Should -BeExactly 'Where-Object'
|
||||
}
|
||||
|
||||
It 'Lhs is DBNull' {
|
||||
$x = [System.DBNull]::Value
|
||||
$x ??= 200
|
||||
$x | Should -Be 200
|
||||
}
|
||||
|
||||
It 'Lhs is AutomationNull' {
|
||||
$x = [System.Management.Automation.Internal.AutomationNull]::Value
|
||||
$x ??= 200
|
||||
$x | Should -Be 200
|
||||
}
|
||||
|
||||
It 'Lhs is NullString' {
|
||||
$x = [NullString]::Value
|
||||
$x ??= 200
|
||||
$x | Should -Be 200
|
||||
}
|
||||
|
||||
It 'Lhs is empty string' {
|
||||
$x = ''
|
||||
$x ??= 20
|
||||
$x | Should -BeExactly ''
|
||||
}
|
||||
|
||||
It 'Error case' {
|
||||
$e = $null
|
||||
$null = [System.Management.Automation.Language.Parser]::ParseInput('1 ??= 100', [ref] $null, [ref] $e)
|
||||
$e[0].ErrorId | Should -BeExactly 'InvalidLeftHandSide'
|
||||
}
|
||||
|
||||
It 'Variable is non-null' {
|
||||
$num = 10
|
||||
$num ??= 20
|
||||
|
||||
$num | Should -Be 10
|
||||
}
|
||||
|
||||
It 'Lhs is $?' {
|
||||
{ $???=$false}
|
||||
$? | Should -BeTrue
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Null coalesce operator ??' {
|
||||
BeforeEach {
|
||||
$x = $null
|
||||
}
|
||||
|
||||
It 'Variable does not exist' {
|
||||
Remove-Variable variableDoesNotExist -ErrorAction SilentlyContinue -Force
|
||||
$variableDoesNotExist ?? 100 | Should -Be 100
|
||||
}
|
||||
|
||||
It 'Variable exists but is null' {
|
||||
$x ?? 100 | Should -Be 100
|
||||
}
|
||||
|
||||
It 'Lhs is not null' {
|
||||
$x = 100
|
||||
$x ?? 200 | Should -Be 100
|
||||
}
|
||||
|
||||
It 'Lhs is a non-null constant' {
|
||||
1 ?? 2 | Should -Be 1
|
||||
}
|
||||
|
||||
It 'Lhs is `$null' {
|
||||
$null ?? 'string value' | Should -BeExactly 'string value'
|
||||
}
|
||||
|
||||
It 'Check precedence of ?? expression resolution' {
|
||||
$x ?? $null ?? 100 | Should -Be 100
|
||||
$null ?? $null ?? 100 | Should -Be 100
|
||||
$null ?? $null ?? $null | Should -Be $null
|
||||
$x ?? 200 ?? $null | Should -Be 200
|
||||
$x ?? 200 ?? 300 | Should -Be 200
|
||||
100 ?? $x ?? 200 | Should -Be 100
|
||||
$null ?? 100 ?? $null ?? 200 | Should -Be 100
|
||||
}
|
||||
|
||||
It 'Rhs is a cmdlet' {
|
||||
$result = $x ?? (Get-Alias -Name 'where')
|
||||
$result.Definition | Should -BeExactly 'Where-Object'
|
||||
}
|
||||
|
||||
It 'Lhs is DBNull' {
|
||||
$x = [System.DBNull]::Value
|
||||
$x ?? 200 | Should -Be 200
|
||||
}
|
||||
|
||||
It 'Lhs is AutomationNull' {
|
||||
$x = [System.Management.Automation.Internal.AutomationNull]::Value
|
||||
$x ?? 200 | Should -Be 200
|
||||
}
|
||||
|
||||
It 'Lhs is NullString' {
|
||||
$x = [NullString]::Value
|
||||
$x ?? 200 | Should -Be 200
|
||||
}
|
||||
|
||||
It 'Rhs is a get variable expression' {
|
||||
$x = [System.DBNull]::Value
|
||||
$y = 2
|
||||
$x ?? $y | Should -Be 2
|
||||
}
|
||||
|
||||
It 'Lhs is a constant' {
|
||||
[System.DBNull]::Value ?? 2 | Should -Be 2
|
||||
}
|
||||
|
||||
It 'Both are null constants' {
|
||||
[System.DBNull]::Value ?? [NullString]::Value | Should -Be ([NullString]::Value)
|
||||
}
|
||||
|
||||
It 'Lhs is $?' {
|
||||
{$???$false} | Should -BeTrue
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Null Coalesce ?? operator precedence' {
|
||||
It '?? precedence over -and' {
|
||||
$true -and $null ?? $true | Should -BeTrue
|
||||
}
|
||||
|
||||
It '?? precedence over -band' {
|
||||
1 -band $null ?? 1 | Should -Be 1
|
||||
}
|
||||
|
||||
It '?? precedence over -eq' {
|
||||
'x' -eq $null ?? 'x' | Should -BeTrue
|
||||
$null -eq $null ?? 'x' | Should -BeFalse
|
||||
}
|
||||
|
||||
It '?? precedence over -as' {
|
||||
'abc' -as [datetime] ?? 1 | Should -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It '?? precedence over -replace' {
|
||||
'x' -replace 'x',$null ?? 1 | Should -Be ([string]::empty)
|
||||
}
|
||||
|
||||
It '+ precedence over ??' {
|
||||
2 + $null ?? 3 | Should -Be 2
|
||||
}
|
||||
|
||||
It '* precedence over ??' {
|
||||
2 * $null ?? 3 | Should -Be 0
|
||||
}
|
||||
|
||||
It '-f precedence over ??' {
|
||||
"{0}" -f $null ?? 'b' | Should -Be ([string]::empty)
|
||||
}
|
||||
|
||||
It '.. precedence ove ??' {
|
||||
1..$null ?? 2 | Should -BeIn 1,0
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Combined usage of null conditional operators' {
|
||||
|
||||
BeforeAll {
|
||||
function GetNull {
|
||||
return $null
|
||||
}
|
||||
|
||||
function GetHello {
|
||||
return "Hello"
|
||||
}
|
||||
}
|
||||
|
||||
BeforeEach {
|
||||
$x = $null
|
||||
}
|
||||
|
||||
It '?? and ??= used together' {
|
||||
$x ??= 100 ?? 200
|
||||
$x | Should -Be 100
|
||||
}
|
||||
|
||||
It '?? and ??= chaining' {
|
||||
$x ??= $x ?? (GetNull) ?? (GetHello)
|
||||
$x | Should -BeExactly 'Hello'
|
||||
}
|
||||
|
||||
It 'First two are null' {
|
||||
$z ??= $null ?? 100
|
||||
$z | Should -Be 100
|
||||
}
|
||||
}
|
||||
}
|
|
@ -262,6 +262,52 @@ Describe 'assignment statement parsing' -Tags "CI" {
|
|||
ShouldBeParseError '$a,$b += 1,2' InvalidLeftHandSide 0
|
||||
}
|
||||
|
||||
Describe 'null coalescing assignment statement parsing' -Tag 'CI' {
|
||||
BeforeAll {
|
||||
$skipTest = -not $EnabledExperimentalFeatures.Contains('PSCoalescingOperators')
|
||||
if ($skipTest) {
|
||||
Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSCoalescingOperators' to be enabled." -Verbose
|
||||
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
|
||||
$PSDefaultParameterValues["it:skip"] = $true
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
if ($skipTest) {
|
||||
$global:PSDefaultParameterValues = $originalDefaultParameterValues
|
||||
}
|
||||
}
|
||||
|
||||
ShouldBeParseError '1 ??= 1' InvalidLeftHandSide 0
|
||||
ShouldBeParseError '@() ??= 1' InvalidLeftHandSide 0
|
||||
ShouldBeParseError '@{} ??= 1' InvalidLeftHandSide 0
|
||||
ShouldBeParseError '1..2 ??= 1' InvalidLeftHandSide 0
|
||||
ShouldBeParseError '[int] ??= 1' InvalidLeftHandSide 0
|
||||
ShouldBeParseError '$cricket ?= $soccer' ExpectedValueExpression,InvalidLeftHandSide 10,0
|
||||
}
|
||||
|
||||
Describe 'null coalescing statement parsing' -Tag "CI" {
|
||||
BeforeAll {
|
||||
$skipTest = -not $EnabledExperimentalFeatures.Contains('PSCoalescingOperators')
|
||||
if ($skipTest) {
|
||||
Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSCoalescingOperators' to be enabled." -Verbose
|
||||
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
|
||||
$PSDefaultParameterValues["it:skip"] = $true
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
if ($skipTest) {
|
||||
$global:PSDefaultParameterValues = $originalDefaultParameterValues
|
||||
}
|
||||
}
|
||||
|
||||
ShouldBeParseError '$x??=' ExpectedValueExpression 5
|
||||
ShouldBeParseError '$x ??Get-Thing' ExpectedValueExpression,UnexpectedToken 5,5
|
||||
ShouldBeParseError '$??=$false' ExpectedValueExpression,InvalidLeftHandSide 3,0
|
||||
ShouldBeParseError '$hello ??? $what' ExpectedValueExpression,MissingColonInTernaryExpression 9,17
|
||||
}
|
||||
|
||||
Describe 'splatting parsing' -Tags "CI" {
|
||||
ShouldBeParseError '@a' SplattingNotPermitted 0
|
||||
ShouldBeParseError 'foreach (@a in $b) {}' SplattingNotPermitted 9
|
||||
|
|
Loading…
Reference in a new issue