blob: 99e9bc67fe12711a6479a2c2b99708a3e41c5bcf [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'recovery_test_support.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ListLiteralTest);
defineReflectiveTests(MapLiteralTest);
defineReflectiveTests(MissingCodeTest);
defineReflectiveTests(ParameterListTest);
defineReflectiveTests(TypedefTest);
});
}
/// Test how well the parser recovers when tokens are missing in a list literal.
@reflectiveTest
class ListLiteralTest extends AbstractRecoveryTest {
void test_extraComma() {
testRecovery(
'''
f() => [a, , b];
''',
[ParserErrorCode.missingIdentifier],
'''
f() => [a, _s_, b];
''',
);
}
void test_missingComma() {
testRecovery(
'''
f() => [a, b c];
''',
[ParserErrorCode.expectedToken],
'''
f() => [a, b, c];
''',
);
}
void test_missingComma_afterIf() {
testRecovery(
'''
f() => [a, if (x) b c];
''',
[ParserErrorCode.expectedElseOrComma],
'''
f() => [a, if (x) b, c];
''',
);
}
void test_missingComma_afterIfElse() {
testRecovery(
'''
f() => [a, if (x) b else y c];
''',
[ParserErrorCode.expectedToken],
'''
f() => [a, if (x) b else y, c];
''',
);
}
}
/// Test how well the parser recovers when tokens are missing in a map literal.
@reflectiveTest
class MapLiteralTest extends AbstractRecoveryTest {
void test_missingComma() {
testRecovery(
'''
f() => {a: b, c: d e: f};
''',
[ParserErrorCode.expectedToken],
'''
f() => {a: b, c: d, e: f};
''',
);
}
void test_missingComma_afterIf() {
testRecovery(
'''
f() => {a: b, if (x) c: d e: f};
''',
[ParserErrorCode.expectedElseOrComma],
'''
f() => {a: b, if (x) c: d, e: f};
''',
);
}
void test_missingComma_afterIfElse() {
testRecovery(
'''
f() => {a: b, if (x) c: d else y: z e: f};
''',
[ParserErrorCode.expectedToken],
'''
f() => {a: b, if (x) c: d else y: z, e: f};
''',
);
}
void test_missingKey() {
testRecovery(
'''
f() => {: b};
''',
[ParserErrorCode.missingIdentifier],
'''
f() => {_s_: b};
''',
);
}
void test_missingValue_last() {
testRecovery(
'''
f() => {a: };
''',
[ParserErrorCode.missingIdentifier],
'''
f() => {a: _s_};
''',
);
}
void test_missingValue_notLast() {
testRecovery(
'''
f() => {a: , b: c};
''',
[ParserErrorCode.missingIdentifier],
'''
f() => {a: _s_, b: c};
''',
);
}
}
/// Test how well the parser recovers when non-paired tokens are missing.
@reflectiveTest
class MissingCodeTest extends AbstractRecoveryTest {
void test_ampersand() {
testBinaryExpression('&');
}
void test_ampersand_super() {
testUserDefinableOperatorWithSuper('&');
}
@failingTest
void test_asExpression_missingLeft() {
testRecovery(
'''
convert(x) => as T;
''',
[ParserErrorCode.expectedTypeName],
'''
convert(x) => _s_ as T;
''',
);
}
void test_asExpression_missingRight() {
testRecovery(
'''
convert(x) => x as ;
''',
[ParserErrorCode.expectedTypeName],
'''
convert(x) => x as _s_;
''',
);
}
void test_assignmentExpression() {
testRecovery(
'''
f() {
var x;
x =
}
''',
[ParserErrorCode.missingIdentifier, ParserErrorCode.expectedToken],
'''
f() {
var x;
x = _s_;
}
''',
);
}
void test_bar() {
testBinaryExpression('|');
}
void test_bar_super() {
testUserDefinableOperatorWithSuper('|');
}
void test_cascade_missingRight() {
testRecovery(
'''
f(x) {
x..
}
''',
[ParserErrorCode.missingIdentifier, ParserErrorCode.expectedToken],
'''
f(x) {
x.. _s_;
}
''',
);
}
void test_classDeclaration_missingName() {
testRecovery(
'''
class {}
''',
[ParserErrorCode.missingIdentifier],
'''
class _s_ {}
''',
);
}
@failingTest
void test_combinatorsBeforePrefix() {
//Expected 1 errors of type ParserErrorCode.MISSING_PREFIX_IN_DEFERRED_IMPORT, found 0
testRecovery(
'''
import 'bar.dart' deferred;
''',
[ParserErrorCode.missingPrefixInDeferredImport],
'''
import 'bar.dart' deferred as _s_;
''',
);
}
void test_comma_missing() {
testRecovery(
'''
f(int a int b) { }
''',
[ParserErrorCode.expectedToken],
'''
f(int a, int b) { }
''',
);
}
void test_conditionalExpression_else() {
testRecovery(
'''
f() => x ? y :
''',
[ParserErrorCode.missingIdentifier, ParserErrorCode.expectedToken],
'''
f() => x ? y : _s_;
''',
);
}
void test_conditionalExpression_then() {
testRecovery(
'''
f() => x ? : z
''',
[ParserErrorCode.missingIdentifier, ParserErrorCode.expectedToken],
'''
f() => x ? _s_ : z;
''',
);
}
void test_equalEqual() {
testBinaryExpression('==');
}
void test_equalEqual_super() {
testUserDefinableOperatorWithSuper('==');
}
void test_expressionBody_missingGt() {
testRecovery(
'''
f(x) = x;
''',
[ParserErrorCode.missingFunctionBody],
'''
f(x) => x;
''',
);
}
void test_expressionBody_return() {
testRecovery(
'''
f(x) return x;
''',
[ParserErrorCode.missingFunctionBody],
'''
f(x) => x;
''',
);
}
void test_greaterThan() {
testBinaryExpression('>');
}
void test_greaterThan_super() {
testUserDefinableOperatorWithSuper('>');
}
void test_greaterThanGreaterThan() {
testBinaryExpression('>>');
}
void test_greaterThanGreaterThan_super() {
testUserDefinableOperatorWithSuper('>>');
}
void test_greaterThanOrEqual() {
testBinaryExpression('>=');
}
void test_greaterThanOrEqual_super() {
testUserDefinableOperatorWithSuper('>=');
}
void test_hat() {
testBinaryExpression('^');
}
void test_hat_super() {
testUserDefinableOperatorWithSuper('^');
}
void test_initializerList_missingComma_assert() {
// https://github.com/dart-lang/sdk/issues/33241
testRecovery(
'''
class Test {
Test()
: assert(true)
assert(true);
}
''',
[ParserErrorCode.expectedToken],
'''
class Test {
Test()
: assert(true),
assert(true);
}
''',
);
}
void test_initializerList_missingComma_field() {
// https://github.com/dart-lang/sdk/issues/33241
testRecovery(
'''
class Test {
Test()
: assert(true)
x = 2;
}
''',
[ParserErrorCode.expectedToken],
'''
class Test {
Test()
: assert(true),
x = 2;
}
''',
);
}
void test_initializerList_missingComma_thisField() {
// https://github.com/dart-lang/sdk/issues/33241
testRecovery(
'''
class Test {
Test()
: assert(true)
this.x = 2;
}
''',
[ParserErrorCode.expectedToken],
'''
class Test {
Test()
: assert(true),
this.x = 2;
}
''',
);
}
void test_isExpression_missingLeft() {
testRecovery(
'''
f() {
if (is String) {
}
}
''',
[ParserErrorCode.missingIdentifier],
'''
f() {
if (_s_ is String) {
}
}
''',
);
}
void test_isExpression_missingRight() {
testRecovery(
'''
f(x) {
if (x is ) {}
}
''',
[ParserErrorCode.expectedTypeName],
'''
f(x) {
if (x is _s_) {}
}
''',
);
}
void test_lessThan() {
testBinaryExpression('<');
}
void test_lessThan_super() {
testUserDefinableOperatorWithSuper('<');
}
void test_lessThanLessThan() {
testBinaryExpression('<<');
}
void test_lessThanLessThan_super() {
testUserDefinableOperatorWithSuper('<<');
}
void test_lessThanOrEqual() {
testBinaryExpression('<=');
}
void test_lessThanOrEqual_super() {
testUserDefinableOperatorWithSuper('<=');
}
void test_minus() {
testBinaryExpression('-');
}
void test_minus_super() {
testUserDefinableOperatorWithSuper('-');
}
@failingTest
void test_missingGet() {
testRecovery(
'''
class Bar {
int foo => 0;
}
''',
[ParserErrorCode.missingGet],
'''
class Bar {
int get foo => 0;
}
''',
);
}
@failingTest
void test_parameterList_leftParen() {
// https://github.com/dart-lang/sdk/issues/22938
testRecovery(
'''
int f int x, int y) {}
''',
[ParserErrorCode.expectedToken],
'''
int f (int x, int y) {}
''',
);
}
@failingTest
void test_parentheses_aroundThrow() {
// https://github.com/dart-lang/sdk/issues/24892
testRecovery(
'''
f(x) => x ?? throw 0;
''',
[ParserErrorCode.expectedToken, ParserErrorCode.expectedToken],
'''
f(x) => x ?? (throw 0);
''',
);
}
void test_percent() {
testBinaryExpression('%');
}
void test_percent_super() {
testUserDefinableOperatorWithSuper('%');
}
void test_plus() {
testBinaryExpression('+');
}
void test_plus_super() {
testUserDefinableOperatorWithSuper('+');
}
void test_prefixedIdentifier() {
testRecovery(
'''
f() {
var v = 'String';
v.
}
''',
[ParserErrorCode.missingIdentifier, ParserErrorCode.expectedToken],
'''
f() {
var v = 'String';
v._s_;
}
''',
);
}
void test_slash() {
testBinaryExpression('/');
}
void test_slash_super() {
testUserDefinableOperatorWithSuper('/');
}
void test_star() {
testBinaryExpression('*');
}
void test_star_super() {
testUserDefinableOperatorWithSuper('*');
}
void test_stringInterpolation_unclosed() {
// https://github.com/dart-lang/sdk/issues/946
// TODO(brianwilkerson): Try to recover better. Ideally there would be a
// single error about an unterminated interpolation block.
// https://github.com/dart-lang/sdk/issues/36101
// TODO(danrubel): improve recovery so that the scanner/parser associates
// `${` with a synthetic `}` inside the " " rather than the `}` at the end.
testRecovery(
r'''
f() {
print("${42");
}
''',
[
ParserErrorCode.expectedToken,
ParserErrorCode.expectedToken,
ScannerErrorCode.expectedToken,
ScannerErrorCode.expectedToken,
ScannerErrorCode.unterminatedStringLiteral,
ScannerErrorCode.unterminatedStringLiteral,
],
r'''
f() {
print("${42}");
}
''',
);
}
void test_tildeSlash() {
testBinaryExpression('~/');
}
void test_tildeSlash_super() {
testUserDefinableOperatorWithSuper('~/');
}
void testBinaryExpression(String operator) {
testRecovery(
'''
f() => x $operator
''',
[ParserErrorCode.missingIdentifier, ParserErrorCode.expectedToken],
'''
f() => x $operator _s_;
''',
);
}
void testUserDefinableOperatorWithSuper(String operator) {
testRecovery(
'''
class C {
int operator $operator(x) => super $operator
}
''',
[ParserErrorCode.missingIdentifier, ParserErrorCode.expectedToken],
'''
class C {
int operator $operator(x) => super $operator _s_;
}
''',
);
}
}
/// Test how well the parser recovers when tokens are missing in a parameter
/// list.
@reflectiveTest
class ParameterListTest extends AbstractRecoveryTest {
@failingTest
void test_extraComma_named_last() {
testRecovery(
'''
f({a, }) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f({a, _s_}) {}
''',
);
}
void test_extraComma_named_noLast() {
testRecovery(
'''
f({a, , b}) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f({a, _s_, b}) {}
''',
);
}
@failingTest
void test_extraComma_positional_last() {
testRecovery(
'''
f([a, ]) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f([a, _s_]) {}
''',
);
}
void test_extraComma_positional_noLast() {
testRecovery(
'''
f([a, , b]) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f([a, _s_, b]) {}
''',
);
}
@failingTest
void test_extraComma_required_last() {
testRecovery(
'''
f(a, ) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f(a, _s_) {}
''',
);
}
void test_extraComma_required_noLast() {
testRecovery(
'''
f(a, , b) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f(a, _s_, b) {}
''',
);
}
void test_fieldFormalParameter_noPeriod_last() {
testRecovery(
'''
class C {
int f;
C(this);
}
''',
[ParserErrorCode.expectedIdentifierButGotKeyword],
'''
class C {
int f;
C(_k_);
}
''',
);
}
void test_fieldFormalParameter_noPeriod_notLast() {
testRecovery(
'''
class C {
int f;
C(this, p);
}
''',
[ParserErrorCode.expectedIdentifierButGotKeyword],
'''
class C {
int f;
C(_k_, p);
}
''',
);
}
void test_fieldFormalParameter_period_last() {
testRecovery(
'''
class C {
int f;
C(this.);
}
''',
[ParserErrorCode.missingIdentifier],
'''
class C {
int f;
C(this._s_);
}
''',
);
}
void test_fieldFormalParameter_period_notLast() {
testRecovery(
'''
class C {
int f;
C(this., p);
}
''',
[ParserErrorCode.missingIdentifier],
'''
class C {
int f;
C(this._s_, p);
}
''',
);
}
void test_incorrectlyTerminatedGroup_named_none() {
testRecovery(
'''
f({a: 0) {}
''',
[ScannerErrorCode.expectedToken],
'''
f({a: 0}) {}
''',
);
}
void test_incorrectlyTerminatedGroup_named_positional() {
testRecovery(
'''
f({a: 0]) {}
''',
[ScannerErrorCode.expectedToken, ParserErrorCode.expectedToken],
'''
f({a: 0}) {}
''',
);
}
void test_incorrectlyTerminatedGroup_none_named() {
testRecovery(
'''
f(a}) {}
''',
[ParserErrorCode.expectedToken],
'''
f(a) {}
''',
);
}
void test_incorrectlyTerminatedGroup_none_positional() {
testRecovery(
'''
f(a]) {}
''',
[ParserErrorCode.expectedToken],
'''
f(a) {}
''',
);
}
void test_incorrectlyTerminatedGroup_positional_named() {
testRecovery(
'''
f([a = 0}) {}
''',
[ScannerErrorCode.expectedToken, ParserErrorCode.expectedToken],
'''
f([a = 0]) {}
''',
);
}
void test_incorrectlyTerminatedGroup_positional_none() {
// Maybe put in paired_tokens_test.dart.
testRecovery(
'''
f([a = 0) {}
''',
[ScannerErrorCode.expectedToken],
'''
f([a = 0]) {}
''',
);
}
void test_missingComma() {
// https://github.com/dart-lang/sdk/issues/22074
testRecovery(
'''
g(a, b, c) {}
h(v1, v2, v) {
g(v1 == v2 || v1 == v 3, true);
}
''',
[ParserErrorCode.expectedToken],
'''
g(a, b, c) {}
h(v1, v2, v) {
g(v1 == v2 || v1 == v, 3, true);
}
''',
);
}
void test_missingDefault_named_last() {
testRecovery(
'''
f({a: }) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f({a: _s_}) {}
''',
);
}
void test_missingDefault_named_notLast() {
testRecovery(
'''
f({a: , b}) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f({a: _s_, b}) {}
''',
);
}
void test_missingDefault_positional_last() {
testRecovery(
'''
f([a = ]) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f([a = _s_]) {}
''',
);
}
void test_missingDefault_positional_notLast() {
testRecovery(
'''
f([a = , b]) {}
''',
[ParserErrorCode.missingIdentifier],
'''
f([a = _s_, b]) {}
''',
);
}
void test_multipleGroups_mixed() {
// TODO(brianwilkerson): Figure out the best way to recover from this.
testRecovery(
'''
f([a = 0], {b: 1}) {}
''',
[ParserErrorCode.expectedToken],
'''
f([a = 0]) {}
''',
);
}
@failingTest
void test_multipleGroups_mixedAndMultiple() {
// TODO(brianwilkerson): Figure out the best way to recover from this.
testRecovery(
'''
f([a = 0], {b: 1}, [c = 2]) {}
''',
[ParserErrorCode.mixedParameterGroups],
'''
f([a = 0, c = 2]) {}
''',
);
}
@failingTest
void test_multipleGroups_named() {
testRecovery(
'''
f({a: 0}, {b: 1}) {}
''',
[ParserErrorCode.multipleNamedParameterGroups],
'''
f({a: 0, b: 1}) {}
''',
);
}
@failingTest
void test_multipleGroups_positional() {
testRecovery(
'''
f([a = 0], [b = 1]) {}
''',
[ParserErrorCode.multiplePositionalParameterGroups],
'''
f([a = 0, b = 1]) {}
''',
);
}
@failingTest
void test_namedOutsideGroup() {
testRecovery(
'''
f(a: 0) {}
''',
[ParserErrorCode.namedParameterOutsideGroup],
'''
f({a: 0}) {}
''',
);
}
@failingTest
void test_positionalOutsideGroup() {
testRecovery(
'''
f(a = 0) {}
''',
[ParserErrorCode.positionalParameterOutsideGroup],
'''
f([a = 0]) {}
''',
);
}
}
/// Test how well the parser recovers when tokens are missing in a typedef.
@reflectiveTest
class TypedefTest extends AbstractRecoveryTest {
@failingTest
void test_missingFunction() {
testRecovery(
'''
typedef Predicate = bool <E>(E element);
''',
[ParserErrorCode.missingIdentifier],
'''
typedef Predicate = bool Function<E>(E element);
''',
);
}
}