3c0e9642d2
Change-Id: Iad6246c55f45953bffe2c3de4a2e30a32db347d2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144820 Reviewed-by: Dmitry Stefantsov <dmitryas@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
834 lines
27 KiB
Dart
834 lines
27 KiB
Dart
// Copyright (c) 2016, 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:kernel/ast.dart';
|
|
import 'package:kernel/text/ast_to_text.dart';
|
|
import 'package:kernel/verifier.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
/// Checks that the verifier correctly find errors in invalid components.
|
|
///
|
|
/// The frontend should never generate invalid components, so we have to test
|
|
/// these by manually constructing invalid ASTs.
|
|
///
|
|
/// We mostly test negative cases here, as we get plenty of positive cases by
|
|
/// compiling the Dart test suite with the verifier enabled.
|
|
main() {
|
|
positiveTest(
|
|
'Test harness has no errors',
|
|
(TestHarness test) {
|
|
test.addNode(NullLiteral());
|
|
},
|
|
);
|
|
negative1Test(
|
|
'VariableGet out of scope',
|
|
(TestHarness test) {
|
|
VariableDeclaration node = test.makeVariable();
|
|
test.addNode(VariableGet(node));
|
|
return node;
|
|
},
|
|
(Node node) => "Variable '$node' used out of scope.",
|
|
);
|
|
negative1Test(
|
|
'VariableSet out of scope',
|
|
(TestHarness test) {
|
|
VariableDeclaration variable = test.makeVariable();
|
|
test.addNode(VariableSet(variable, new NullLiteral()));
|
|
return variable;
|
|
},
|
|
(Node node) => "Variable '$node' used out of scope.",
|
|
);
|
|
negative1Test(
|
|
'Variable block scope',
|
|
(TestHarness test) {
|
|
VariableDeclaration variable = test.makeVariable();
|
|
test.addNode(Block([
|
|
new Block([variable]),
|
|
new ReturnStatement(new VariableGet(variable))
|
|
]));
|
|
return variable;
|
|
},
|
|
(Node node) => "Variable '$node' used out of scope.",
|
|
);
|
|
negative1Test(
|
|
'Variable let scope',
|
|
(TestHarness test) {
|
|
VariableDeclaration variable = test.makeVariable();
|
|
test.addNode(LogicalExpression(
|
|
new Let(variable, new VariableGet(variable)),
|
|
'&&',
|
|
new VariableGet(variable)));
|
|
return variable;
|
|
},
|
|
(Node node) => "Variable '$node' used out of scope.",
|
|
);
|
|
negative1Test(
|
|
'Variable redeclared',
|
|
(TestHarness test) {
|
|
VariableDeclaration variable = test.makeVariable();
|
|
test.addNode(Block([variable, variable]));
|
|
return variable;
|
|
},
|
|
(Node node) => "Variable '$node' declared more than once.",
|
|
);
|
|
negative1Test(
|
|
'Member redeclared',
|
|
(TestHarness test) {
|
|
Field field =
|
|
new Field(new Name('field'), initializer: new NullLiteral());
|
|
test.addNode(Class(
|
|
name: 'Test',
|
|
supertype: test.objectClass.asRawSupertype,
|
|
fields: [field, field]));
|
|
return field;
|
|
},
|
|
(Node node) => "Member '$node' has been declared more than once.",
|
|
);
|
|
negative1Test(
|
|
'Class redeclared',
|
|
(TestHarness test) {
|
|
Class otherClass = test.otherClass;
|
|
test.addNode(
|
|
otherClass); // Test harness also adds otherClass to component.
|
|
return test.otherClass;
|
|
},
|
|
(Node node) => "Class '$node' declared more than once.",
|
|
);
|
|
negative1Test(
|
|
'Class type parameter redeclared',
|
|
(TestHarness test) {
|
|
TypeParameter parameter = test.makeTypeParameter();
|
|
test.addNode(Class(
|
|
name: 'Test',
|
|
supertype: test.objectClass.asRawSupertype,
|
|
typeParameters: [parameter, parameter]));
|
|
return parameter;
|
|
},
|
|
(Node node) => "Type parameter '$node' redeclared.",
|
|
);
|
|
negative1Test(
|
|
'Member type parameter redeclared',
|
|
(TestHarness test) {
|
|
TypeParameter parameter = test.makeTypeParameter();
|
|
test.addNode(Procedure(
|
|
new Name('bar'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new ReturnStatement(new NullLiteral()),
|
|
typeParameters: [parameter, parameter])));
|
|
|
|
return parameter;
|
|
},
|
|
(Node node) => "Type parameter '$node' redeclared.",
|
|
);
|
|
negative2Test(
|
|
'Type parameter out of scope',
|
|
(TestHarness test) {
|
|
TypeParameter parameter = test.makeTypeParameter();
|
|
test.addNode(ListLiteral([],
|
|
typeArgument: new TypeParameterType(parameter, Nullability.legacy)));
|
|
return [parameter, null];
|
|
},
|
|
(Node node, Node parent) =>
|
|
"Type parameter '$node' referenced out of scope,"
|
|
" parent is: '$parent'.",
|
|
);
|
|
negative2Test(
|
|
'Class type parameter from another class',
|
|
(TestHarness test) {
|
|
TypeParameter node = test.otherClass.typeParameters[0];
|
|
test.addNode(
|
|
TypeLiteral(new TypeParameterType(node, Nullability.legacy)));
|
|
return [node, test.otherClass];
|
|
},
|
|
(Node node, Node parent) =>
|
|
"Type parameter '$node' referenced out of scope,"
|
|
" parent is: '$parent'.",
|
|
);
|
|
negative2Test(
|
|
'Class type parameter in static method',
|
|
(TestHarness test) {
|
|
TypeParameter node = test.classTypeParameter;
|
|
test.addNode(Procedure(
|
|
new Name('bar'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new ReturnStatement(new TypeLiteral(
|
|
new TypeParameterType(node, Nullability.legacy)))),
|
|
isStatic: true));
|
|
|
|
return [node, test.enclosingClass];
|
|
},
|
|
(Node node, Node parent) =>
|
|
"Type parameter '$node' referenced from static context,"
|
|
" parent is: '$parent'.",
|
|
);
|
|
negative2Test(
|
|
'Class type parameter in static field',
|
|
(TestHarness test) {
|
|
TypeParameter node = test.classTypeParameter;
|
|
test.addNode(Field(new Name('field'),
|
|
initializer:
|
|
new TypeLiteral(new TypeParameterType(node, Nullability.legacy)),
|
|
isStatic: true));
|
|
return [node, test.enclosingClass];
|
|
},
|
|
(Node node, Node parent) =>
|
|
"Type parameter '$node' referenced from static context,"
|
|
" parent is: '$parent'.",
|
|
);
|
|
negative2Test(
|
|
'Method type parameter out of scope',
|
|
(TestHarness test) {
|
|
TypeParameter parameter = test.makeTypeParameter();
|
|
FunctionNode parent =
|
|
new FunctionNode(new EmptyStatement(), typeParameters: [parameter]);
|
|
test.addNode(Class(
|
|
name: 'Test',
|
|
supertype: test.objectClass.asRawSupertype,
|
|
procedures: [
|
|
new Procedure(new Name('generic'), ProcedureKind.Method, parent),
|
|
new Procedure(
|
|
new Name('use'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new ReturnStatement(new TypeLiteral(
|
|
new TypeParameterType(parameter, Nullability.legacy)))))
|
|
]));
|
|
|
|
return [parameter, parent];
|
|
},
|
|
(Node node, Node parent) =>
|
|
"Type parameter '$node' referenced out of scope,"
|
|
" parent is: '$parent'.",
|
|
);
|
|
negative1Test(
|
|
'Interface type arity too low',
|
|
(TestHarness test) {
|
|
InterfaceType node =
|
|
new InterfaceType(test.otherClass, Nullability.legacy, []);
|
|
test.addNode(TypeLiteral(node));
|
|
return node;
|
|
},
|
|
(Node node) => "Type $node provides 0 type arguments,"
|
|
" but the class declares 1 parameters.",
|
|
);
|
|
negative1Test(
|
|
'Interface type arity too high',
|
|
(TestHarness test) {
|
|
InterfaceType node = new InterfaceType(test.otherClass,
|
|
Nullability.legacy, [new DynamicType(), new DynamicType()]);
|
|
test.addNode(TypeLiteral(node));
|
|
return node;
|
|
},
|
|
(Node node) => "Type $node provides 2 type arguments,"
|
|
" but the class declares 1 parameters.",
|
|
);
|
|
negative1Test(
|
|
'Dangling interface type',
|
|
(TestHarness test) {
|
|
Class orphan = new Class();
|
|
test.addNode(
|
|
new TypeLiteral(new InterfaceType(orphan, Nullability.legacy)));
|
|
return orphan;
|
|
},
|
|
(Node node) => "Dangling reference to '$node', parent is: 'null'.",
|
|
);
|
|
negative1Test(
|
|
'Dangling field get',
|
|
(TestHarness test) {
|
|
Field orphan = new Field(new Name('foo'));
|
|
test.addNode(new DirectPropertyGet(new NullLiteral(), orphan));
|
|
return orphan;
|
|
},
|
|
(Node node) => "Dangling reference to '$node', parent is: 'null'.",
|
|
);
|
|
simpleNegativeTest(
|
|
'Missing block parent pointer',
|
|
"Incorrect parent pointer on ReturnStatement:"
|
|
" expected 'Block', but found: 'Null'.",
|
|
(TestHarness test) {
|
|
var block = new Block([]);
|
|
block.statements.add(new ReturnStatement());
|
|
test.addNode(block);
|
|
},
|
|
);
|
|
simpleNegativeTest(
|
|
'Missing function parent pointer',
|
|
"Incorrect parent pointer on FunctionNode:"
|
|
" expected 'Procedure', but found: 'Null'.",
|
|
(TestHarness test) {
|
|
var procedure =
|
|
new Procedure(new Name('bar'), ProcedureKind.Method, null);
|
|
procedure.function = new FunctionNode(new EmptyStatement());
|
|
test.addNode(procedure);
|
|
},
|
|
);
|
|
simpleNegativeTest(
|
|
'StaticGet without target',
|
|
"StaticGet without target.",
|
|
(TestHarness test) {
|
|
test.addNode(StaticGet(null));
|
|
},
|
|
);
|
|
simpleNegativeTest(
|
|
'StaticSet without target',
|
|
"StaticSet without target.",
|
|
(TestHarness test) {
|
|
test.addNode(StaticSet(null, new NullLiteral()));
|
|
},
|
|
);
|
|
simpleNegativeTest(
|
|
'StaticInvocation without target',
|
|
"StaticInvocation without target.",
|
|
(TestHarness test) {
|
|
test.addNode(StaticInvocation(null, new Arguments.empty()));
|
|
},
|
|
);
|
|
positiveTest(
|
|
'Correct StaticInvocation',
|
|
(TestHarness test) {
|
|
var method = new Procedure(
|
|
new Name('foo'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new EmptyStatement(),
|
|
positionalParameters: [new VariableDeclaration('p')]),
|
|
isStatic: true);
|
|
test.enclosingClass.addMember(method);
|
|
test.addNode(
|
|
StaticInvocation(method, new Arguments([new NullLiteral()])));
|
|
},
|
|
);
|
|
negative1Test(
|
|
'StaticInvocation with too many parameters',
|
|
(TestHarness test) {
|
|
var method = new Procedure(new Name('bar'), ProcedureKind.Method,
|
|
new FunctionNode(new EmptyStatement()),
|
|
isStatic: true);
|
|
test.enclosingClass.addMember(method);
|
|
test.addNode(
|
|
StaticInvocation(method, new Arguments([new NullLiteral()])));
|
|
return method;
|
|
},
|
|
(Node node) => "StaticInvocation with incompatible arguments for"
|
|
" '$node'.",
|
|
);
|
|
negative1Test(
|
|
'StaticInvocation with too few parameters',
|
|
(TestHarness test) {
|
|
var method = new Procedure(
|
|
new Name('bar'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new EmptyStatement(),
|
|
positionalParameters: [new VariableDeclaration('p')]),
|
|
isStatic: true);
|
|
test.enclosingClass.addMember(method);
|
|
test.addNode(StaticInvocation(method, new Arguments.empty()));
|
|
return method;
|
|
},
|
|
(Node node) => "StaticInvocation with incompatible arguments for '$node'.",
|
|
);
|
|
negative1Test(
|
|
'StaticInvocation with unmatched named parameter',
|
|
(TestHarness test) {
|
|
var method = new Procedure(new Name('bar'), ProcedureKind.Method,
|
|
new FunctionNode(new EmptyStatement()),
|
|
isStatic: true);
|
|
test.enclosingClass.addMember(method);
|
|
test.addNode(StaticInvocation(
|
|
method,
|
|
new Arguments([],
|
|
named: [new NamedExpression('p', new NullLiteral())])));
|
|
return method;
|
|
},
|
|
(Node node) => "StaticInvocation with incompatible arguments for"
|
|
" '$node'.",
|
|
);
|
|
negative1Test(
|
|
'StaticInvocation with missing type argument',
|
|
(TestHarness test) {
|
|
Procedure method = new Procedure(
|
|
new Name('bar'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new EmptyStatement(),
|
|
typeParameters: [test.makeTypeParameter()]),
|
|
isStatic: true);
|
|
test.enclosingClass.addMember(method);
|
|
test.addNode(StaticInvocation(method, new Arguments.empty()));
|
|
return method;
|
|
},
|
|
(Node node) => "StaticInvocation with wrong number of type arguments for"
|
|
" '$node'.",
|
|
);
|
|
negative1Test(
|
|
'ConstructorInvocation with missing type argument',
|
|
(TestHarness test) {
|
|
var constructor = new Constructor(new FunctionNode(new EmptyStatement()),
|
|
name: new Name('foo'));
|
|
test.enclosingClass.addMember(constructor);
|
|
test.addNode(ConstructorInvocation(constructor, new Arguments.empty()));
|
|
return constructor;
|
|
},
|
|
(Node node) =>
|
|
"ConstructorInvocation with wrong number of type arguments for"
|
|
" '$node'.",
|
|
);
|
|
positiveTest(
|
|
'Valid typedef Foo = `(C) => void`',
|
|
(TestHarness test) {
|
|
var typedef_ = new Typedef(
|
|
'Foo',
|
|
new FunctionType(
|
|
[test.otherLegacyRawType], const VoidType(), Nullability.legacy));
|
|
test.addNode(typedef_);
|
|
},
|
|
);
|
|
positiveTest(
|
|
'Valid typedef Foo = C<dynamic>',
|
|
(TestHarness test) {
|
|
var typedef_ = new Typedef(
|
|
'Foo',
|
|
new InterfaceType(
|
|
test.otherClass, Nullability.legacy, [const DynamicType()]));
|
|
test.addNode(typedef_);
|
|
},
|
|
);
|
|
positiveTest(
|
|
'Valid typedefs Foo = Bar, Bar = C',
|
|
(TestHarness test) {
|
|
var foo = new Typedef('Foo', null);
|
|
var bar = new Typedef('Bar', null);
|
|
foo.type = new TypedefType(bar, Nullability.legacy);
|
|
bar.type = test.otherLegacyRawType;
|
|
test.enclosingLibrary.addTypedef(foo);
|
|
test.enclosingLibrary.addTypedef(bar);
|
|
},
|
|
);
|
|
positiveTest(
|
|
'Valid typedefs Foo = C<Bar>, Bar = C',
|
|
(TestHarness test) {
|
|
var foo = new Typedef('Foo', null);
|
|
var bar = new Typedef('Bar', null);
|
|
foo.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypedefType(bar, Nullability.legacy)]);
|
|
bar.type = test.otherLegacyRawType;
|
|
test.enclosingLibrary.addTypedef(foo);
|
|
test.enclosingLibrary.addTypedef(bar);
|
|
},
|
|
);
|
|
positiveTest(
|
|
'Valid typedef type in field',
|
|
(TestHarness test) {
|
|
var typedef_ = new Typedef(
|
|
'Foo',
|
|
new FunctionType(
|
|
[test.otherLegacyRawType], const VoidType(), Nullability.legacy));
|
|
var field = new Field(new Name('field'),
|
|
type: new TypedefType(typedef_, Nullability.legacy), isStatic: true);
|
|
test.enclosingLibrary.addTypedef(typedef_);
|
|
test.enclosingLibrary.addMember(field);
|
|
},
|
|
);
|
|
negative1Test(
|
|
'Invalid typedef Foo = Foo',
|
|
(TestHarness test) {
|
|
var typedef_ = new Typedef('Foo', null);
|
|
typedef_.type = new TypedefType(typedef_, Nullability.legacy);
|
|
test.addNode(typedef_);
|
|
return typedef_;
|
|
},
|
|
(Node node) => "The typedef '$node' refers to itself",
|
|
);
|
|
negative1Test(
|
|
'Invalid typedef Foo = `(Foo) => void`',
|
|
(TestHarness test) {
|
|
var typedef_ = new Typedef('Foo', null);
|
|
typedef_.type = new FunctionType(
|
|
[new TypedefType(typedef_, Nullability.legacy)],
|
|
const VoidType(),
|
|
Nullability.legacy);
|
|
test.addNode(typedef_);
|
|
return typedef_;
|
|
},
|
|
(Node node) => "The typedef '$node' refers to itself",
|
|
);
|
|
negative1Test(
|
|
'Invalid typedef Foo = `() => Foo`',
|
|
(TestHarness test) {
|
|
var typedef_ = new Typedef('Foo', null);
|
|
typedef_.type = new FunctionType([],
|
|
new TypedefType(typedef_, Nullability.legacy), Nullability.legacy);
|
|
test.addNode(typedef_);
|
|
return typedef_;
|
|
},
|
|
(Node node) => "The typedef '$node' refers to itself",
|
|
);
|
|
negative1Test(
|
|
'Invalid typedef Foo = C<Foo>',
|
|
(TestHarness test) {
|
|
var typedef_ = new Typedef('Foo', null);
|
|
typedef_.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypedefType(typedef_, Nullability.legacy)]);
|
|
test.addNode(typedef_);
|
|
return typedef_;
|
|
},
|
|
(Node node) => "The typedef '$node' refers to itself",
|
|
);
|
|
negative1Test(
|
|
'Invalid typedefs Foo = Bar, Bar = Foo',
|
|
(TestHarness test) {
|
|
var foo = new Typedef('Foo', null);
|
|
var bar = new Typedef('Bar', null);
|
|
foo.type = new TypedefType(bar, Nullability.legacy);
|
|
bar.type = new TypedefType(foo, Nullability.legacy);
|
|
test.enclosingLibrary.addTypedef(foo);
|
|
test.enclosingLibrary.addTypedef(bar);
|
|
return foo;
|
|
},
|
|
(Node foo) => "The typedef '$foo' refers to itself",
|
|
);
|
|
negative1Test(
|
|
'Invalid typedefs Foo = Bar, Bar = C<Foo>',
|
|
(TestHarness test) {
|
|
var foo = new Typedef('Foo', null);
|
|
var bar = new Typedef('Bar', null);
|
|
foo.type = new TypedefType(bar, Nullability.legacy);
|
|
bar.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypedefType(foo, Nullability.legacy)]);
|
|
test.enclosingLibrary.addTypedef(foo);
|
|
test.enclosingLibrary.addTypedef(bar);
|
|
return foo;
|
|
},
|
|
(Node foo) => "The typedef '$foo' refers to itself",
|
|
);
|
|
negative1Test(
|
|
'Invalid typedefs Foo = C<Bar>, Bar = C<Foo>',
|
|
(TestHarness test) {
|
|
var foo = new Typedef('Foo', null);
|
|
var bar = new Typedef('Bar', null);
|
|
foo.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypedefType(bar, Nullability.legacy)]);
|
|
bar.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypedefType(foo, Nullability.legacy)]);
|
|
test.enclosingLibrary.addTypedef(foo);
|
|
test.enclosingLibrary.addTypedef(bar);
|
|
return foo;
|
|
},
|
|
(Node foo) => "The typedef '$foo' refers to itself",
|
|
);
|
|
positiveTest(
|
|
'Valid long typedefs C20 = C19 = ... = C1 = C0 = dynamic',
|
|
(TestHarness test) {
|
|
var typedef_ = new Typedef('C0', const DynamicType());
|
|
test.enclosingLibrary.addTypedef(typedef_);
|
|
for (int i = 1; i < 20; ++i) {
|
|
typedef_ =
|
|
new Typedef('C$i', new TypedefType(typedef_, Nullability.legacy));
|
|
test.enclosingLibrary.addTypedef(typedef_);
|
|
}
|
|
},
|
|
);
|
|
negative1Test(
|
|
'Invalid long typedefs C20 = C19 = ... = C1 = C0 = C20',
|
|
(TestHarness test) {
|
|
Typedef firstTypedef = new Typedef('C0', null);
|
|
Typedef typedef_ = firstTypedef;
|
|
test.enclosingLibrary.addTypedef(typedef_);
|
|
var first = typedef_;
|
|
for (int i = 1; i < 20; ++i) {
|
|
typedef_ =
|
|
new Typedef('C$i', new TypedefType(typedef_, Nullability.legacy));
|
|
test.enclosingLibrary.addTypedef(typedef_);
|
|
}
|
|
first.type = new TypedefType(typedef_, Nullability.legacy);
|
|
return firstTypedef;
|
|
},
|
|
(Node node) => "The typedef '$node' refers to itself",
|
|
);
|
|
positiveTest(
|
|
'Valid typedef Foo<T extends C> = C<T>',
|
|
(TestHarness test) {
|
|
var param = new TypeParameter('T', test.otherLegacyRawType);
|
|
var foo = new Typedef(
|
|
'Foo',
|
|
new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypeParameterType(param, Nullability.legacy)]),
|
|
typeParameters: [param]);
|
|
test.addNode(foo);
|
|
},
|
|
);
|
|
positiveTest(
|
|
'Valid typedef Foo<T extends C<T>> = C<T>',
|
|
(TestHarness test) {
|
|
var param = new TypeParameter('T', test.otherLegacyRawType);
|
|
param.bound = new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypeParameterType(param, Nullability.legacy)]);
|
|
var foo = new Typedef(
|
|
'Foo',
|
|
new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypeParameterType(param, Nullability.legacy)]),
|
|
typeParameters: [param]);
|
|
test.addNode(foo);
|
|
},
|
|
);
|
|
positiveTest(
|
|
'Valid typedef Foo<T> = dynamic, Bar<T extends Foo<T>> = C<T>',
|
|
(TestHarness test) {
|
|
var fooParam = test.makeTypeParameter('T');
|
|
var foo =
|
|
new Typedef('Foo', const DynamicType(), typeParameters: [fooParam]);
|
|
var barParam = new TypeParameter('T', null);
|
|
barParam.bound = new TypedefType(foo, Nullability.legacy,
|
|
[new TypeParameterType(barParam, Nullability.legacy)]);
|
|
var bar = new Typedef(
|
|
'Bar',
|
|
new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypeParameterType(barParam, Nullability.legacy)]),
|
|
typeParameters: [barParam]);
|
|
test.enclosingLibrary.addTypedef(foo);
|
|
test.enclosingLibrary.addTypedef(bar);
|
|
},
|
|
);
|
|
negative1Test(
|
|
'Invalid typedefs Foo<T extends Bar<T>>, Bar<T extends Foo<T>>',
|
|
(TestHarness test) {
|
|
var fooParam = test.makeTypeParameter('T');
|
|
var foo =
|
|
new Typedef('Foo', const DynamicType(), typeParameters: [fooParam]);
|
|
var barParam = new TypeParameter('T', null);
|
|
barParam.bound = new TypedefType(foo, Nullability.legacy,
|
|
[new TypeParameterType(barParam, Nullability.legacy)]);
|
|
var bar = new Typedef(
|
|
'Bar',
|
|
new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypeParameterType(barParam, Nullability.legacy)]),
|
|
typeParameters: [barParam]);
|
|
fooParam.bound = new TypedefType(bar, Nullability.legacy,
|
|
[new TypeParameterType(fooParam, Nullability.legacy)]);
|
|
test.enclosingLibrary.addTypedef(foo);
|
|
test.enclosingLibrary.addTypedef(bar);
|
|
return foo;
|
|
},
|
|
(Node foo) => "The typedef '$foo' refers to itself",
|
|
);
|
|
negative1Test(
|
|
'Invalid typedef Foo<T extends Foo<dynamic> = C<T>',
|
|
(TestHarness test) {
|
|
var param = new TypeParameter('T', null);
|
|
var foo = new Typedef(
|
|
'Foo',
|
|
new InterfaceType(test.otherClass, Nullability.legacy,
|
|
[new TypeParameterType(param, Nullability.legacy)]),
|
|
typeParameters: [param]);
|
|
param.bound =
|
|
new TypedefType(foo, Nullability.legacy, [const DynamicType()]);
|
|
test.addNode(foo);
|
|
return foo;
|
|
},
|
|
(Node foo) => "The typedef '$foo' refers to itself",
|
|
);
|
|
negative1Test(
|
|
'Typedef arity error',
|
|
(TestHarness test) {
|
|
var param = test.makeTypeParameter('T');
|
|
var foo =
|
|
new Typedef('Foo', test.otherLegacyRawType, typeParameters: [param]);
|
|
var typedefType = new TypedefType(foo, Nullability.legacy, []);
|
|
var field =
|
|
new Field(new Name('field'), type: typedefType, isStatic: true);
|
|
test.enclosingLibrary.addTypedef(foo);
|
|
test.enclosingLibrary.addMember(field);
|
|
return typedefType;
|
|
},
|
|
(Node typedefType) =>
|
|
"The typedef type $typedefType provides 0 type arguments,"
|
|
" but the typedef declares 1 parameters.",
|
|
);
|
|
negative1Test(
|
|
'Dangling typedef reference',
|
|
(TestHarness test) {
|
|
var foo = new Typedef('Foo', test.otherLegacyRawType, typeParameters: []);
|
|
var field = new Field(new Name('field'),
|
|
type: new TypedefType(foo, Nullability.legacy, []), isStatic: true);
|
|
test.enclosingLibrary.addMember(field);
|
|
return foo;
|
|
},
|
|
(Node foo) => "Dangling reference to '$foo', parent is: 'null'",
|
|
);
|
|
negative1Test(
|
|
'Non-static top-level field',
|
|
(TestHarness test) {
|
|
var field = new Field(new Name('field'));
|
|
test.enclosingLibrary.addMember(field);
|
|
return null;
|
|
},
|
|
(Node node) => "The top-level field 'field' should be static",
|
|
);
|
|
}
|
|
|
|
checkHasError(Component component, Matcher matcher) {
|
|
try {
|
|
verifyComponent(component);
|
|
} on VerificationError catch (e) {
|
|
expect(e.details, matcher);
|
|
return;
|
|
}
|
|
fail('Failed to reject invalid component:\n${componentToString(component)}');
|
|
}
|
|
|
|
class TestHarness {
|
|
Component component;
|
|
Class objectClass;
|
|
Library stubLibrary;
|
|
|
|
TypeParameter classTypeParameter;
|
|
|
|
Library enclosingLibrary;
|
|
Class enclosingClass;
|
|
Procedure enclosingMember;
|
|
|
|
Class otherClass;
|
|
|
|
InterfaceType objectLegacyRawType;
|
|
InterfaceType enclosingLegacyRawType;
|
|
InterfaceType otherLegacyRawType;
|
|
|
|
void addNode(TreeNode node) {
|
|
if (node is Expression) {
|
|
addExpression(node);
|
|
} else if (node is Statement) {
|
|
addStatement(node);
|
|
} else if (node is Member) {
|
|
addClassMember(node);
|
|
} else if (node is Class) {
|
|
addClass(node);
|
|
} else if (node is Typedef) {
|
|
addTypedef(node);
|
|
}
|
|
}
|
|
|
|
void addExpression(Expression node) {
|
|
addStatement(new ReturnStatement(node));
|
|
}
|
|
|
|
void addStatement(Statement node) {
|
|
var function = enclosingMember.function;
|
|
function.body = node..parent = function;
|
|
}
|
|
|
|
void addClassMember(Member node) {
|
|
enclosingClass.addMember(node);
|
|
}
|
|
|
|
void addTopLevelMember(Member node) {
|
|
enclosingLibrary.addMember(node);
|
|
}
|
|
|
|
void addClass(Class node) {
|
|
enclosingLibrary.addClass(node);
|
|
}
|
|
|
|
void addTypedef(Typedef node) {
|
|
enclosingLibrary.addTypedef(node);
|
|
}
|
|
|
|
VariableDeclaration makeVariable() => new VariableDeclaration(null);
|
|
|
|
TypeParameter makeTypeParameter([String name]) {
|
|
return new TypeParameter(name, objectLegacyRawType, const DynamicType());
|
|
}
|
|
|
|
TestHarness() {
|
|
setupComponent();
|
|
}
|
|
|
|
void setupComponent() {
|
|
component = new Component();
|
|
stubLibrary = new Library(Uri.parse('dart:core'));
|
|
component.libraries.add(stubLibrary..parent = component);
|
|
stubLibrary.name = 'dart.core';
|
|
objectClass = new Class(name: 'Object');
|
|
objectLegacyRawType =
|
|
new InterfaceType(objectClass, Nullability.legacy, const <DartType>[]);
|
|
stubLibrary.addClass(objectClass);
|
|
enclosingLibrary = new Library(Uri.parse('file://test.dart'));
|
|
component.libraries.add(enclosingLibrary..parent = component);
|
|
enclosingLibrary.name = 'test_lib';
|
|
classTypeParameter = makeTypeParameter('T');
|
|
enclosingClass = new Class(
|
|
name: 'TestClass',
|
|
typeParameters: [classTypeParameter],
|
|
supertype: objectClass.asRawSupertype);
|
|
enclosingLegacyRawType = new InterfaceType(enclosingClass,
|
|
Nullability.legacy, const <DartType>[const DynamicType()]);
|
|
enclosingLibrary.addClass(enclosingClass);
|
|
enclosingMember = new Procedure(new Name('test'), ProcedureKind.Method,
|
|
new FunctionNode(new EmptyStatement()));
|
|
enclosingClass.addMember(enclosingMember);
|
|
otherClass = new Class(
|
|
name: 'OtherClass',
|
|
typeParameters: [makeTypeParameter('OtherT')],
|
|
supertype: objectClass.asRawSupertype);
|
|
otherLegacyRawType = new InterfaceType(
|
|
otherClass, Nullability.legacy, const <DartType>[const DynamicType()]);
|
|
enclosingLibrary.addClass(otherClass);
|
|
}
|
|
}
|
|
|
|
negative1Test(String name, Node Function(TestHarness test) nodeProvider,
|
|
dynamic Function(Node node) matcher) {
|
|
TestHarness testHarness = new TestHarness();
|
|
Node node = nodeProvider(testHarness);
|
|
test(
|
|
name,
|
|
() {
|
|
dynamic matcherResult = matcher(node);
|
|
if (matcherResult is String) {
|
|
matcherResult = equals(matcherResult);
|
|
}
|
|
checkHasError(testHarness.component, matcherResult);
|
|
},
|
|
);
|
|
}
|
|
|
|
negative2Test(String name, List<Node> Function(TestHarness test) nodeProvider,
|
|
dynamic Function(Node node, Node other) matcher) {
|
|
TestHarness testHarness = new TestHarness();
|
|
List<Node> nodes = nodeProvider(testHarness);
|
|
if (nodes.length != 2) throw "Needs exactly 2 nodes: Node and other!";
|
|
test(
|
|
name,
|
|
() {
|
|
dynamic matcherResult = matcher(nodes[0], nodes[1]);
|
|
if (matcherResult is String) {
|
|
matcherResult = equals(matcherResult);
|
|
}
|
|
checkHasError(testHarness.component, matcherResult);
|
|
},
|
|
);
|
|
}
|
|
|
|
simpleNegativeTest(String name, dynamic matcher,
|
|
void Function(TestHarness test) makeTestCase) {
|
|
TestHarness testHarness = new TestHarness();
|
|
test(
|
|
name,
|
|
() {
|
|
makeTestCase(testHarness);
|
|
if (matcher is String) {
|
|
matcher = equals(matcher);
|
|
}
|
|
checkHasError(testHarness.component, matcher);
|
|
},
|
|
);
|
|
}
|
|
|
|
positiveTest(String name, void makeTestCase(TestHarness test)) {
|
|
test(
|
|
name,
|
|
() {
|
|
var test = new TestHarness();
|
|
makeTestCase(test);
|
|
verifyComponent(test.component);
|
|
},
|
|
);
|
|
}
|