blob: e189d2cc1657670a89eefe9e33bd1e6b76c87c89 [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/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'recovery_test_support.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(AnnotationTest);
defineReflectiveTests(MiscellaneousTest);
defineReflectiveTests(ModifiersTest);
defineReflectiveTests(MultipleTypeTest);
defineReflectiveTests(PunctuationTest);
defineReflectiveTests(VarianceModifierTest);
});
}
/**
* Test how well the parser recovers when annotations are included in places
* where they are not allowed.
*/
@reflectiveTest
class AnnotationTest extends AbstractRecoveryTest {
void test_typeArgument() {
testRecovery('''
const annotation = null;
class A<E> {}
class C {
m() => new A<@annotation C>();
}
''', [ParserErrorCode.UNEXPECTED_TOKEN], '''
const annotation = null;
class A<E> {}
class C {
m() => new A<C>();
}
''');
}
}
/**
* Test how well the parser recovers in other cases.
*/
@reflectiveTest
class MiscellaneousTest extends AbstractRecoveryTest {
void test_classTypeAlias_withBody() {
testRecovery('''
class B = Object with A {}
''',
// TODO(danrubel): Consolidate and improve error message.
[ParserErrorCode.EXPECTED_EXECUTABLE, ParserErrorCode.EXPECTED_TOKEN],
'''
class B = Object with A;
''');
}
void test_getter_parameters() {
var content = '''
int get g(x) => 0;
''';
var unit = parseCompilationUnit(content,
codes: [ParserErrorCode.GETTER_WITH_PARAMETERS]);
validateTokenStream(unit.beginToken);
FunctionDeclaration g = unit.declarations.first;
var parameters = g.functionExpression.parameters;
expect(parameters, isNotNull);
expect(parameters.parameters, hasLength(1));
}
@failingTest
void test_identifier_afterNamedArgument() {
// https://github.com/dart-lang/sdk/issues/30370
testRecovery('''
a() {
b(c: c(d: d(e: null f,),),);
}
''', [], '''
a() {
b(c: c(d: d(e: null,),),);
}
''');
}
void test_invalidRangeCheck() {
parseCompilationUnit('''
f(x) {
while (1 < x < 3) {}
}
''', codes: [ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND]);
}
@failingTest
void test_listLiteralType() {
// https://github.com/dart-lang/sdk/issues/4348
testRecovery('''
List<int> ints = List<int>[];
''', [], '''
List<int> ints = <int>[];
''');
}
@failingTest
void test_mapLiteralType() {
// https://github.com/dart-lang/sdk/issues/4348
testRecovery('''
Map<int, int> map = Map<int, int>{};
''', [], '''
Map<int, int> map = <int, int>{};
''');
}
void test_multipleRedirectingInitializers() {
testRecovery('''
class A {
A() : this.a(), this.b();
A.a() {}
A.b() {}
}
''', [], '''
class A {
A() : this.a(), this.b();
A.a() {}
A.b() {}
}
''');
}
@failingTest
void test_parenInMapLiteral() {
// https://github.com/dart-lang/sdk/issues/12100
testRecovery('''
class C {}
final Map v = {
'a': () => new C(),
'b': () => new C()),
'c': () => new C(),
};
''', [ParserErrorCode.UNEXPECTED_TOKEN], '''
class C {}
final Map v = {
'a': () => new C(),
'b': () => new C(),
'c': () => new C(),
};
''');
}
}
/**
* Test how well the parser recovers when extra modifiers are provided.
*/
@reflectiveTest
class ModifiersTest extends AbstractRecoveryTest {
@failingTest
void test_classDeclaration_static() {
// TODO(danrubel): Fails because compilation unit begin token is `static`
// even after recovery.
testRecovery('''
static class A {}
''', [ParserErrorCode.EXTRANEOUS_MODIFIER], '''
class A {}
''');
}
void test_methodDeclaration_const_getter() {
testRecovery('''
main() {}
const int get foo => 499;
''', [ParserErrorCode.EXTRANEOUS_MODIFIER], '''
main() {}
int get foo => 499;
''');
}
void test_methodDeclaration_const_method() {
testRecovery('''
main() {}
const int foo() => 499;
''', [ParserErrorCode.EXTRANEOUS_MODIFIER], '''
main() {}
int foo() => 499;
''');
}
void test_methodDeclaration_const_setter() {
testRecovery('''
main() {}
const set foo(v) => 499;
''', [ParserErrorCode.EXTRANEOUS_MODIFIER], '''
main() {}
set foo(v) => 499;
''');
}
}
/**
* Test how well the parser recovers when multiple type annotations are
* provided.
*/
@reflectiveTest
class MultipleTypeTest extends AbstractRecoveryTest {
@failingTest
void test_topLevelVariable() {
// https://github.com/dart-lang/sdk/issues/25875
// Recovers with 'void bar() {}', which seems wrong. Seems like we should
// keep the first type, not the second.
testRecovery('''
String void bar() { }
''', [ParserErrorCode.UNEXPECTED_TOKEN], '''
String bar() { }
''');
}
}
/**
* Test how well the parser recovers when there is extra punctuation.
*/
@reflectiveTest
class PunctuationTest extends AbstractRecoveryTest {
@failingTest
void test_extraComma_extendsClause() {
// https://github.com/dart-lang/sdk/issues/22313
testRecovery('''
class A { }
class B { }
class Foo extends A, B {
Foo() { }
}
''', [ParserErrorCode.UNEXPECTED_TOKEN, ParserErrorCode.UNEXPECTED_TOKEN], '''
class A { }
class B { }
class Foo extends A {
Foo() { }
}
''');
}
void test_extraSemicolon_afterLastClassMember() {
testRecovery('''
class C {
foo() {};
}
''', [ParserErrorCode.EXPECTED_CLASS_MEMBER], '''
class C {
foo() {}
}
''');
}
void test_extraSemicolon_afterLastTopLevelMember() {
testRecovery('''
foo() {};
''', [ParserErrorCode.UNEXPECTED_TOKEN], '''
foo() {}
''');
}
void test_extraSemicolon_beforeFirstClassMember() {
testRecovery('''
class C {
;foo() {}
}
''', [ParserErrorCode.EXPECTED_CLASS_MEMBER], '''
class C {
foo() {}
}
''');
}
@failingTest
void test_extraSemicolon_beforeFirstTopLevelMember() {
// This test fails because the beginning token for the invalid unit is the
// semicolon, despite the fact that it was skipped.
testRecovery('''
;foo() {}
''', [ParserErrorCode.EXPECTED_EXECUTABLE], '''
foo() {}
''');
}
void test_extraSemicolon_betweenClassMembers() {
testRecovery('''
class C {
foo() {};
bar() {}
}
''', [ParserErrorCode.EXPECTED_CLASS_MEMBER], '''
class C {
foo() {}
bar() {}
}
''');
}
void test_extraSemicolon_betweenTopLevelMembers() {
testRecovery('''
foo() {};
bar() {}
''', [ParserErrorCode.UNEXPECTED_TOKEN], '''
foo() {}
bar() {}
''');
}
}
/**
* Test how well the parser recovers when there is extra variance modifiers.
*/
@reflectiveTest
class VarianceModifierTest extends AbstractRecoveryTest {
void test_extraModifier_inClass() {
testRecovery('''
class A<in out X> {}
''', [ParserErrorCode.MULTIPLE_VARIANCE_MODIFIERS], '''
class A<in X> {}
''',
featureSet: FeatureSet.forTesting(
sdkVersion: '2.5.0',
additionalFeatures: [Feature.variance],
));
}
}