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:
Paul Berry 2023-03-31 13:12:48 +00:00 committed by Commit Queue
parent e6d352dec1
commit 452dcdf517
13 changed files with 564 additions and 4 deletions

View file

@ -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;

View file

@ -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) {

View file

@ -0,0 +1 @@
void f(condition, when, otherwise) => condition as bool ? when : otherwise;

View file

@ -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, )

View file

@ -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, )

View file

@ -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]

View file

@ -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]

View file

@ -0,0 +1,6 @@
void f(x) {
switch (x) {
case _ as int? when x == null:
break;
}
}

View file

@ -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, )

View file

@ -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, )

View file

@ -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]

View file

@ -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]

View 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();
}