mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:59:41 +00:00
Fix parsing of PATTERN as T? when
The logic for parsing types has special disambiguation rules for deciding whether a trailing `?` should be included in the type, based on what token(s) follow the `?`. In the case where the token that follows the `?` is `when`, we need to look further ahead to disambiguate, to distinguish `PATTERN as T? when guard` from something like `EXPRESSION is T ? when : otherwise`. (Note: an alternative implementation would be to disambiguate based on whether we're parsing a pattern or an expression. But in the future I want to move toward an architecture where expression parsing and pattern parsing are combined, so that if the parser makes the wrong decision about whether it's looking at a pattern or an expression, error recovery will do a better job. So I'm disambiguating based solely on what follows the `?`.) Bug: https://github.com/dart-lang/sdk/issues/50035 Change-Id: Idbc780b7b54fecc7fd01cae868c34771564dd804 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/292282 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
This commit is contained in:
parent
e6d352dec1
commit
452dcdf517
|
@ -7578,11 +7578,15 @@ class Parser {
|
|||
// rather than the start of a conditional expression.
|
||||
return typeInfo;
|
||||
}
|
||||
if (optional('{', next)) {
|
||||
if (optional('{', next) || optional('when', next)) {
|
||||
// <expression> is/as <type> ? {
|
||||
// This could be either a nullable type (e.g. last initializer in a
|
||||
// constructor with a body), or a non-nullable type and a conditional.
|
||||
// As with "?[" we check and have it as a conditional if it can be.
|
||||
// This could be either a nullable type (e.g. last initializer in a
|
||||
// constructor with a body), or a non-nullable type and a conditional.
|
||||
// <expression> is/as <type> ? when
|
||||
// This could be either a nullable type (e.g. a cast pattern followed
|
||||
// by a guard), or a non-nullable type and a conditional (where the
|
||||
// first token of the "then" expression is the identifier `when`).
|
||||
// If it can be successfully parsed as a conditional, we do so.
|
||||
bool isConditional = canParseAsConditional(skipToken);
|
||||
if (!isConditional) {
|
||||
return typeInfo;
|
||||
|
|
|
@ -8296,6 +8296,73 @@ SwitchExpression
|
|||
''');
|
||||
}
|
||||
|
||||
test_typeQuestionBeforeWhen_conditional() {
|
||||
// The logic for parsing types has special disambiguation rules for deciding
|
||||
// whether a trailing `?` should be included in the type; these rules are
|
||||
// based primarily on what token(s) follow the `?`. Make sure that these
|
||||
// rules do the right thing if the token that follows the `?` is `when`, but
|
||||
// the `when` is an ordinary identifier.
|
||||
_parse('''
|
||||
void f(condition, when, otherwise) => condition as bool ? when : otherwise;
|
||||
''');
|
||||
var node = findNode.functionDeclaration('=>').functionExpression.body;
|
||||
assertParsedNodeText(node, r'''
|
||||
ExpressionFunctionBody
|
||||
functionDefinition: =>
|
||||
expression: ConditionalExpression
|
||||
condition: AsExpression
|
||||
expression: SimpleIdentifier
|
||||
token: condition
|
||||
asOperator: as
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: bool
|
||||
question: ?
|
||||
thenExpression: SimpleIdentifier
|
||||
token: when
|
||||
colon: :
|
||||
elseExpression: SimpleIdentifier
|
||||
token: otherwise
|
||||
semicolon: ;
|
||||
''');
|
||||
}
|
||||
|
||||
test_typeQuestionBeforeWhen_guard() {
|
||||
// The logic for parsing types has special disambiguation rules for deciding
|
||||
// whether a trailing `?` should be included in the type; these rules are
|
||||
// based primarily on what token(s) follow the `?`. Make sure that these
|
||||
// rules do the right thing if the token that follows the `?` is the `when`
|
||||
// of a pattern guard.
|
||||
_parse('''
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case _ as int? when x == null:
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
var node = findNode.singleGuardedPattern;
|
||||
assertParsedNodeText(node, r'''
|
||||
GuardedPattern
|
||||
pattern: CastPattern
|
||||
pattern: WildcardPattern
|
||||
name: _
|
||||
asToken: as
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: int
|
||||
question: ?
|
||||
whenClause: WhenClause
|
||||
whenKeyword: when
|
||||
expression: BinaryExpression
|
||||
leftOperand: SimpleIdentifier
|
||||
token: x
|
||||
operator: ==
|
||||
rightOperand: NullLiteral
|
||||
literal: null
|
||||
''');
|
||||
}
|
||||
|
||||
test_variable_bare_insideCast() {
|
||||
_parse('''
|
||||
void f(x) {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
void f(condition, when, otherwise) => condition as bool ? when : otherwise;
|
|
@ -0,0 +1,57 @@
|
|||
beginCompilationUnit(void)
|
||||
beginMetadataStar(void)
|
||||
endMetadataStar(0)
|
||||
beginTopLevelMember(void)
|
||||
beginTopLevelMethod(, null, null)
|
||||
handleVoidKeyword(void)
|
||||
handleIdentifier(f, topLevelFunctionDeclaration)
|
||||
handleNoTypeVariables(()
|
||||
beginFormalParameters((, MemberKind.TopLevelMethod)
|
||||
beginMetadataStar(condition)
|
||||
endMetadataStar(0)
|
||||
beginFormalParameter(condition, MemberKind.TopLevelMethod, null, null, null)
|
||||
handleNoType(()
|
||||
handleIdentifier(condition, formalParameterDeclaration)
|
||||
handleFormalParameterWithoutValue(,)
|
||||
endFormalParameter(null, null, null, condition, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
beginMetadataStar(when)
|
||||
endMetadataStar(0)
|
||||
beginFormalParameter(when, MemberKind.TopLevelMethod, null, null, null)
|
||||
handleNoType(,)
|
||||
handleIdentifier(when, formalParameterDeclaration)
|
||||
handleFormalParameterWithoutValue(,)
|
||||
endFormalParameter(null, null, null, when, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
beginMetadataStar(otherwise)
|
||||
endMetadataStar(0)
|
||||
beginFormalParameter(otherwise, MemberKind.TopLevelMethod, null, null, null)
|
||||
handleNoType(,)
|
||||
handleIdentifier(otherwise, formalParameterDeclaration)
|
||||
handleFormalParameterWithoutValue())
|
||||
endFormalParameter(null, null, null, otherwise, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
endFormalParameters(3, (, ), MemberKind.TopLevelMethod)
|
||||
handleAsyncModifier(null, null)
|
||||
handleIdentifier(condition, expression)
|
||||
handleNoTypeArguments(as)
|
||||
handleNoArguments(as)
|
||||
handleSend(condition, as)
|
||||
beginAsOperatorType(as)
|
||||
handleIdentifier(bool, typeReference)
|
||||
handleNoTypeArguments(?)
|
||||
handleType(bool, null)
|
||||
endAsOperatorType(as)
|
||||
handleAsOperator(as)
|
||||
beginConditionalExpression(?)
|
||||
handleIdentifier(when, expression)
|
||||
handleNoTypeArguments(:)
|
||||
handleNoArguments(:)
|
||||
handleSend(when, :)
|
||||
handleConditionalExpressionColon()
|
||||
handleIdentifier(otherwise, expression)
|
||||
handleNoTypeArguments(;)
|
||||
handleNoArguments(;)
|
||||
handleSend(otherwise, ;)
|
||||
endConditionalExpression(?, :)
|
||||
handleExpressionFunctionBody(=>, ;)
|
||||
endTopLevelMethod(void, null, ;)
|
||||
endTopLevelDeclaration()
|
||||
endCompilationUnit(1, )
|
|
@ -0,0 +1,144 @@
|
|||
parseUnit(void)
|
||||
skipErrorTokens(void)
|
||||
listener: beginCompilationUnit(void)
|
||||
syntheticPreviousToken(void)
|
||||
parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
|
||||
parseMetadataStar()
|
||||
listener: beginMetadataStar(void)
|
||||
listener: endMetadataStar(0)
|
||||
parseTopLevelMemberImpl()
|
||||
listener: beginTopLevelMember(void)
|
||||
parseTopLevelMethod(, null, null, , Instance of 'VoidType', null, f, false)
|
||||
listener: beginTopLevelMethod(, null, null)
|
||||
listener: handleVoidKeyword(void)
|
||||
ensureIdentifierPotentiallyRecovered(void, topLevelFunctionDeclaration, false)
|
||||
listener: handleIdentifier(f, topLevelFunctionDeclaration)
|
||||
parseMethodTypeVar(f)
|
||||
listener: handleNoTypeVariables(()
|
||||
parseGetterOrFormalParameters(f, f, false, MemberKind.TopLevelMethod)
|
||||
parseFormalParameters(f, MemberKind.TopLevelMethod)
|
||||
parseFormalParametersRest((, MemberKind.TopLevelMethod)
|
||||
listener: beginFormalParameters((, MemberKind.TopLevelMethod)
|
||||
parseFormalParameter((, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
parseMetadataStar(()
|
||||
listener: beginMetadataStar(condition)
|
||||
listener: endMetadataStar(0)
|
||||
listener: beginFormalParameter(condition, MemberKind.TopLevelMethod, null, null, null)
|
||||
listener: handleNoType(()
|
||||
ensureIdentifier((, formalParameterDeclaration)
|
||||
listener: handleIdentifier(condition, formalParameterDeclaration)
|
||||
listener: handleFormalParameterWithoutValue(,)
|
||||
listener: endFormalParameter(null, null, null, condition, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
parseFormalParameter(,, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
parseMetadataStar(,)
|
||||
listener: beginMetadataStar(when)
|
||||
listener: endMetadataStar(0)
|
||||
listener: beginFormalParameter(when, MemberKind.TopLevelMethod, null, null, null)
|
||||
listener: handleNoType(,)
|
||||
ensureIdentifier(,, formalParameterDeclaration)
|
||||
inPlainSync()
|
||||
listener: handleIdentifier(when, formalParameterDeclaration)
|
||||
listener: handleFormalParameterWithoutValue(,)
|
||||
listener: endFormalParameter(null, null, null, when, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
parseFormalParameter(,, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
parseMetadataStar(,)
|
||||
listener: beginMetadataStar(otherwise)
|
||||
listener: endMetadataStar(0)
|
||||
listener: beginFormalParameter(otherwise, MemberKind.TopLevelMethod, null, null, null)
|
||||
listener: handleNoType(,)
|
||||
ensureIdentifier(,, formalParameterDeclaration)
|
||||
listener: handleIdentifier(otherwise, formalParameterDeclaration)
|
||||
listener: handleFormalParameterWithoutValue())
|
||||
listener: endFormalParameter(null, null, null, otherwise, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
listener: endFormalParameters(3, (, ), MemberKind.TopLevelMethod)
|
||||
parseAsyncModifierOpt())
|
||||
listener: handleAsyncModifier(null, null)
|
||||
inPlainSync()
|
||||
parseFunctionBody(), false, false)
|
||||
parseExpressionFunctionBody(=>, false)
|
||||
parseExpression(=>)
|
||||
looksLikeOuterPatternEquals(=>)
|
||||
skipOuterPattern(=>)
|
||||
skipObjectPatternRest(condition)
|
||||
parsePrecedenceExpression(=>, 1, true, ConstantPatternContext.none)
|
||||
parseUnaryExpression(=>, true, ConstantPatternContext.none)
|
||||
parsePrimary(=>, expression, ConstantPatternContext.none)
|
||||
parseSendOrFunctionLiteral(=>, expression, ConstantPatternContext.none)
|
||||
parseSend(=>, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(=>)
|
||||
ensureIdentifier(=>, expression)
|
||||
listener: handleIdentifier(condition, expression)
|
||||
listener: handleNoTypeArguments(as)
|
||||
parseArgumentsOpt(condition)
|
||||
listener: handleNoArguments(as)
|
||||
listener: handleSend(condition, as)
|
||||
parseAsOperatorRest(condition)
|
||||
listener: beginAsOperatorType(as)
|
||||
computeTypeAfterIsOrAs(as)
|
||||
canParseAsConditional(?)
|
||||
parseExpressionWithoutCascade(?)
|
||||
parsePrecedenceExpression(?, 1, false, ConstantPatternContext.none)
|
||||
parseUnaryExpression(?, false, ConstantPatternContext.none)
|
||||
parsePrimary(?, expression, ConstantPatternContext.none)
|
||||
inPlainSync()
|
||||
parseSendOrFunctionLiteral(?, expression, ConstantPatternContext.none)
|
||||
parseSend(?, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(?)
|
||||
ensureIdentifier(?, expression)
|
||||
inPlainSync()
|
||||
parseArgumentsOpt(when)
|
||||
parseExpressionWithoutCascade(:)
|
||||
parsePrecedenceExpression(:, 1, false, ConstantPatternContext.none)
|
||||
parseUnaryExpression(:, false, ConstantPatternContext.none)
|
||||
parsePrimary(:, expression, ConstantPatternContext.none)
|
||||
parseSendOrFunctionLiteral(:, expression, ConstantPatternContext.none)
|
||||
parseSend(:, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(:)
|
||||
ensureIdentifier(:, expression)
|
||||
parseArgumentsOpt(otherwise)
|
||||
listener: handleIdentifier(bool, typeReference)
|
||||
listener: handleNoTypeArguments(?)
|
||||
listener: handleType(bool, null)
|
||||
listener: endAsOperatorType(as)
|
||||
listener: handleAsOperator(as)
|
||||
skipChainedAsIsOperators(bool)
|
||||
parseConditionalExpressionRest(bool)
|
||||
listener: beginConditionalExpression(?)
|
||||
parseExpressionWithoutCascade(?)
|
||||
parsePrecedenceExpression(?, 1, false, ConstantPatternContext.none)
|
||||
parseUnaryExpression(?, false, ConstantPatternContext.none)
|
||||
parsePrimary(?, expression, ConstantPatternContext.none)
|
||||
inPlainSync()
|
||||
parseSendOrFunctionLiteral(?, expression, ConstantPatternContext.none)
|
||||
parseSend(?, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(?)
|
||||
ensureIdentifier(?, expression)
|
||||
inPlainSync()
|
||||
listener: handleIdentifier(when, expression)
|
||||
listener: handleNoTypeArguments(:)
|
||||
parseArgumentsOpt(when)
|
||||
listener: handleNoArguments(:)
|
||||
listener: handleSend(when, :)
|
||||
ensureColon(when)
|
||||
listener: handleConditionalExpressionColon()
|
||||
parseExpressionWithoutCascade(:)
|
||||
parsePrecedenceExpression(:, 1, false, ConstantPatternContext.none)
|
||||
parseUnaryExpression(:, false, ConstantPatternContext.none)
|
||||
parsePrimary(:, expression, ConstantPatternContext.none)
|
||||
parseSendOrFunctionLiteral(:, expression, ConstantPatternContext.none)
|
||||
parseSend(:, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(:)
|
||||
ensureIdentifier(:, expression)
|
||||
listener: handleIdentifier(otherwise, expression)
|
||||
listener: handleNoTypeArguments(;)
|
||||
parseArgumentsOpt(otherwise)
|
||||
listener: handleNoArguments(;)
|
||||
listener: handleSend(otherwise, ;)
|
||||
listener: endConditionalExpression(?, :)
|
||||
ensureSemicolon(otherwise)
|
||||
listener: handleExpressionFunctionBody(=>, ;)
|
||||
inGenerator()
|
||||
listener: endTopLevelMethod(void, null, ;)
|
||||
listener: endTopLevelDeclaration()
|
||||
reportAllErrorTokens(void)
|
||||
listener: endCompilationUnit(1, )
|
|
@ -0,0 +1,5 @@
|
|||
void f(condition, when, otherwise) => condition as bool ? when : otherwise;
|
||||
|
||||
|
||||
void[KeywordToken] f[StringToken]([BeginToken]condition[StringToken],[SimpleToken] when[KeywordToken],[SimpleToken] otherwise[StringToken])[SimpleToken] =>[SimpleToken] condition[StringToken] as[KeywordToken] bool[StringToken] ?[SimpleToken] when[KeywordToken] :[SimpleToken] otherwise[StringToken];[SimpleToken]
|
||||
[SimpleToken]
|
|
@ -0,0 +1,5 @@
|
|||
void f(condition, when, otherwise) => condition as bool ? when : otherwise;
|
||||
|
||||
|
||||
void[KeywordToken] f[StringToken]([BeginToken]condition[StringToken],[SimpleToken] when[KeywordToken],[SimpleToken] otherwise[StringToken])[SimpleToken] =>[SimpleToken] condition[StringToken] as[KeywordToken] bool[StringToken] ?[SimpleToken] when[KeywordToken] :[SimpleToken] otherwise[StringToken];[SimpleToken]
|
||||
[SimpleToken]
|
|
@ -0,0 +1,6 @@
|
|||
void f(x) {
|
||||
switch (x) {
|
||||
case _ as int? when x == null:
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
beginCompilationUnit(void)
|
||||
beginMetadataStar(void)
|
||||
endMetadataStar(0)
|
||||
beginTopLevelMember(void)
|
||||
beginTopLevelMethod(, null, null)
|
||||
handleVoidKeyword(void)
|
||||
handleIdentifier(f, topLevelFunctionDeclaration)
|
||||
handleNoTypeVariables(()
|
||||
beginFormalParameters((, MemberKind.TopLevelMethod)
|
||||
beginMetadataStar(x)
|
||||
endMetadataStar(0)
|
||||
beginFormalParameter(x, MemberKind.TopLevelMethod, null, null, null)
|
||||
handleNoType(()
|
||||
handleIdentifier(x, formalParameterDeclaration)
|
||||
handleFormalParameterWithoutValue())
|
||||
endFormalParameter(null, null, null, x, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
|
||||
handleAsyncModifier(null, null)
|
||||
beginBlockFunctionBody({)
|
||||
beginSwitchStatement(switch)
|
||||
handleIdentifier(x, expression)
|
||||
handleNoTypeArguments())
|
||||
handleNoArguments())
|
||||
handleSend(x, ))
|
||||
handleParenthesizedCondition((, null, null)
|
||||
beginSwitchBlock({)
|
||||
beginCaseExpression(case)
|
||||
handleNoType(_)
|
||||
handleWildcardPattern(null, _)
|
||||
beginAsOperatorType(as)
|
||||
handleIdentifier(int, typeReference)
|
||||
handleNoTypeArguments(?)
|
||||
handleType(int, ?)
|
||||
endAsOperatorType(as)
|
||||
handleCastPattern(as)
|
||||
beginSwitchCaseWhenClause(when)
|
||||
handleIdentifier(x, expression)
|
||||
handleNoTypeArguments(==)
|
||||
handleNoArguments(==)
|
||||
handleSend(x, ==)
|
||||
beginBinaryExpression(==)
|
||||
handleLiteralNull(null)
|
||||
endBinaryExpression(==)
|
||||
endSwitchCaseWhenClause(null)
|
||||
endCaseExpression(case, when, :)
|
||||
beginSwitchCase(0, 1, case)
|
||||
handleBreakStatement(false, break, ;)
|
||||
endSwitchCase(0, 1, null, null, 1, case, })
|
||||
endSwitchBlock(1, {, })
|
||||
endSwitchStatement(switch, })
|
||||
endBlockFunctionBody(1, {, })
|
||||
endTopLevelMethod(void, null, })
|
||||
endTopLevelDeclaration()
|
||||
endCompilationUnit(1, )
|
|
@ -0,0 +1,139 @@
|
|||
parseUnit(void)
|
||||
skipErrorTokens(void)
|
||||
listener: beginCompilationUnit(void)
|
||||
syntheticPreviousToken(void)
|
||||
parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
|
||||
parseMetadataStar()
|
||||
listener: beginMetadataStar(void)
|
||||
listener: endMetadataStar(0)
|
||||
parseTopLevelMemberImpl()
|
||||
listener: beginTopLevelMember(void)
|
||||
parseTopLevelMethod(, null, null, , Instance of 'VoidType', null, f, false)
|
||||
listener: beginTopLevelMethod(, null, null)
|
||||
listener: handleVoidKeyword(void)
|
||||
ensureIdentifierPotentiallyRecovered(void, topLevelFunctionDeclaration, false)
|
||||
listener: handleIdentifier(f, topLevelFunctionDeclaration)
|
||||
parseMethodTypeVar(f)
|
||||
listener: handleNoTypeVariables(()
|
||||
parseGetterOrFormalParameters(f, f, false, MemberKind.TopLevelMethod)
|
||||
parseFormalParameters(f, MemberKind.TopLevelMethod)
|
||||
parseFormalParametersRest((, MemberKind.TopLevelMethod)
|
||||
listener: beginFormalParameters((, MemberKind.TopLevelMethod)
|
||||
parseFormalParameter((, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
parseMetadataStar(()
|
||||
listener: beginMetadataStar(x)
|
||||
listener: endMetadataStar(0)
|
||||
listener: beginFormalParameter(x, MemberKind.TopLevelMethod, null, null, null)
|
||||
listener: handleNoType(()
|
||||
ensureIdentifier((, formalParameterDeclaration)
|
||||
listener: handleIdentifier(x, formalParameterDeclaration)
|
||||
listener: handleFormalParameterWithoutValue())
|
||||
listener: endFormalParameter(null, null, null, x, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
|
||||
listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
|
||||
parseAsyncModifierOpt())
|
||||
listener: handleAsyncModifier(null, null)
|
||||
inPlainSync()
|
||||
parseFunctionBody(), false, false)
|
||||
listener: beginBlockFunctionBody({)
|
||||
notEofOrValue(}, switch)
|
||||
parseStatement({)
|
||||
parseStatementX({)
|
||||
parseSwitchStatement({)
|
||||
listener: beginSwitchStatement(switch)
|
||||
ensureParenthesizedCondition(switch, allowCase: false)
|
||||
parseExpressionInParenthesisRest((, allowCase: false)
|
||||
parseExpression(()
|
||||
looksLikeOuterPatternEquals(()
|
||||
skipOuterPattern(()
|
||||
skipObjectPatternRest(x)
|
||||
parsePrecedenceExpression((, 1, true, ConstantPatternContext.none)
|
||||
parseUnaryExpression((, true, ConstantPatternContext.none)
|
||||
parsePrimary((, expression, ConstantPatternContext.none)
|
||||
parseSendOrFunctionLiteral((, expression, ConstantPatternContext.none)
|
||||
parseSend((, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(()
|
||||
ensureIdentifier((, expression)
|
||||
listener: handleIdentifier(x, expression)
|
||||
listener: handleNoTypeArguments())
|
||||
parseArgumentsOpt(x)
|
||||
listener: handleNoArguments())
|
||||
listener: handleSend(x, ))
|
||||
ensureCloseParen(x, ()
|
||||
listener: handleParenthesizedCondition((, null, null)
|
||||
parseSwitchBlock())
|
||||
ensureBlock(), null, switch statement)
|
||||
listener: beginSwitchBlock({)
|
||||
notEofOrValue(}, case)
|
||||
peekPastLabels(case)
|
||||
listener: beginCaseExpression(case)
|
||||
parsePattern(case, PatternContext.matching, precedence: 1)
|
||||
parsePrimaryPattern(case, PatternContext.matching)
|
||||
parseVariablePattern(case, PatternContext.matching, typeInfo: Instance of 'NoType')
|
||||
listener: handleNoType(_)
|
||||
listener: handleWildcardPattern(null, _)
|
||||
listener: beginAsOperatorType(as)
|
||||
computeTypeAfterIsOrAs(as)
|
||||
canParseAsConditional(?)
|
||||
parseExpressionWithoutCascade(?)
|
||||
parsePrecedenceExpression(?, 1, false, ConstantPatternContext.none)
|
||||
parseUnaryExpression(?, false, ConstantPatternContext.none)
|
||||
parsePrimary(?, expression, ConstantPatternContext.none)
|
||||
inPlainSync()
|
||||
parseSendOrFunctionLiteral(?, expression, ConstantPatternContext.none)
|
||||
parseSend(?, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(?)
|
||||
ensureIdentifier(?, expression)
|
||||
inPlainSync()
|
||||
parseArgumentsOpt(when)
|
||||
listener: handleIdentifier(int, typeReference)
|
||||
listener: handleNoTypeArguments(?)
|
||||
listener: handleType(int, ?)
|
||||
listener: endAsOperatorType(as)
|
||||
listener: handleCastPattern(as)
|
||||
listener: beginSwitchCaseWhenClause(when)
|
||||
parseExpression(when)
|
||||
looksLikeOuterPatternEquals(when)
|
||||
skipOuterPattern(when)
|
||||
skipObjectPatternRest(x)
|
||||
parsePrecedenceExpression(when, 1, true, ConstantPatternContext.none)
|
||||
parseUnaryExpression(when, true, ConstantPatternContext.none)
|
||||
parsePrimary(when, expression, ConstantPatternContext.none)
|
||||
parseSendOrFunctionLiteral(when, expression, ConstantPatternContext.none)
|
||||
parseSend(when, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(when)
|
||||
ensureIdentifier(when, expression)
|
||||
listener: handleIdentifier(x, expression)
|
||||
listener: handleNoTypeArguments(==)
|
||||
parseArgumentsOpt(x)
|
||||
listener: handleNoArguments(==)
|
||||
listener: handleSend(x, ==)
|
||||
listener: beginBinaryExpression(==)
|
||||
parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
|
||||
parseUnaryExpression(==, true, ConstantPatternContext.none)
|
||||
parsePrimary(==, expression, ConstantPatternContext.none)
|
||||
parseLiteralNull(==)
|
||||
listener: handleLiteralNull(null)
|
||||
listener: endBinaryExpression(==)
|
||||
listener: endSwitchCaseWhenClause(null)
|
||||
ensureColon(null)
|
||||
listener: endCaseExpression(case, when, :)
|
||||
peekPastLabels(break)
|
||||
parseStatementsInSwitchCase(:, break, case, 0, 1, null, null)
|
||||
listener: beginSwitchCase(0, 1, case)
|
||||
parseStatement(:)
|
||||
parseStatementX(:)
|
||||
parseBreakStatement(:)
|
||||
isBreakAllowed()
|
||||
ensureSemicolon(break)
|
||||
listener: handleBreakStatement(false, break, ;)
|
||||
peekPastLabels(})
|
||||
listener: endSwitchCase(0, 1, null, null, 1, case, })
|
||||
notEofOrValue(}, })
|
||||
listener: endSwitchBlock(1, {, })
|
||||
listener: endSwitchStatement(switch, })
|
||||
notEofOrValue(}, })
|
||||
listener: endBlockFunctionBody(1, {, })
|
||||
listener: endTopLevelMethod(void, null, })
|
||||
listener: endTopLevelDeclaration()
|
||||
reportAllErrorTokens(void)
|
||||
listener: endCompilationUnit(1, )
|
|
@ -0,0 +1,15 @@
|
|||
void f(x) {
|
||||
switch (x) {
|
||||
case _ as int? when x == null:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
case[KeywordToken] _[StringToken] as[KeywordToken] int[StringToken]?[SimpleToken] when[KeywordToken] x[StringToken] ==[SimpleToken] null[KeywordToken]:[SimpleToken]
|
||||
break[KeywordToken];[SimpleToken]
|
||||
}[SimpleToken]
|
||||
}[SimpleToken]
|
||||
[SimpleToken]
|
|
@ -0,0 +1,15 @@
|
|||
void f(x) {
|
||||
switch (x) {
|
||||
case _ as int? when x == null:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
case[KeywordToken] _[StringToken] as[KeywordToken] int[StringToken]?[SimpleToken] when[KeywordToken] x[StringToken] ==[SimpleToken] null[KeywordToken]:[SimpleToken]
|
||||
break[KeywordToken];[SimpleToken]
|
||||
}[SimpleToken]
|
||||
}[SimpleToken]
|
||||
[SimpleToken]
|
48
tests/language/patterns/type_question_before_when_test.dart
Normal file
48
tests/language/patterns/type_question_before_when_test.dart
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// SharedOptions=--enable-experiment=patterns
|
||||
|
||||
// Test that a type followed by `? when` is correctly parsed.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import '../static_type_helper.dart';
|
||||
|
||||
void nullableTypeInsideGuardedCastPattern() {
|
||||
bool matched(Object? x, bool b) {
|
||||
switch (x) {
|
||||
case var y as int? when b:
|
||||
y.expectStaticType<Exactly<int?>>();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Expect.isTrue(matched(null, true));
|
||||
Expect.isTrue(matched(0, true));
|
||||
Expect.isFalse(matched(0, false));
|
||||
Expect.throws<TypeError>(() => matched('', true));
|
||||
}
|
||||
|
||||
void nonNullableTypeInsideAsExpressionInsideConditional() {
|
||||
Object? f(Object? condition, Object? when, Object? otherwise) =>
|
||||
condition as bool ? when : otherwise;
|
||||
Expect.equals('a', f(true, 'a', 'b'));
|
||||
Expect.equals('b', f(false, 'a', 'b'));
|
||||
}
|
||||
|
||||
void nonNullableTypeInsideIsExpressionInsideConditional() {
|
||||
Object? f(Object? obj, Object? when, Object? otherwise) =>
|
||||
obj is int ? when : otherwise;
|
||||
Expect.equals('a', f(0, 'a', 'b'));
|
||||
Expect.equals('b', f('x', 'a', 'b'));
|
||||
}
|
||||
|
||||
main() {
|
||||
nullableTypeInsideGuardedCastPattern();
|
||||
nonNullableTypeInsideAsExpressionInsideConditional();
|
||||
nonNullableTypeInsideIsExpressionInsideConditional();
|
||||
}
|
Loading…
Reference in a new issue