update parser to handle "!." in nnbd expressions

Fix https://github.com/dart-lang/sdk/issues/37111

Change-Id: I6e156a804085c9be12e5e7a0d0019cfeb77c9128
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104187
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
Dan Rubel 2019-05-31 11:37:44 +00:00 committed by commit-bot@chromium.org
parent dc7fa3bae7
commit 2938630937
2 changed files with 86 additions and 89 deletions

View file

@ -2207,19 +2207,24 @@ class FormalParameterParserTest_Fasta extends FastaParserTestCase
*/
@reflectiveTest
class NNBDParserTest_Fasta extends FastaParserTestCase {
@override
CompilationUnit parseCompilationUnit(String content,
{List<ErrorCode> codes,
List<ExpectedError> errors,
FeatureSet featureSet}) =>
super.parseCompilationUnit(content,
codes: codes, errors: errors, featureSet: featureSet ?? nonNullable);
void test_assignment_complex() {
parseCompilationUnit('D? foo(X? x) { X? x1; X? x2 = x + bar(7); }',
featureSet: nonNullable);
parseCompilationUnit('D? foo(X? x) { X? x1; X? x2 = x + bar(7); }');
}
void test_assignment_simple() {
parseCompilationUnit('D? foo(X? x) { X? x1; X? x2 = x; }',
featureSet: nonNullable);
parseCompilationUnit('D? foo(X? x) { X? x1; X? x2 = x; }');
}
void test_binary_expression_statement() {
final unit = parseCompilationUnit('D? foo(X? x) { X ?? x2; }',
featureSet: nonNullable);
final unit = parseCompilationUnit('D? foo(X? x) { X ?? x2; }');
FunctionDeclaration funct = unit.declarations[0];
BlockFunctionBody body = funct.functionExpression.body;
ExpressionStatement statement = body.block.statements[0];
@ -2232,13 +2237,11 @@ class NNBDParserTest_Fasta extends FastaParserTestCase {
}
void test_conditional() {
parseCompilationUnit('D? foo(X? x) { X ? 7 : y; }',
featureSet: nonNullable);
parseCompilationUnit('D? foo(X? x) { X ? 7 : y; }');
}
void test_conditional_complex() {
parseCompilationUnit('D? foo(X? x) { X ? x2 = x + bar(7) : y; }',
featureSet: nonNullable);
parseCompilationUnit('D? foo(X? x) { X ? x2 = x + bar(7) : y; }');
}
void test_conditional_error() {
@ -2247,74 +2250,62 @@ class NNBDParserTest_Fasta extends FastaParserTestCase {
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 19, 1),
expectedError(ParserErrorCode.EXPECTED_TOKEN, 40, 1),
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 40, 1),
],
featureSet: nonNullable);
]);
}
void test_conditional_simple() {
parseCompilationUnit('D? foo(X? x) { X ? x2 = x : y; }',
featureSet: nonNullable);
parseCompilationUnit('D? foo(X? x) { X ? x2 = x : y; }');
}
void test_enableNonNullable_false() {
parseCompilationUnit('main() { x is String? ? (x + y) : z; }',
errors: [expectedError(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 20, 1)]);
errors: [expectedError(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 20, 1)],
featureSet: preNonNullable);
}
void test_for() {
parseCompilationUnit('main() { for(int x = 0; x < 7; ++x) { } }',
featureSet: nonNullable);
parseCompilationUnit('main() { for(int x = 0; x < 7; ++x) { } }');
}
void test_for_conditional() {
parseCompilationUnit('main() { for(x ? y = 7 : y = 8; y < 10; ++y) { } }',
featureSet: nonNullable);
parseCompilationUnit('main() { for(x ? y = 7 : y = 8; y < 10; ++y) { } }');
}
void test_for_nullable() {
parseCompilationUnit('main() { for(int? x = 0; x < 7; ++x) { } }',
featureSet: nonNullable);
parseCompilationUnit('main() { for(int? x = 0; x < 7; ++x) { } }');
}
void test_foreach() {
parseCompilationUnit('main() { for(int x in [7]) { } }',
featureSet: nonNullable);
parseCompilationUnit('main() { for(int x in [7]) { } }');
}
void test_foreach_nullable() {
parseCompilationUnit('main() { for(int? x in [7, null]) { } }',
featureSet: nonNullable);
parseCompilationUnit('main() { for(int? x in [7, null]) { } }');
}
void test_gft_nullable() {
parseCompilationUnit('main() { C? Function() x = 7; }',
featureSet: nonNullable);
parseCompilationUnit('main() { C? Function() x = 7; }');
}
void test_gft_nullable_1() {
parseCompilationUnit('main() { C Function()? x = 7; }',
featureSet: nonNullable);
parseCompilationUnit('main() { C Function()? x = 7; }');
}
void test_gft_nullable_2() {
parseCompilationUnit('main() { C? Function()? x = 7; }',
featureSet: nonNullable);
parseCompilationUnit('main() { C? Function()? x = 7; }');
}
void test_gft_nullable_3() {
parseCompilationUnit('main() { C? Function()? Function()? x = 7; }',
featureSet: nonNullable);
parseCompilationUnit('main() { C? Function()? Function()? x = 7; }');
}
void test_gft_nullable_prefixed() {
parseCompilationUnit('main() { C.a? Function()? x = 7; }',
featureSet: nonNullable);
parseCompilationUnit('main() { C.a? Function()? x = 7; }');
}
void test_is_nullable() {
CompilationUnit unit = parseCompilationUnit(
'main() { x is String? ? (x + y) : z; }',
featureSet: nonNullable);
CompilationUnit unit =
parseCompilationUnit('main() { x is String? ? (x + y) : z; }');
FunctionDeclaration function = unit.declarations[0];
BlockFunctionBody body = function.functionExpression.body;
ExpressionStatement statement = body.block.statements[0];
@ -2329,9 +2320,8 @@ class NNBDParserTest_Fasta extends FastaParserTestCase {
}
void test_is_nullable_parenthesis() {
CompilationUnit unit = parseCompilationUnit(
'main() { (x is String?) ? (x + y) : z; }',
featureSet: nonNullable);
CompilationUnit unit =
parseCompilationUnit('main() { (x is String?) ? (x + y) : z; }');
FunctionDeclaration function = unit.declarations[0];
BlockFunctionBody body = function.functionExpression.body;
ExpressionStatement statement = body.block.statements[0];
@ -2350,9 +2340,7 @@ class NNBDParserTest_Fasta extends FastaParserTestCase {
parseCompilationUnit('''
// @dart = 2.2
main() { (x is String?) ? (x + y) : z; }
''',
errors: [expectedError(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 36, 1)],
featureSet: nonNullable);
''', errors: [expectedError(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 36, 1)]);
}
void test_late_as_identifier() {
@ -2385,12 +2373,11 @@ void f(C c) {
main() {
f(new C());
}
''', featureSet: nonNullable);
''');
}
void test_nullCheck() {
var unit = parseCompilationUnit('f(int? y) { var x = y!; }',
featureSet: nonNullable);
var unit = parseCompilationUnit('f(int? y) { var x = y!; }');
FunctionDeclaration function = unit.declarations[0];
BlockFunctionBody body = function.functionExpression.body;
VariableDeclarationStatement statement = body.block.statements[0];
@ -2414,23 +2401,36 @@ main() {
expect(identifier.name, 'y');
}
void test_nullCheckAfterGetterAccess() {
parseCompilationUnit('f() { var x = g.x!.y + 7; }');
}
void test_nullCheckAfterMethodCall() {
parseCompilationUnit('f() { var x = g.m()!.y + 7; }');
}
void test_nullCheckBeforeGetterAccess() {
parseCompilationUnit('f() { var x = g!.x + 7; }');
}
void test_nullCheckBeforeMethodCall() {
parseCompilationUnit('f() { var x = g!.m() + 7; }');
}
void test_nullCheckFunctionResult() {
parseCompilationUnit('f() { var x = g()! + 7; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = g()! + 7; }');
}
void test_nullCheckIndexedValue() {
parseCompilationUnit('f(int? y) { var x = y[0]! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f(int? y) { var x = y[0]! + 7; }');
}
void test_nullCheckIndexedValue2() {
parseCompilationUnit('f(int? y) { var x = super.y[0]! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f(int? y) { var x = super.y[0]! + 7; }');
}
void test_nullCheckInExpression() {
parseCompilationUnit('f(int? y) { var x = y! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f(int? y) { var x = y! + 7; }');
}
void test_nullCheckInExpression_disabled() {
@ -2443,28 +2443,23 @@ main() {
}
void test_nullCheckMethodResult() {
parseCompilationUnit('f() { var x = g.m()! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f() { var x = g.m()! + 7; }');
}
void test_nullCheckMethodResult2() {
parseCompilationUnit('f() { var x = g?.m()! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f() { var x = g?.m()! + 7; }');
}
void test_nullCheckMethodResult3() {
parseCompilationUnit('f() { var x = super.m()! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f() { var x = super.m()! + 7; }');
}
void test_nullCheckOnConstConstructor() {
parseCompilationUnit('f() { var x = const Foo()!; }',
featureSet: nonNullable);
parseCompilationUnit('f() { var x = const Foo()!; }');
}
void test_nullCheckOnConstructor() {
parseCompilationUnit('f() { var x = new Foo()!; }',
featureSet: nonNullable);
parseCompilationUnit('f() { var x = new Foo()!; }');
}
void test_nullCheckOnLiteral_disabled() {
@ -2475,47 +2470,46 @@ main() {
void test_nullCheckOnLiteralDouble() {
// Issues like this should be caught during later analysis
parseCompilationUnit('f() { var x = 1.2!; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = 1.2!; }');
}
void test_nullCheckOnLiteralInt() {
// Issues like this should be caught during later analysis
parseCompilationUnit('f() { var x = 0!; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = 0!; }');
}
void test_nullCheckOnLiteralList() {
// Issues like this should be caught during later analysis
parseCompilationUnit('f() { var x = [1,2]!; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = [1,2]!; }');
}
void test_nullCheckOnLiteralMap() {
// Issues like this should be caught during later analysis
parseCompilationUnit('f() { var x = {1:2}!; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = {1:2}!; }');
}
void test_nullCheckOnLiteralSet() {
// Issues like this should be caught during later analysis
parseCompilationUnit('f() { var x = {1,2}!; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = {1,2}!; }');
}
void test_nullCheckOnLiteralString() {
// Issues like this should be caught during later analysis
parseCompilationUnit('f() { var x = "seven"!; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = "seven"!; }');
}
void test_nullCheckOnNull() {
// Issues like this should be caught during later analysis
parseCompilationUnit('f() { var x = null!; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = null!; }');
}
void test_nullCheckOnSymbol() {
// Issues like this should be caught during later analysis
parseCompilationUnit('f() { var x = #seven!; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = #seven!; }');
}
void test_nullCheckOnValue() {
parseCompilationUnit('f(Point p) { var x = p.y! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f(Point p) { var x = p.y! + 7; }');
}
void test_nullCheckOnValue_disabled() {
@ -2525,27 +2519,24 @@ main() {
}
void test_nullCheckParenthesizedExpression() {
parseCompilationUnit('f(int? y) { var x = (y)! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f(int? y) { var x = (y)! + 7; }');
}
void test_nullCheckPropertyAccess() {
parseCompilationUnit('f() { var x = g.p! + 7; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = g.p! + 7; }');
}
void test_nullCheckPropertyAccess2() {
parseCompilationUnit('f() { var x = g?.p! + 7; }', featureSet: nonNullable);
parseCompilationUnit('f() { var x = g?.p! + 7; }');
}
void test_nullCheckPropertyAccess3() {
parseCompilationUnit('f() { var x = super.p! + 7; }',
featureSet: nonNullable);
parseCompilationUnit('f() { var x = super.p! + 7; }');
}
void test_postfix_null_assertion_and_unary_prefix_operator_precedence() {
// -x! is parsed as -(x!).
var unit =
parseCompilationUnit('void main() { -x!; }', featureSet: nonNullable);
var unit = parseCompilationUnit('void main() { -x!; }');
var function = unit.declarations[0] as FunctionDeclaration;
var body = function.functionExpression.body as BlockFunctionBody;
var statement = body.block.statements[0] as ExpressionStatement;
@ -2557,8 +2548,7 @@ main() {
void test_postfix_null_assertion_of_postfix_expression() {
// x++! is parsed as (x++)!.
var unit =
parseCompilationUnit('void main() { x++!; }', featureSet: nonNullable);
var unit = parseCompilationUnit('void main() { x++!; }');
var function = unit.declarations[0] as FunctionDeclaration;
var body = function.functionExpression.body as BlockFunctionBody;
var statement = body.block.statements[0] as ExpressionStatement;

View file

@ -3939,7 +3939,7 @@ class Parser {
}
Token next = token.next;
TokenType type = next.type;
int tokenLevel = _computePrecedence(type);
int tokenLevel = _computePrecedence(next);
for (int level = tokenLevel; level >= precedence; --level) {
int lastBinaryExpressionLevel = -1;
while (identical(tokenLevel, level)) {
@ -3988,10 +3988,14 @@ class Parser {
rewriter.replaceTokenFollowing(token, replacement);
replacement.endToken = replacement.next;
token = parseArgumentOrIndexStar(token, noTypeParamOrArg);
} else if (identical(type, TokenType.BANG)) {
listener.handleNonNullAssertExpression(token.next);
token = next;
} else {
// Recovery
reportRecoverableErrorWithToken(
token.next, fasta.templateUnexpectedToken);
token = next;
}
} else if (identical(type, TokenType.IS)) {
token = parseIsOperatorRest(token);
@ -4020,20 +4024,23 @@ class Parser {
}
next = token.next;
type = next.type;
tokenLevel = _computePrecedence(type);
tokenLevel = _computePrecedence(next);
}
}
return token;
}
int _computePrecedence(TokenType type) {
int _computePrecedence(Token token) {
TokenType type = token.type;
if (identical(type, TokenType.BANG)) {
// The '!' has prefix precedence but here it's being used as a
// postfix operator to assert the expression has a non-null value.
if (identical(token.next.type, TokenType.PERIOD)) {
return SELECTOR_PRECEDENCE;
}
return POSTFIX_PRECEDENCE;
} else {
return type.precedence;
}
return type.precedence;
}
Token parseCascadeExpression(Token token) {