440813c67b
This also fixes some issues in the frontend and transformers that generated calls without the correct number of type arguments. BUG= R=kmillikin@google.com Review URL: https://codereview.chromium.org/2533793005 .
278 lines
9.7 KiB
Dart
278 lines
9.7 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 programs.
|
|
///
|
|
/// The frontend should never generate invalid programs, 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', () {
|
|
return new NullLiteral();
|
|
});
|
|
negativeTest('VariableGet out of scope', () {
|
|
return new VariableGet(makeVariable());
|
|
});
|
|
negativeTest('VariableSet out of scope', () {
|
|
return new VariableSet(makeVariable(), new NullLiteral());
|
|
});
|
|
negativeTest('Variable block scope', () {
|
|
VariableDeclaration variable = makeVariable();
|
|
return new Block([
|
|
new Block([variable]),
|
|
new ReturnStatement(new VariableGet(variable))
|
|
]);
|
|
});
|
|
negativeTest('Variable let scope', () {
|
|
VariableDeclaration variable = makeVariable();
|
|
return new LogicalExpression(new Let(variable, new VariableGet(variable)),
|
|
'&&', new VariableGet(variable));
|
|
});
|
|
negativeTest('Variable redeclared', () {
|
|
VariableDeclaration variable = makeVariable();
|
|
return new Block([variable, variable]);
|
|
});
|
|
negativeTest('Member redeclared', () {
|
|
Field field = new Field(new Name('field'), initializer: new NullLiteral());
|
|
return new Class(
|
|
name: 'Test',
|
|
supertype: objectClass.asRawSupertype,
|
|
fields: [field, field]);
|
|
});
|
|
negativeTest('Class redeclared', () {
|
|
return otherClass; // Test harness also adds otherClass to program.
|
|
});
|
|
negativeTest('Class type parameter redeclared', () {
|
|
var parameter = makeTypeParameter();
|
|
return new Class(
|
|
name: 'Test',
|
|
supertype: objectClass.asRawSupertype,
|
|
typeParameters: [parameter, parameter]);
|
|
});
|
|
negativeTest('Member type parameter redeclared', () {
|
|
var parameter = makeTypeParameter();
|
|
return new Procedure(
|
|
new Name('test'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new ReturnStatement(new NullLiteral()),
|
|
typeParameters: [parameter, parameter]));
|
|
});
|
|
negativeTest('Type parameter out of scope', () {
|
|
var parameter = makeTypeParameter();
|
|
return new ListLiteral([], typeArgument: new TypeParameterType(parameter));
|
|
});
|
|
negativeTest('Class type parameter from another class', () {
|
|
return new TypeLiteral(new TypeParameterType(otherClass.typeParameters[0]));
|
|
});
|
|
negativeTest('Class type parameter in static method', () {
|
|
return new Procedure(
|
|
new Name('test'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new ReturnStatement(
|
|
new TypeLiteral(new TypeParameterType(classTypeParameter)))),
|
|
isStatic: true);
|
|
});
|
|
negativeTest('Class type parameter in static field', () {
|
|
return new Field(new Name('field'),
|
|
initializer: new TypeLiteral(new TypeParameterType(classTypeParameter)),
|
|
isStatic: true);
|
|
});
|
|
negativeTest('Method type parameter out of scope', () {
|
|
var parameter = makeTypeParameter();
|
|
return new Class(
|
|
name: 'Test',
|
|
supertype: objectClass.asRawSupertype,
|
|
procedures: [
|
|
new Procedure(
|
|
new Name('generic'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new EmptyStatement(),
|
|
typeParameters: [parameter])),
|
|
new Procedure(
|
|
new Name('use'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(new ReturnStatement(
|
|
new TypeLiteral(new TypeParameterType(parameter)))))
|
|
]);
|
|
});
|
|
negativeTest('Interface type arity too low', () {
|
|
return new TypeLiteral(new InterfaceType(otherClass, []));
|
|
});
|
|
negativeTest('Interface type arity too high', () {
|
|
return new TypeLiteral(
|
|
new InterfaceType(otherClass, [new DynamicType(), new DynamicType()]));
|
|
});
|
|
negativeTest('Dangling interface type', () {
|
|
return new TypeLiteral(new InterfaceType(new Class()));
|
|
});
|
|
negativeTest('Dangling field get', () {
|
|
return new DirectPropertyGet(new NullLiteral(), new Field(new Name('foo')));
|
|
});
|
|
negativeTest('Missing block parent pointer', () {
|
|
var block = new Block([]);
|
|
block.statements.add(new ReturnStatement());
|
|
return block;
|
|
});
|
|
negativeTest('Missing function parent pointer', () {
|
|
var procedure = new Procedure(new Name('test'), ProcedureKind.Method, null);
|
|
procedure.function = new FunctionNode(new EmptyStatement());
|
|
return procedure;
|
|
});
|
|
negativeTest('StaticGet without target', () {
|
|
return new StaticGet(null);
|
|
});
|
|
negativeTest('StaticSet without target', () {
|
|
return new StaticSet(null, new NullLiteral());
|
|
});
|
|
negativeTest('StaticInvocation without target', () {
|
|
return new StaticInvocation(null, new Arguments.empty());
|
|
});
|
|
positiveTest('Correct StaticInvocation', () {
|
|
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
|
|
isStatic: true);
|
|
method.function = new FunctionNode(
|
|
new ReturnStatement(
|
|
new StaticInvocation(method, new Arguments([new NullLiteral()]))),
|
|
positionalParameters: [new VariableDeclaration('p')])..parent = method;
|
|
return new Class(
|
|
name: 'Test',
|
|
supertype: objectClass.asRawSupertype,
|
|
procedures: [method]);
|
|
});
|
|
negativeTest('StaticInvocation with too many parameters', () {
|
|
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
|
|
isStatic: true);
|
|
method.function = new FunctionNode(new ReturnStatement(
|
|
new StaticInvocation(method, new Arguments([new NullLiteral()]))))
|
|
..parent = method;
|
|
return new Class(
|
|
name: 'Test',
|
|
supertype: objectClass.asRawSupertype,
|
|
procedures: [method]);
|
|
});
|
|
negativeTest('StaticInvocation with too few parameters', () {
|
|
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
|
|
isStatic: true);
|
|
method.function = new FunctionNode(
|
|
new ReturnStatement(
|
|
new StaticInvocation(method, new Arguments.empty())),
|
|
positionalParameters: [new VariableDeclaration('p')])..parent = method;
|
|
return new Class(
|
|
name: 'Test',
|
|
supertype: objectClass.asRawSupertype,
|
|
procedures: [method]);
|
|
});
|
|
negativeTest('StaticInvocation with unmatched named parameter', () {
|
|
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
|
|
isStatic: true);
|
|
method.function = new FunctionNode(new ReturnStatement(new StaticInvocation(
|
|
method,
|
|
new Arguments([],
|
|
named: [new NamedExpression('p', new NullLiteral())]))))
|
|
..parent = method;
|
|
return new Class(
|
|
name: 'Test',
|
|
supertype: objectClass.asRawSupertype,
|
|
procedures: [method]);
|
|
});
|
|
negativeTest('StaticInvocation with missing type argument', () {
|
|
var method = new Procedure(new Name('test'), ProcedureKind.Method, null,
|
|
isStatic: true);
|
|
method.function = new FunctionNode(
|
|
new ReturnStatement(
|
|
new StaticInvocation(method, new Arguments.empty())),
|
|
typeParameters: [makeTypeParameter()])..parent = method;
|
|
return new Class(
|
|
name: 'Test',
|
|
supertype: objectClass.asRawSupertype,
|
|
procedures: [method]);
|
|
});
|
|
negativeTest('ConstructorInvocation with missing type argument', () {
|
|
var constructor = new Constructor(null);
|
|
constructor.function = new FunctionNode(new ReturnStatement(
|
|
new ConstructorInvocation(constructor, new Arguments.empty())))
|
|
..parent = constructor;
|
|
return new Class(
|
|
name: 'Test',
|
|
typeParameters: [makeTypeParameter()],
|
|
supertype: objectClass.asRawSupertype,
|
|
constructors: [constructor]);
|
|
});
|
|
}
|
|
|
|
checkHasError(Program program) {
|
|
bool passed = false;
|
|
try {
|
|
verifyProgram(program);
|
|
passed = true;
|
|
} catch (e) {}
|
|
if (passed) {
|
|
fail('Failed to reject invalid program:\n${programToString(program)}');
|
|
}
|
|
}
|
|
|
|
Class objectClass = new Class(name: 'Object');
|
|
|
|
Library stubLibrary = new Library(Uri.parse('dart:core'))
|
|
..addClass(objectClass);
|
|
|
|
TypeParameter classTypeParameter = makeTypeParameter('T');
|
|
|
|
Class otherClass = new Class(
|
|
name: 'OtherClass',
|
|
typeParameters: [makeTypeParameter('OtherT')],
|
|
supertype: objectClass.asRawSupertype);
|
|
|
|
Program makeProgram(TreeNode makeBody()) {
|
|
var node = makeBody();
|
|
if (node is Expression) {
|
|
node = new ReturnStatement(node);
|
|
}
|
|
if (node is Statement) {
|
|
node = new FunctionNode(node);
|
|
}
|
|
if (node is FunctionNode) {
|
|
node = new Procedure(new Name('test'), ProcedureKind.Method, node);
|
|
}
|
|
if (node is Member) {
|
|
node = new Class(
|
|
name: 'Test',
|
|
typeParameters: [classTypeParameter],
|
|
supertype: objectClass.asRawSupertype)..addMember(node);
|
|
}
|
|
if (node is Class) {
|
|
node =
|
|
new Library(Uri.parse('test.dart'), classes: <Class>[node, otherClass]);
|
|
}
|
|
if (node is Library) {
|
|
node = new Program(<Library>[node, stubLibrary]);
|
|
}
|
|
assert(node is Program);
|
|
return node;
|
|
}
|
|
|
|
negativeTest(String name, TreeNode makeBody()) {
|
|
test(name, () {
|
|
checkHasError(makeProgram(makeBody));
|
|
});
|
|
}
|
|
|
|
positiveTest(String name, TreeNode makeBody()) {
|
|
test(name, () {
|
|
verifyProgram(makeProgram(makeBody));
|
|
});
|
|
}
|
|
|
|
VariableDeclaration makeVariable() => new VariableDeclaration(null);
|
|
|
|
TypeParameter makeTypeParameter([String name]) {
|
|
return new TypeParameter(name, new InterfaceType(objectClass));
|
|
}
|