Index: pkg/kernel/test/verify_test.dart |
diff --git a/pkg/kernel/test/verify_test.dart b/pkg/kernel/test/verify_test.dart |
index 3d4c51d67af167ad0c5d9308550d97073c4cad30..7c7c243c7ce0510686f17a473bddc0fdebbb1233 100644 |
--- a/pkg/kernel/test/verify_test.dart |
+++ b/pkg/kernel/test/verify_test.dart |
@@ -1,11 +1,16 @@ |
// 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'; |
+const String varRegexp = "#t[0-9]+"; |
+ |
+const String tvarRegexp = "#T[0-9]+"; |
+ |
/// Checks that the verifier correctly find errors in invalid programs. |
/// |
/// The frontend should never generate invalid programs, so we have to test |
@@ -17,46 +22,64 @@ main() { |
positiveTest('Test harness has no errors', (TestHarness test) { |
return new NullLiteral(); |
}); |
- negativeTest('VariableGet out of scope', (TestHarness test) { |
+ negativeTest('VariableGet out of scope', |
+ matches("Variable '$varRegexp' used out of scope\\."), |
+ (TestHarness test) { |
return new VariableGet(test.makeVariable()); |
}); |
- negativeTest('VariableSet out of scope', (TestHarness test) { |
+ negativeTest('VariableSet out of scope', |
+ matches("Variable '$varRegexp' used out of scope\\."), |
+ (TestHarness test) { |
return new VariableSet(test.makeVariable(), new NullLiteral()); |
}); |
- negativeTest('Variable block scope', (TestHarness test) { |
+ negativeTest('Variable block scope', |
+ matches("Variable '$varRegexp' used out of scope\\."), |
+ (TestHarness test) { |
VariableDeclaration variable = test.makeVariable(); |
return new Block([ |
new Block([variable]), |
new ReturnStatement(new VariableGet(variable)) |
]); |
}); |
- negativeTest('Variable let scope', (TestHarness test) { |
+ negativeTest('Variable let scope', |
+ matches("Variable '$varRegexp' used out of scope\\."), |
+ (TestHarness test) { |
VariableDeclaration variable = test.makeVariable(); |
return new LogicalExpression(new Let(variable, new VariableGet(variable)), |
'&&', new VariableGet(variable)); |
}); |
- negativeTest('Variable redeclared', (TestHarness test) { |
+ negativeTest('Variable redeclared', |
+ matches("Variable '$varRegexp' declared more than once\\."), |
+ (TestHarness test) { |
VariableDeclaration variable = test.makeVariable(); |
return new Block([variable, variable]); |
}); |
- negativeTest('Member redeclared', (TestHarness test) { |
+ negativeTest('Member redeclared', |
+ "Member 'test_lib::Test::field' has been declared more than once.", |
+ (TestHarness test) { |
Field field = new Field(new Name('field'), initializer: new NullLiteral()); |
return new Class( |
name: 'Test', |
supertype: test.objectClass.asRawSupertype, |
fields: [field, field]); |
}); |
- negativeTest('Class redeclared', (TestHarness test) { |
+ negativeTest('Class redeclared', |
+ "Class 'test_lib::OtherClass' declared more than once.", |
+ (TestHarness test) { |
return test.otherClass; // Test harness also adds otherClass to program. |
}); |
- negativeTest('Class type parameter redeclared', (TestHarness test) { |
+ negativeTest('Class type parameter redeclared', |
+ matches("Type parameter 'test_lib::Test::$tvarRegexp' redeclared\\."), |
+ (TestHarness test) { |
var parameter = test.makeTypeParameter(); |
return new Class( |
name: 'Test', |
supertype: test.objectClass.asRawSupertype, |
typeParameters: [parameter, parameter]); |
}); |
- negativeTest('Member type parameter redeclared', (TestHarness test) { |
+ negativeTest('Member type parameter redeclared', |
+ matches("Type parameter '$tvarRegexp' redeclared\\."), |
+ (TestHarness test) { |
var parameter = test.makeTypeParameter(); |
return new Procedure( |
new Name('bar'), |
@@ -64,15 +87,24 @@ main() { |
new FunctionNode(new ReturnStatement(new NullLiteral()), |
typeParameters: [parameter, parameter])); |
}); |
- negativeTest('Type parameter out of scope', (TestHarness test) { |
+ negativeTest( |
+ 'Type parameter out of scope', |
+ matches("Type parameter '$tvarRegexp' referenced out of scope," |
+ " parent is: 'null'\\."), (TestHarness test) { |
var parameter = test.makeTypeParameter(); |
return new ListLiteral([], typeArgument: new TypeParameterType(parameter)); |
}); |
- negativeTest('Class type parameter from another class', (TestHarness test) { |
+ negativeTest( |
+ 'Class type parameter from another class', |
+ "Type parameter 'test_lib::OtherClass::OtherT' referenced out of scope," |
+ " parent is: 'test_lib::OtherClass'.", (TestHarness test) { |
return new TypeLiteral( |
new TypeParameterType(test.otherClass.typeParameters[0])); |
}); |
- negativeTest('Class type parameter in static method', (TestHarness test) { |
+ negativeTest( |
+ 'Class type parameter in static method', |
+ "Type parameter 'test_lib::TestClass::T' referenced from static context," |
+ " parent is 'test_lib::TestClass'.", (TestHarness test) { |
return new Procedure( |
new Name('bar'), |
ProcedureKind.Method, |
@@ -80,13 +112,19 @@ main() { |
new TypeLiteral(new TypeParameterType(test.classTypeParameter)))), |
isStatic: true); |
}); |
- negativeTest('Class type parameter in static field', (TestHarness test) { |
+ negativeTest( |
+ 'Class type parameter in static field', |
+ "Type parameter 'test_lib::TestClass::T' referenced from static context," |
+ " parent is 'test_lib::TestClass'.", (TestHarness test) { |
return new Field(new Name('field'), |
initializer: |
new TypeLiteral(new TypeParameterType(test.classTypeParameter)), |
isStatic: true); |
}); |
- negativeTest('Method type parameter out of scope', (TestHarness test) { |
+ negativeTest( |
+ 'Method type parameter out of scope', |
+ matches("Type parameter '$tvarRegexp' referenced out of scope," |
+ " parent is: '<FunctionNode>'\\."), (TestHarness test) { |
var parameter = test.makeTypeParameter(); |
return new Class( |
name: 'Test', |
@@ -104,38 +142,59 @@ main() { |
new TypeLiteral(new TypeParameterType(parameter))))) |
]); |
}); |
- negativeTest('Interface type arity too low', (TestHarness test) { |
+ negativeTest( |
+ 'Interface type arity too low', |
+ "Type test_lib::OtherClass provides 0 type arguments" |
+ " but the class declares 1 parameters.", (TestHarness test) { |
return new TypeLiteral(new InterfaceType(test.otherClass, [])); |
}); |
- negativeTest('Interface type arity too high', (TestHarness test) { |
+ negativeTest( |
+ 'Interface type arity too high', |
+ "Type test_lib::OtherClass<dynamic, dynamic> provides 2 type arguments" |
+ " but the class declares 1 parameters.", (TestHarness test) { |
return new TypeLiteral(new InterfaceType( |
test.otherClass, [new DynamicType(), new DynamicType()])); |
}); |
- negativeTest('Dangling interface type', (TestHarness test) { |
+ negativeTest( |
+ 'Dangling interface type', |
+ matches("Dangling reference to 'null::#class[0-9]+'," |
+ " parent is: 'null'\\."), (TestHarness test) { |
var orphan = new Class(); |
return new TypeLiteral(new InterfaceType(orphan)); |
}); |
- negativeTest('Dangling field get', (TestHarness test) { |
+ negativeTest('Dangling field get', |
+ "Dangling reference to 'null::foo', parent is: 'null'.", |
+ (TestHarness test) { |
var orphan = new Field(new Name('foo')); |
return new DirectPropertyGet(new NullLiteral(), orphan); |
}); |
- negativeTest('Missing block parent pointer', (TestHarness test) { |
+ negativeTest( |
+ '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()); |
return block; |
}); |
- negativeTest('Missing function parent pointer', (TestHarness test) { |
+ negativeTest( |
+ '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()); |
return procedure; |
}); |
- negativeTest('StaticGet without target', (TestHarness test) { |
+ negativeTest('StaticGet without target', "StaticGet without target.", |
+ (TestHarness test) { |
return new StaticGet(null); |
}); |
- negativeTest('StaticSet without target', (TestHarness test) { |
+ negativeTest('StaticSet without target', "StaticSet without target.", |
+ (TestHarness test) { |
return new StaticSet(null, new NullLiteral()); |
}); |
- negativeTest('StaticInvocation without target', (TestHarness test) { |
+ negativeTest( |
+ 'StaticInvocation without target', "StaticInvocation without target.", |
+ (TestHarness test) { |
return new StaticInvocation(null, new Arguments.empty()); |
}); |
positiveTest('Correct StaticInvocation', (TestHarness test) { |
@@ -148,14 +207,20 @@ main() { |
test.enclosingClass.addMember(method); |
return new StaticInvocation(method, new Arguments([new NullLiteral()])); |
}); |
- negativeTest('StaticInvocation with too many parameters', (TestHarness test) { |
+ negativeTest( |
+ 'StaticInvocation with too many parameters', |
+ "StaticInvocation with incompatible arguments for" |
+ " 'test_lib::TestClass::bar'.", (TestHarness test) { |
var method = new Procedure(new Name('bar'), ProcedureKind.Method, |
new FunctionNode(new EmptyStatement()), |
isStatic: true); |
test.enclosingClass.addMember(method); |
return new StaticInvocation(method, new Arguments([new NullLiteral()])); |
}); |
- negativeTest('StaticInvocation with too few parameters', (TestHarness test) { |
+ negativeTest( |
+ 'StaticInvocation with too few parameters', |
+ "StaticInvocation with incompatible arguments for" |
+ " 'test_lib::TestClass::bar'.", (TestHarness test) { |
var method = new Procedure( |
new Name('bar'), |
ProcedureKind.Method, |
@@ -165,8 +230,10 @@ main() { |
test.enclosingClass.addMember(method); |
return new StaticInvocation(method, new Arguments.empty()); |
}); |
- negativeTest('StaticInvocation with unmatched named parameter', |
- (TestHarness test) { |
+ negativeTest( |
+ 'StaticInvocation with unmatched named parameter', |
+ "StaticInvocation with incompatible arguments for" |
+ " 'test_lib::TestClass::bar'.", (TestHarness test) { |
var method = new Procedure(new Name('bar'), ProcedureKind.Method, |
new FunctionNode(new EmptyStatement()), |
isStatic: true); |
@@ -176,8 +243,10 @@ main() { |
new Arguments([], |
named: [new NamedExpression('p', new NullLiteral())])); |
}); |
- negativeTest('StaticInvocation with missing type argument', |
- (TestHarness test) { |
+ negativeTest( |
+ 'StaticInvocation with missing type argument', |
+ "StaticInvocation with wrong number of type arguments for" |
+ " 'test_lib::TestClass::bar'.", (TestHarness test) { |
var method = new Procedure( |
new Name('bar'), |
ProcedureKind.Method, |
@@ -187,8 +256,10 @@ main() { |
test.enclosingClass.addMember(method); |
return new StaticInvocation(method, new Arguments.empty()); |
}); |
- negativeTest('ConstructorInvocation with missing type argument', |
- (TestHarness test) { |
+ negativeTest( |
+ 'ConstructorInvocation with missing type argument', |
+ "ConstructorInvocation with wrong number of type arguments for" |
+ " 'test_lib::TestClass::foo'.", (TestHarness test) { |
var class_ = new Class( |
name: 'Test', |
typeParameters: [test.makeTypeParameter()], |
@@ -228,33 +299,49 @@ main() { |
positiveTest('Valid typedef type in field', (TestHarness test) { |
var typedef_ = new Typedef( |
'Foo', new FunctionType([test.otherClass.rawType], const VoidType())); |
- var field = new Field(new Name('field'), type: new TypedefType(typedef_)); |
+ var field = new Field(new Name('field'), |
+ type: new TypedefType(typedef_), isStatic: true); |
test.enclosingLibrary.addTypedef(typedef_); |
test.enclosingLibrary.addMember(field); |
}); |
- negativeTest('Invalid typedef Foo = Foo', (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedef Foo = Foo', |
+ "The typedef 'typedef Foo = test_lib::Foo;\n'" |
+ " refers to itself", (TestHarness test) { |
var typedef_ = new Typedef('Foo', null); |
typedef_.type = new TypedefType(typedef_); |
test.enclosingLibrary.addTypedef(typedef_); |
}); |
- negativeTest('Invalid typedef Foo = `(Foo) => void`', (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedef Foo = `(Foo) => void`', |
+ "The typedef 'typedef Foo = (test_lib::Foo) → void;\n'" |
+ " refers to itself", (TestHarness test) { |
var typedef_ = new Typedef('Foo', null); |
typedef_.type = |
new FunctionType([new TypedefType(typedef_)], const VoidType()); |
test.enclosingLibrary.addTypedef(typedef_); |
}); |
- negativeTest('Invalid typedef Foo = `() => Foo`', (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedef Foo = `() => Foo`', |
+ "The typedef 'typedef Foo = () → test_lib::Foo;\n'" |
+ " refers to itself", (TestHarness test) { |
var typedef_ = new Typedef('Foo', null); |
typedef_.type = new FunctionType([], new TypedefType(typedef_)); |
test.enclosingLibrary.addTypedef(typedef_); |
}); |
- negativeTest('Invalid typedef Foo = C<Foo>', (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedef Foo = C<Foo>', |
+ "The typedef 'typedef Foo = test_lib::OtherClass<test_lib::Foo>;\n'" |
+ " refers to itself", (TestHarness test) { |
var typedef_ = new Typedef('Foo', null); |
typedef_.type = |
new InterfaceType(test.otherClass, [new TypedefType(typedef_)]); |
test.enclosingLibrary.addTypedef(typedef_); |
}); |
- negativeTest('Invalid typedefs Foo = Bar, Bar = Foo', (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedefs Foo = Bar, Bar = Foo', |
+ "The typedef 'typedef Foo = test_lib::Bar;\n'" |
+ " refers to itself", (TestHarness test) { |
var foo = new Typedef('Foo', null); |
var bar = new Typedef('Bar', null); |
foo.type = new TypedefType(bar); |
@@ -262,7 +349,10 @@ main() { |
test.enclosingLibrary.addTypedef(foo); |
test.enclosingLibrary.addTypedef(bar); |
}); |
- negativeTest('Invalid typedefs Foo = Bar, Bar = C<Foo>', (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedefs Foo = Bar, Bar = C<Foo>', |
+ "The typedef 'typedef Foo = test_lib::Bar;\n'" |
+ " refers to itself", (TestHarness test) { |
var foo = new Typedef('Foo', null); |
var bar = new Typedef('Bar', null); |
foo.type = new TypedefType(bar); |
@@ -270,8 +360,10 @@ main() { |
test.enclosingLibrary.addTypedef(foo); |
test.enclosingLibrary.addTypedef(bar); |
}); |
- negativeTest('Invalid typedefs Foo = C<Bar>, Bar = C<Foo>', |
- (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedefs Foo = C<Bar>, Bar = C<Foo>', |
+ "The typedef 'typedef Foo = test_lib::OtherClass<test_lib::Bar>;\n'" |
+ " refers to itself", (TestHarness test) { |
var foo = new Typedef('Foo', null); |
var bar = new Typedef('Bar', null); |
foo.type = new InterfaceType(test.otherClass, [new TypedefType(bar)]); |
@@ -288,8 +380,10 @@ main() { |
test.enclosingLibrary.addTypedef(typedef_); |
} |
}); |
- negativeTest('Invalid long typedefs C20 = C19 = ... = C1 = C0 = C20', |
- (TestHarness test) { |
+ negativeTest( |
+ 'Invalid long typedefs C20 = C19 = ... = C1 = C0 = C20', |
+ "The typedef 'typedef C0 = test_lib::C19;\n'" |
+ " refers to itself", (TestHarness test) { |
var typedef_ = new Typedef('C0', null); |
test.enclosingLibrary.addTypedef(typedef_); |
var first = typedef_; |
@@ -328,8 +422,10 @@ main() { |
test.enclosingLibrary.addTypedef(foo); |
test.enclosingLibrary.addTypedef(bar); |
}); |
- negativeTest('Invalid typedefs Foo<T extends Bar<T>>, Bar<T extends Foo<T>>', |
- (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedefs Foo<T extends Bar<T>>, Bar<T extends Foo<T>>', |
+ "The typedef 'typedef Foo<T extends test_lib::Bar<T>> = dynamic;\n'" |
+ " refers to itself", (TestHarness test) { |
var fooParam = test.makeTypeParameter('T'); |
var foo = |
new Typedef('Foo', const DynamicType(), typeParameters: [fooParam]); |
@@ -342,8 +438,11 @@ main() { |
test.enclosingLibrary.addTypedef(foo); |
test.enclosingLibrary.addTypedef(bar); |
}); |
- negativeTest('Invalid typedef Foo<T extends Foo<dynamic> = C<T>', |
- (TestHarness test) { |
+ negativeTest( |
+ 'Invalid typedef Foo<T extends Foo<dynamic> = C<T>', |
+ "The typedef 'typedef Foo<T extends test_lib::Foo<dynamic>> = " |
+ "test_lib::OtherClass<T>;\n'" |
+ " refers to itself", (TestHarness test) { |
var param = new TypeParameter('T', null); |
var foo = new Typedef('Foo', |
new InterfaceType(test.otherClass, [new TypeParameterType(param)]), |
@@ -351,30 +450,42 @@ main() { |
param.bound = new TypedefType(foo, [const DynamicType()]); |
test.enclosingLibrary.addTypedef(foo); |
}); |
- negativeTest('Typedef arity error', (TestHarness test) { |
+ negativeTest( |
+ 'Typedef arity error', |
+ "The typedef type test_lib::Foo provides 0 type arguments" |
+ " but the typedef declares 1 parameters.", (TestHarness test) { |
var param = test.makeTypeParameter('T'); |
var foo = |
new Typedef('Foo', test.otherClass.rawType, typeParameters: [param]); |
- var field = new Field(new Name('field'), type: new TypedefType(foo, [])); |
+ var field = new Field(new Name('field'), |
+ type: new TypedefType(foo, []), isStatic: true); |
test.enclosingLibrary.addTypedef(foo); |
test.enclosingLibrary.addMember(field); |
}); |
- negativeTest('Dangling typedef reference', (TestHarness test) { |
+ negativeTest( |
+ 'Dangling typedef reference', |
+ "Dangling reference to 'typedef Foo = test_lib::OtherClass<dynamic>;\n'" |
+ ", parent is: 'null'", (TestHarness test) { |
var foo = new Typedef('Foo', test.otherClass.rawType, typeParameters: []); |
- var field = new Field(new Name('field'), type: new TypedefType(foo, [])); |
+ var field = new Field(new Name('field'), |
+ type: new TypedefType(foo, []), isStatic: true); |
+ test.enclosingLibrary.addMember(field); |
+ }); |
+ negativeTest('Non-static top-level field', |
+ "The top-level field 'field' should be static", (TestHarness test) { |
+ var field = new Field(new Name('field')); |
test.enclosingLibrary.addMember(field); |
}); |
} |
-checkHasError(Program program) { |
- bool passed = false; |
+checkHasError(Program program, Matcher matcher) { |
try { |
verifyProgram(program); |
- passed = true; |
- } catch (e) {} |
- if (passed) { |
- fail('Failed to reject invalid program:\n${programToString(program)}'); |
+ } on VerificationError catch (e) { |
+ expect(e.details, matcher); |
+ return; |
} |
+ fail('Failed to reject invalid program:\n${programToString(program)}'); |
} |
class TestHarness { |
@@ -460,11 +571,14 @@ class TestHarness { |
} |
} |
-negativeTest(String name, TreeNode makeTestCase(TestHarness test)) { |
+negativeTest(String name, matcher, TreeNode makeTestCase(TestHarness test)) { |
+ if (matcher is String) { |
+ matcher = equals(matcher); |
+ } |
test(name, () { |
var test = new TestHarness(); |
test.addNode(makeTestCase(test)); |
- checkHasError(test.program); |
+ checkHasError(test.program, matcher); |
}); |
} |