blob: a78fe94bde6173041d9dffd9e3a21eb161d99cfb [file]
// 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:test_reflective_loader/test_reflective_loader.dart';
import '../../dart/resolution/node_text_expectations.dart';
import '../../diagnostics/parser_diagnostics.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(AnnotationTest);
defineReflectiveTests(MiscellaneousTest);
defineReflectiveTests(ModifiersTest);
defineReflectiveTests(MultipleTypeTest);
defineReflectiveTests(PunctuationTest);
defineReflectiveTests(VarianceModifierTest);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
/// Test how well the parser recovers when annotations are included in places
/// where they are not allowed.
@reflectiveTest
class AnnotationTest extends ParserDiagnosticsTest {
void test_typeArgument() {
var parseResult = parseTestCodeWithDiagnostics(r'''
const annotation = null;
class A<E> {}
class C {
m() => new A<@annotation C>();
// ^^^^^^^^^^^
// [diag.annotationOnTypeArgument] Type arguments can't have annotations because they aren't declarations.
}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
TopLevelVariableDeclaration
variables: VariableDeclarationList
keyword: const
variables
VariableDeclaration
name: annotation
equals: =
initializer: NullLiteral
literal: null
semicolon: ;
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: A
typeParameters: TypeParameterList
leftBracket: <
typeParameters
TypeParameter
name: E
rightBracket: >
body: BlockClassBody
leftBracket: {
rightBracket: }
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: C
body: BlockClassBody
leftBracket: {
members
MethodDeclaration
name: m
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: ExpressionFunctionBody
functionDefinition: =>
expression: InstanceCreationExpression
keyword: new
constructorName: ConstructorName
type: NamedType
name: A
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: C
rightBracket: >
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
semicolon: ;
rightBracket: }
''');
}
}
/// Test how well the parser recovers in other cases.
@reflectiveTest
class MiscellaneousTest extends ParserDiagnosticsTest {
void test_classTypeAlias_withBody() {
var parseResult = parseTestCodeWithDiagnostics(r'''
class B = Object with A {}
// ^
// [diag.expectedToken] Expected to find ';'.
// ^
// [diag.expectedExecutable] Expected a method, getter, setter or operator declaration.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassTypeAlias
typedefKeyword: class
name: B
equals: =
superclass: NamedType
name: Object
withClause: WithClause
withKeyword: with
mixinTypes
NamedType
name: A
semicolon: ; <synthetic>
''');
}
void test_getter_parameters() {
var parseResult = parseTestCodeWithDiagnostics(r'''
int get g(x) => 0;
// ^
// [diag.getterWithParameters] Getters must be declared without a parameter list.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
returnType: NamedType
name: int
propertyKeyword: get
name: g
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
parameter: RegularFormalParameter
name: x
rightParenthesis: )
body: ExpressionFunctionBody
functionDefinition: =>
expression: IntegerLiteral
literal: 0
semicolon: ;
''');
}
void test_identifier_afterNamedArgument() {
var parseResult = parseTestCodeWithDiagnostics(r'''
a() {
b(c: c(d: d(e: null f,),),);
// ^
// [diag.expectedToken] Expected to find ','.
}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
name: a
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
statements
ExpressionStatement
expression: MethodInvocation
methodName: SimpleIdentifier
token: b
argumentList: ArgumentList
leftParenthesis: (
arguments
NamedArgument
name: c
colon: :
argumentExpression: MethodInvocation
methodName: SimpleIdentifier
token: c
argumentList: ArgumentList
leftParenthesis: (
arguments
NamedArgument
name: d
colon: :
argumentExpression: MethodInvocation
methodName: SimpleIdentifier
token: d
argumentList: ArgumentList
leftParenthesis: (
arguments
NamedArgument
name: e
colon: :
argumentExpression: NullLiteral
literal: null
SimpleIdentifier
token: f
rightParenthesis: )
rightParenthesis: )
rightParenthesis: )
semicolon: ;
rightBracket: }
''');
}
void test_invalidRangeCheck() {
var parseResult = parseTestCodeWithDiagnostics(r'''
f(x) {
while (1 < x < 3) {}
// ^
// [diag.equalityCannotBeEqualityOperand] A comparison expression can't be an operand of another comparison expression.
}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
name: f
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
parameter: RegularFormalParameter
name: x
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
statements
WhileStatement
whileKeyword: while
leftParenthesis: (
condition: BinaryExpression
leftOperand: BinaryExpression
leftOperand: IntegerLiteral
literal: 1
operator: <
rightOperand: SimpleIdentifier
token: x
operator: <
rightOperand: IntegerLiteral
literal: 3
rightParenthesis: )
body: Block
leftBracket: {
rightBracket: }
rightBracket: }
''');
}
void test_listLiteralType() {
var parseResult = parseTestCodeWithDiagnostics(r'''
List<int> ints = List<int>[];
// ^^^^
// [diag.literalWithClass] A list literal can't be prefixed by 'List'.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
TopLevelVariableDeclaration
variables: VariableDeclarationList
type: NamedType
name: List
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
rightBracket: >
variables
VariableDeclaration
name: ints
equals: =
initializer: ListLiteral
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
rightBracket: >
leftBracket: [
rightBracket: ]
semicolon: ;
''');
}
void test_mapLiteralType() {
var parseResult = parseTestCodeWithDiagnostics(r'''
Map<int, int> map = Map<int, int>{};
// ^^^
// [diag.literalWithClass] A map literal can't be prefixed by 'Map'.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
TopLevelVariableDeclaration
variables: VariableDeclarationList
type: NamedType
name: Map
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
NamedType
name: int
rightBracket: >
variables
VariableDeclaration
name: map
equals: =
initializer: SetOrMapLiteral
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
NamedType
name: int
rightBracket: >
leftBracket: {
rightBracket: }
isMap: false
semicolon: ;
''');
}
void test_mixin_using_with_clause() {
var parseResult = parseTestCodeWithDiagnostics(r'''
mixin M {}
mixin N with M {}
// ^^^^
// [diag.mixinWithClause] A mixin can't have a with clause.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
MixinDeclaration
mixinKeyword: mixin
name: M
body: BlockClassBody
leftBracket: {
rightBracket: }
MixinDeclaration
mixinKeyword: mixin
name: N
body: BlockClassBody
leftBracket: {
rightBracket: }
''');
}
void test_multipleRedirectingInitializers() {
var parseResult = parseTestCodeWithDiagnostics(r'''
class A {
A() : this.a(), this.b();
A.a() {}
A.b() {}
}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: A
body: BlockClassBody
leftBracket: {
members
ConstructorDeclaration
typeName: SimpleIdentifier
token: A
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
separator: :
initializers
RedirectingConstructorInvocation
thisKeyword: this
period: .
constructorName: SimpleIdentifier
token: a
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
RedirectingConstructorInvocation
thisKeyword: this
period: .
constructorName: SimpleIdentifier
token: b
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
body: EmptyFunctionBody
semicolon: ;
ConstructorDeclaration
typeName: SimpleIdentifier
token: A
period: .
name: a
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
ConstructorDeclaration
typeName: SimpleIdentifier
token: A
period: .
name: b
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
rightBracket: }
''');
}
void test_parenInMapLiteral() {
var parseResult = parseTestCodeWithDiagnostics(r'''
class C {}
final Map v = {
'a': () => new C(),
'b': () => new C()),
// ^
// [diag.expectedToken] Expected to find '}'.
'c': () => new C(),
};
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: C
body: BlockClassBody
leftBracket: {
rightBracket: }
TopLevelVariableDeclaration
variables: VariableDeclarationList
keyword: final
type: NamedType
name: Map
variables
VariableDeclaration
name: v
equals: =
initializer: SetOrMapLiteral
leftBracket: {
elements
MapLiteralEntry
key: SimpleStringLiteral
literal: 'a'
separator: :
value: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: ExpressionFunctionBody
functionDefinition: =>
expression: InstanceCreationExpression
keyword: new
constructorName: ConstructorName
type: NamedType
name: C
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
MapLiteralEntry
key: SimpleStringLiteral
literal: 'b'
separator: :
value: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: ExpressionFunctionBody
functionDefinition: =>
expression: InstanceCreationExpression
keyword: new
constructorName: ConstructorName
type: NamedType
name: C
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
rightBracket: }
isMap: false
semicolon: ;
''');
}
}
/// Test how well the parser recovers when extra modifiers are provided.
@reflectiveTest
class ModifiersTest extends ParserDiagnosticsTest {
void test_classDeclaration_static() {
var parseResult = parseTestCodeWithDiagnostics(r'''
static class A {}
// [diag.extraneousModifier][column 1][length 6] Can't have modifier 'static' here.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: A
body: BlockClassBody
leftBracket: {
rightBracket: }
''');
}
void test_methodDeclaration_const_getter() {
var parseResult = parseTestCodeWithDiagnostics(r'''
main() {}
const int get foo => 499;
// [diag.extraneousModifier][column 1][length 5] Can't have modifier 'const' here.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
name: main
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
FunctionDeclaration
returnType: NamedType
name: int
propertyKeyword: get
name: foo
functionExpression: FunctionExpression
body: ExpressionFunctionBody
functionDefinition: =>
expression: IntegerLiteral
literal: 499
semicolon: ;
''');
}
void test_methodDeclaration_const_method() {
var parseResult = parseTestCodeWithDiagnostics(r'''
main() {}
const int foo() => 499;
// [diag.extraneousModifier][column 1][length 5] Can't have modifier 'const' here.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
name: main
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
FunctionDeclaration
returnType: NamedType
name: int
name: foo
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: ExpressionFunctionBody
functionDefinition: =>
expression: IntegerLiteral
literal: 499
semicolon: ;
''');
}
void test_methodDeclaration_const_setter() {
var parseResult = parseTestCodeWithDiagnostics(r'''
main() {}
const set foo(v) => 499;
// [diag.extraneousModifier][column 1][length 5] Can't have modifier 'const' here.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
name: main
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
FunctionDeclaration
propertyKeyword: set
name: foo
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
parameter: RegularFormalParameter
name: v
rightParenthesis: )
body: ExpressionFunctionBody
functionDefinition: =>
expression: IntegerLiteral
literal: 499
semicolon: ;
''');
}
}
/// Test how well the parser recovers when multiple type annotations are
/// provided.
@reflectiveTest
class MultipleTypeTest extends ParserDiagnosticsTest {
void test_topLevelVariable() {
var parseResult = parseTestCodeWithDiagnostics(r'''
String void bar() { }
// [diag.missingConstFinalVarOrType][column 1][length 6] Variables must be declared using the keywords 'const', 'final', 'var' or a type name.
// [diag.expectedToken][column 1][length 6] Expected to find ';'.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
TopLevelVariableDeclaration
variables: VariableDeclarationList
variables
VariableDeclaration
name: String
semicolon: ; <synthetic>
FunctionDeclaration
returnType: NamedType
name: void
name: bar
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
''');
}
}
/// Test how well the parser recovers when there is extra punctuation.
@reflectiveTest
class PunctuationTest extends ParserDiagnosticsTest {
void test_extraComma_extendsClause() {
var parseResult = parseTestCodeWithDiagnostics(r'''
class A { }
class B { }
class Foo extends A, B {
// ^
// [diag.multipleExtendsClauses] Each class definition can have at most one extends clause.
Foo() { }
}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: A
body: BlockClassBody
leftBracket: {
rightBracket: }
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: B
body: BlockClassBody
leftBracket: {
rightBracket: }
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: Foo
extendsClause: ExtendsClause
extendsKeyword: extends
superclass: NamedType
name: A
body: BlockClassBody
leftBracket: {
members
ConstructorDeclaration
typeName: SimpleIdentifier
token: Foo
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
rightBracket: }
''');
}
void test_extraSemicolon_afterLastClassMember() {
var parseResult = parseTestCodeWithDiagnostics(r'''
class C {
foo() {};
// ^
// [diag.expectedClassMember] Expected a class member.
}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: C
body: BlockClassBody
leftBracket: {
members
MethodDeclaration
name: foo
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
rightBracket: }
''');
}
void test_extraSemicolon_afterLastTopLevelMember() {
var parseResult = parseTestCodeWithDiagnostics(r'''
foo() {};
// ^
// [diag.unexpectedToken] Unexpected text ';'.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
name: foo
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
''');
}
void test_extraSemicolon_beforeFirstClassMember() {
var parseResult = parseTestCodeWithDiagnostics(r'''
class C {
;foo() {}
//^
// [diag.expectedClassMember] Expected a class member.
}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: C
body: BlockClassBody
leftBracket: {
members
MethodDeclaration
name: foo
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
rightBracket: }
''');
}
void test_extraSemicolon_beforeFirstTopLevelMember() {
var parseResult = parseTestCodeWithDiagnostics(r'''
;foo() {}
// [diag.unexpectedToken][column 1][length 1] Unexpected text ';'.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
name: foo
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
''');
}
void test_extraSemicolon_betweenClassMembers() {
var parseResult = parseTestCodeWithDiagnostics(r'''
class C {
foo() {};
// ^
// [diag.expectedClassMember] Expected a class member.
bar() {}
}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: C
body: BlockClassBody
leftBracket: {
members
MethodDeclaration
name: foo
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
MethodDeclaration
name: bar
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
rightBracket: }
''');
}
void test_extraSemicolon_betweenTopLevelMembers() {
var parseResult = parseTestCodeWithDiagnostics(r'''
foo() {};
// ^
// [diag.unexpectedToken] Unexpected text ';'.
bar() {}
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
FunctionDeclaration
name: foo
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
FunctionDeclaration
name: bar
functionExpression: FunctionExpression
parameters: FormalParameterList
leftParenthesis: (
rightParenthesis: )
body: BlockFunctionBody
block: Block
leftBracket: {
rightBracket: }
''');
}
}
/// Test how well the parser recovers when there is extra variance modifiers.
@reflectiveTest
class VarianceModifierTest extends ParserDiagnosticsTest {
void test_extraModifier_inClass() {
var parseResult = parseTestCodeWithDiagnostics(r'''
class A<in out X> {}
// ^^^
// [diag.multipleVarianceModifiers] Each type parameter can have at most one variance modifier.
''');
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
CompilationUnit
declarations
ClassDeclaration
classKeyword: class
namePart: NameWithTypeParameters
typeName: A
typeParameters: TypeParameterList
leftBracket: <
typeParameters
TypeParameter
varianceKeyword: in
name: X
rightBracket: >
body: BlockClassBody
leftBracket: {
rightBracket: }
''');
}
}