blob: c80dfaa4349665c760e0eb125920826b428d41d8 [file] [log] [blame]
// 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.
void 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)),
LogicalExpressionOperator.AND,
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.mutable(new Name('field'),
initializer: new NullLiteral(), fileUri: dummyUri);
test.addNode(Class(
name: 'Test',
supertype: test.objectClass.asRawSupertype,
fields: [field, field],
fileUri: dummyUri));
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],
fileUri: dummyUri));
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]),
fileUri: dummyUri));
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,"
" owner 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,"
" owner 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,
fileUri: dummyUri));
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.mutable(new Name('field'),
initializer:
new TypeLiteral(new TypeParameterType(node, Nullability.legacy)),
isStatic: true,
fileUri: dummyUri));
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,
fileUri: dummyUri),
new Procedure(
new Name('use'),
ProcedureKind.Method,
new FunctionNode(new ReturnStatement(new TypeLiteral(
new TypeParameterType(parameter, Nullability.legacy)))),
fileUri: dummyUri)
],
fileUri: dummyUri));
return [parameter, parent];
},
(Node? node, Node? parent) =>
"Type parameter '$node' referenced out of scope,"
" owner is: '${(parent as TreeNode).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(name: 'Class', fileUri: dummyUri);
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.mutable(new Name('foo'), fileUri: dummyUri);
test.addNode(new InstanceGet(
InstanceAccessKind.Instance, new NullLiteral(), orphan.name,
interfaceTarget: orphan, resultType: orphan.getterType));
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, dummyFunctionNode,
fileUri: dummyUri);
procedure.function = new FunctionNode(new EmptyStatement());
test.addNode(procedure);
},
);
positiveTest(
'Correct StaticInvocation',
(TestHarness test) {
var method = new Procedure(
new Name('foo'),
ProcedureKind.Method,
new FunctionNode(new EmptyStatement(),
positionalParameters: [new VariableDeclaration('p')]),
isStatic: true,
fileUri: dummyUri);
test.enclosingClass.addProcedure(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, fileUri: dummyUri);
test.enclosingClass.addProcedure(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,
fileUri: dummyUri);
test.enclosingClass.addProcedure(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, fileUri: dummyUri);
test.enclosingClass.addProcedure(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,
fileUri: dummyUri);
test.enclosingClass.addProcedure(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'), fileUri: dummyUri);
test.enclosingClass.addConstructor(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),
fileUri: dummyUri);
test.addNode(typedef_);
},
);
positiveTest(
'Valid typedef Foo = C<dynamic>',
(TestHarness test) {
var typedef_ = new Typedef(
'Foo',
new InterfaceType(
test.otherClass, Nullability.legacy, [const DynamicType()]),
fileUri: dummyUri);
test.addNode(typedef_);
},
);
positiveTest(
'Valid typedefs Foo = Bar, Bar = C',
(TestHarness test) {
var foo = new Typedef('Foo', null, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
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, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
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),
fileUri: dummyUri);
var field = new Field.mutable(new Name('field'),
type: new TypedefType(typedef_, Nullability.legacy),
isStatic: true,
fileUri: dummyUri);
test.enclosingLibrary.addTypedef(typedef_);
test.enclosingLibrary.addField(field);
},
);
negative1Test(
'Invalid typedef Foo = Foo',
(TestHarness test) {
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
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, fileUri: dummyUri);
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, fileUri: dummyUri);
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, fileUri: dummyUri);
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, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
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, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
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, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
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(), fileUri: dummyUri);
test.enclosingLibrary.addTypedef(typedef_);
for (int i = 1; i < 20; ++i) {
typedef_ = new Typedef(
'C$i', new TypedefType(typedef_, Nullability.legacy),
fileUri: dummyUri);
test.enclosingLibrary.addTypedef(typedef_);
}
},
);
negative1Test(
'Invalid long typedefs C20 = C19 = ... = C1 = C0 = C20',
(TestHarness test) {
Typedef firstTypedef = new Typedef('C0', null, fileUri: dummyUri);
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),
fileUri: dummyUri);
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],
fileUri: dummyUri);
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],
fileUri: dummyUri);
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], fileUri: dummyUri);
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],
fileUri: dummyUri);
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], fileUri: dummyUri);
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],
fileUri: dummyUri);
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],
fileUri: dummyUri);
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], fileUri: dummyUri);
var typedefType = new TypedefType(foo, Nullability.legacy, []);
var field = new Field.mutable(new Name('field'),
type: typedefType, isStatic: true, fileUri: dummyUri);
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addField(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: [], fileUri: dummyUri);
var field = new Field.mutable(new Name('field'),
type: new TypedefType(foo, Nullability.legacy, []),
isStatic: true,
fileUri: dummyUri);
test.enclosingLibrary.addField(field);
return foo;
},
(Node? foo) => "Dangling reference to '$foo', parent is: 'null'",
);
negative1Test(
'Non-static top-level field',
(TestHarness test) {
var field = new Field.mutable(new Name('field'), fileUri: dummyUri);
test.enclosingLibrary.addField(field);
return null;
},
(Node? node) => "The top-level field 'field' should be static",
);
}
void 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 {
late Component component;
late Class objectClass;
late Library stubLibrary;
late TypeParameter classTypeParameter;
late Library enclosingLibrary;
late Class enclosingClass;
late Procedure enclosingMember;
late Class otherClass;
late InterfaceType objectLegacyRawType;
late InterfaceType enclosingLegacyRawType;
late 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) {
if (node is Procedure) {
enclosingClass.addProcedure(node);
} else if (node is Field) {
enclosingClass.addField(node);
} else if (node is Constructor) {
enclosingClass.addConstructor(node);
} else if (node is RedirectingFactory) {
enclosingClass.addRedirectingFactory(node);
} else {
throw "Unexpected class member: ${node.runtimeType}";
}
}
void addTopLevelMember(Member node) {
if (node is Procedure) {
enclosingLibrary.addProcedure(node);
} else if (node is Field) {
enclosingLibrary.addField(node);
} else {
throw "Unexpected top level member: ${node.runtimeType}";
}
}
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();
Uri dartCoreUri = Uri.parse('dart:core');
stubLibrary = new Library(dartCoreUri, fileUri: dartCoreUri);
component.libraries.add(stubLibrary..parent = component);
stubLibrary.name = 'dart.core';
objectClass = new Class(name: 'Object', fileUri: dartCoreUri);
objectLegacyRawType =
new InterfaceType(objectClass, Nullability.legacy, const <DartType>[]);
stubLibrary.addClass(objectClass);
Uri testUri = Uri.parse('file://test.dart');
enclosingLibrary = new Library(testUri, fileUri: testUri);
component.libraries.add(enclosingLibrary..parent = component);
enclosingLibrary.name = 'test_lib';
classTypeParameter = makeTypeParameter('T');
enclosingClass = new Class(
name: 'TestClass',
typeParameters: [classTypeParameter],
supertype: objectClass.asRawSupertype,
fileUri: testUri);
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()),
fileUri: dummyUri);
enclosingClass.addProcedure(enclosingMember);
otherClass = new Class(
name: 'OtherClass',
typeParameters: [makeTypeParameter('OtherT')],
supertype: objectClass.asRawSupertype,
fileUri: testUri);
otherLegacyRawType = new InterfaceType(
otherClass, Nullability.legacy, const <DartType>[const DynamicType()]);
enclosingLibrary.addClass(otherClass);
}
}
void 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);
},
);
}
void 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);
},
);
}
void 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);
},
);
}
void positiveTest(String name, void makeTestCase(TestHarness test)) {
test(
name,
() {
var test = new TestHarness();
makeTestCase(test);
verifyComponent(test.component);
},
);
}