blob: 11f54f97ac38ab2d4d5f41ad7823dc44016797ce [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 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));
}