mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Flag default switch statement condition clause as keyword (#10487)
This commit is contained in:
@@ -2746,28 +2746,42 @@ namespace System.Management.Automation.Language
|
||||
|
||||
while (true)
|
||||
{
|
||||
ExpressionAst clauseCondition = GetSingleCommandArgument(CommandArgumentContext.SwitchCondition);
|
||||
if (clauseCondition == null)
|
||||
Token token = PeekToken();
|
||||
bool isDefaultClause = token.Kind == TokenKind.Default;
|
||||
ExpressionAst clauseCondition = null;
|
||||
|
||||
if (isDefaultClause)
|
||||
{
|
||||
// ErrorRecovery: if we don't have anything that looks like a condition, we won't
|
||||
// find a body (because a body is just a script block, which works as a condition.)
|
||||
// So don't look for a body, hope we find the '}' next.
|
||||
|
||||
isError = true;
|
||||
ReportIncompleteInput(After(endErrorStatement),
|
||||
nameof(ParserStrings.MissingSwitchConditionExpression),
|
||||
ParserStrings.MissingSwitchConditionExpression);
|
||||
// Consume a closing curly, if there is one, to avoid an extra error
|
||||
if (PeekToken().Kind == TokenKind.RCurly)
|
||||
// Consume the 'default' token.
|
||||
SkipToken();
|
||||
clauseCondition = new StringConstantExpressionAst(token.Extent, token.Text, StringConstantType.BareWord);
|
||||
}
|
||||
else
|
||||
{
|
||||
clauseCondition = GetSingleCommandArgument(CommandArgumentContext.SwitchCondition);
|
||||
if (clauseCondition == null)
|
||||
{
|
||||
SkipToken();
|
||||
}
|
||||
// ErrorRecovery: if we don't have anything that looks like a condition, we won't
|
||||
// find a body (because a body is just a script block, which works as a condition.)
|
||||
// So don't look for a body, hope we find the '}' next.
|
||||
isError = true;
|
||||
ReportIncompleteInput(After(endErrorStatement),
|
||||
nameof(ParserStrings.MissingSwitchConditionExpression),
|
||||
ParserStrings.MissingSwitchConditionExpression);
|
||||
|
||||
break;
|
||||
// Consume a closing curly, if there is one, to avoid an extra error
|
||||
if (PeekToken().Kind == TokenKind.RCurly)
|
||||
{
|
||||
SkipToken();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
errorAsts.Add(clauseCondition);
|
||||
endErrorStatement = clauseCondition.Extent;
|
||||
|
||||
StatementBlockAst clauseBody = StatementBlockRule();
|
||||
if (clauseBody == null)
|
||||
{
|
||||
@@ -2783,11 +2797,7 @@ namespace System.Management.Automation.Language
|
||||
errorAsts.Add(clauseBody);
|
||||
endErrorStatement = clauseBody.Extent;
|
||||
|
||||
var clauseConditionString = clauseCondition as StringConstantExpressionAst;
|
||||
|
||||
if (clauseConditionString != null &&
|
||||
clauseConditionString.StringConstantType == StringConstantType.BareWord &&
|
||||
clauseConditionString.Value.Equals("default", StringComparison.OrdinalIgnoreCase))
|
||||
if (isDefaultClause)
|
||||
{
|
||||
if (@default != null)
|
||||
{
|
||||
@@ -2809,7 +2819,7 @@ namespace System.Management.Automation.Language
|
||||
|
||||
SkipNewlinesAndSemicolons();
|
||||
|
||||
Token token = PeekToken();
|
||||
token = PeekToken();
|
||||
if (token.Kind == TokenKind.RCurly)
|
||||
{
|
||||
rCurly = token;
|
||||
|
||||
@@ -585,6 +585,9 @@ namespace System.Management.Automation.Language
|
||||
/// <summary>The 'base' keyword</summary>
|
||||
Base = 168,
|
||||
|
||||
/// <summary>The 'default' keyword</summary>
|
||||
Default = 169,
|
||||
|
||||
#endregion Keywords
|
||||
}
|
||||
|
||||
@@ -944,6 +947,7 @@ namespace System.Management.Automation.Language
|
||||
/* Command */ TokenFlags.Keyword,
|
||||
/* Hidden */ TokenFlags.Keyword,
|
||||
/* Base */ TokenFlags.Keyword,
|
||||
/* Default */ TokenFlags.Keyword,
|
||||
|
||||
#endregion Flags for keywords
|
||||
};
|
||||
@@ -1142,6 +1146,7 @@ namespace System.Management.Automation.Language
|
||||
/* Command */ "command",
|
||||
/* Hidden */ "hidden",
|
||||
/* Base */ "base",
|
||||
/* Default */ "default",
|
||||
|
||||
#endregion Text for keywords
|
||||
};
|
||||
@@ -1149,9 +1154,9 @@ namespace System.Management.Automation.Language
|
||||
#if DEBUG
|
||||
static TokenTraits()
|
||||
{
|
||||
Diagnostics.Assert(s_staticTokenFlags.Length == ((int)TokenKind.Base + 1),
|
||||
Diagnostics.Assert(s_staticTokenFlags.Length == ((int)TokenKind.Default + 1),
|
||||
"Table size out of sync with enum - _staticTokenFlags");
|
||||
Diagnostics.Assert(s_tokenText.Length == ((int)TokenKind.Base + 1),
|
||||
Diagnostics.Assert(s_tokenText.Length == ((int)TokenKind.Default + 1),
|
||||
"Table size out of sync with enum - _tokenText");
|
||||
// Some random assertions to make sure the enum and the traits are in sync
|
||||
Diagnostics.Assert(GetTraits(TokenKind.Begin) == (TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName),
|
||||
|
||||
@@ -629,7 +629,7 @@ namespace System.Management.Automation.Language
|
||||
/*A*/ "configuration", "public", "private", "static", /*A*/
|
||||
/*B*/ "interface", "enum", "namespace", "module", /*B*/
|
||||
/*C*/ "type", "assembly", "command", "hidden", /*C*/
|
||||
/*D*/ "base", /*D*/
|
||||
/*D*/ "base", "default", /*D*/
|
||||
};
|
||||
|
||||
private static readonly TokenKind[] s_keywordTokenKind = new TokenKind[] {
|
||||
@@ -645,7 +645,7 @@ namespace System.Management.Automation.Language
|
||||
/*A*/ TokenKind.Configuration, TokenKind.Public, TokenKind.Private, TokenKind.Static, /*A*/
|
||||
/*B*/ TokenKind.Interface, TokenKind.Enum, TokenKind.Namespace,TokenKind.Module, /*B*/
|
||||
/*C*/ TokenKind.Type, TokenKind.Assembly, TokenKind.Command, TokenKind.Hidden, /*C*/
|
||||
/*D*/ TokenKind.Base, /*D*/
|
||||
/*D*/ TokenKind.Base, TokenKind.Default, /*D*/
|
||||
};
|
||||
|
||||
internal static readonly string[] _operatorText = new string[] {
|
||||
|
||||
@@ -479,3 +479,165 @@ Describe "ParserError type tests" -Tag CI {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Keywords 'default', 'hidden', 'in', 'static' Token parsing" -Tags CI {
|
||||
BeforeAll {
|
||||
$testCases_basic = @(
|
||||
@{
|
||||
Script = 'switch (1) {default {0} 1 {1}}'
|
||||
TokensToCheck = @{
|
||||
5 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Default
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'switch (1) {"default" {0} 1 {1}}'
|
||||
TokensToCheck = @{
|
||||
5 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::StringExpandable
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::None
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'switch (1) {adefault {0} 1 {1}}'
|
||||
TokensToCheck = @{
|
||||
5 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Identifier
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::None
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'foreach ($i in 1..2) {$i}'
|
||||
TokensToCheck = @{
|
||||
3 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::In
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'class test {hidden $a; static aMethod () {return $this.a} }'
|
||||
TokensToCheck = @{
|
||||
3 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Hidden
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
}
|
||||
6 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Static
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'echo default hidden in static'
|
||||
TokensToCheck = @{
|
||||
1 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Generic
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::None
|
||||
}
|
||||
2 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Generic
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::None
|
||||
}
|
||||
3 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Generic
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::None
|
||||
}
|
||||
4 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Generic
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::None
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'default'
|
||||
TokensToCheck = @{
|
||||
0 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Default
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword -bor [System.Management.Automation.Language.TokenFlags]::CommandName
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::CommandName
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'hidden'
|
||||
TokensToCheck = @{
|
||||
0 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Hidden
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword -bor [System.Management.Automation.Language.TokenFlags]::CommandName
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::CommandName
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'in'
|
||||
TokensToCheck = @{
|
||||
0 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::In
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword -bor [System.Management.Automation.Language.TokenFlags]::CommandName
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::CommandName
|
||||
}
|
||||
}
|
||||
}
|
||||
@{
|
||||
Script = 'static'
|
||||
TokensToCheck = @{
|
||||
0 = @{
|
||||
TokenKind = [System.Management.Automation.Language.TokenKind]::Static
|
||||
TokenFlags_Mask = [System.Management.Automation.Language.TokenFlags]::Keyword -bor [System.Management.Automation.Language.TokenFlags]::CommandName
|
||||
TokenFlags_Value = [System.Management.Automation.Language.TokenFlags]::CommandName
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
}
|
||||
|
||||
It "Keywords 'default', 'hidden', 'in', 'static' in {<Script>} correctly tokenized." -TestCases $testCases_basic {
|
||||
param($Script, $TokensToCheck)
|
||||
|
||||
$tks = $null
|
||||
$ers = $null
|
||||
$result = [System.Management.Automation.Language.Parser]::ParseInput($Script, [ref]$tks, [ref]$ers)
|
||||
|
||||
foreach ($token in $TokensToCheck.Keys ) {
|
||||
if ($TokensToCheck[$Token].ContainsKey('TokenKind')) {
|
||||
$tks[$token].Kind | Should -Be $TokensToCheck[$token].TokenKind -Because 'because TokenKind must be as expected'
|
||||
}
|
||||
if ($TokensToCheck[$Token].ContainsKey('TokenFlags_Value')) {
|
||||
$tks[$token].TokenFlags -band $TokensToCheck[$token].TokenFlags_Mask | Should -Be $TokensToCheck[$token].TokenFlags_Value -Because 'because TokenFlags must be as expected after masking'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$testKeywordsAsCmds = @(
|
||||
@{ Keyword = 'default' }
|
||||
@{ Keyword = 'hidden' }
|
||||
@{ Keyword = 'in' } # Note: this overwrites Pester's `In` function.
|
||||
@{ Keyword = 'static' }
|
||||
)
|
||||
|
||||
It "<Keyword> can be used as command name" -TestCases $testKeywordsAsCmds {
|
||||
param($Keyword)
|
||||
|
||||
Invoke-Expression "function $Keyword { '$Keyword' }"
|
||||
|
||||
. $Keyword | Should -BeExactly $Keyword
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user