| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 import 'package:kernel/ast.dart'; | 4 import 'package:kernel/ast.dart'; |
| 5 import 'package:kernel/text/ast_to_text.dart'; | 5 import 'package:kernel/text/ast_to_text.dart'; |
| 6 import 'package:kernel/verifier.dart'; | 6 import 'package:kernel/verifier.dart'; |
| 7 import 'package:test/test.dart'; | 7 import 'package:test/test.dart'; |
| 8 | 8 |
| 9 /// Checks that the verifier correctly find errors in invalid programs. | 9 /// Checks that the verifier correctly find errors in invalid programs. |
| 10 /// | 10 /// |
| 11 /// The frontend should never generate invalid programs, so we have to test | 11 /// The frontend should never generate invalid programs, so we have to test |
| 12 /// these by manually constructing invalid ASTs. | 12 /// these by manually constructing invalid ASTs. |
| 13 /// | 13 /// |
| 14 /// We mostly test negative cases here, as we get plenty of positive cases by | 14 /// We mostly test negative cases here, as we get plenty of positive cases by |
| 15 /// compiling the Dart test suite with the verifier enabled. | 15 /// compiling the Dart test suite with the verifier enabled. |
| 16 main() { | 16 main() { |
| 17 positiveTest('Test harness has no errors', () { | 17 positiveTest('Test harness has no errors', (TestHarness test) { |
| 18 return new NullLiteral(); | 18 return new NullLiteral(); |
| 19 }); | 19 }); |
| 20 negativeTest('VariableGet out of scope', () { | 20 negativeTest('VariableGet out of scope', (TestHarness test) { |
| 21 return new VariableGet(makeVariable()); | 21 return new VariableGet(test.makeVariable()); |
| 22 }); | 22 }); |
| 23 negativeTest('VariableSet out of scope', () { | 23 negativeTest('VariableSet out of scope', (TestHarness test) { |
| 24 return new VariableSet(makeVariable(), new NullLiteral()); | 24 return new VariableSet(test.makeVariable(), new NullLiteral()); |
| 25 }); | 25 }); |
| 26 negativeTest('Variable block scope', () { | 26 negativeTest('Variable block scope', (TestHarness test) { |
| 27 VariableDeclaration variable = makeVariable(); | 27 VariableDeclaration variable = test.makeVariable(); |
| 28 return new Block([ | 28 return new Block([ |
| 29 new Block([variable]), | 29 new Block([variable]), |
| 30 new ReturnStatement(new VariableGet(variable)) | 30 new ReturnStatement(new VariableGet(variable)) |
| 31 ]); | 31 ]); |
| 32 }); | 32 }); |
| 33 negativeTest('Variable let scope', () { | 33 negativeTest('Variable let scope', (TestHarness test) { |
| 34 VariableDeclaration variable = makeVariable(); | 34 VariableDeclaration variable = test.makeVariable(); |
| 35 return new LogicalExpression(new Let(variable, new VariableGet(variable)), | 35 return new LogicalExpression(new Let(variable, new VariableGet(variable)), |
| 36 '&&', new VariableGet(variable)); | 36 '&&', new VariableGet(variable)); |
| 37 }); | 37 }); |
| 38 negativeTest('Variable redeclared', () { | 38 negativeTest('Variable redeclared', (TestHarness test) { |
| 39 VariableDeclaration variable = makeVariable(); | 39 VariableDeclaration variable = test.makeVariable(); |
| 40 return new Block([variable, variable]); | 40 return new Block([variable, variable]); |
| 41 }); | 41 }); |
| 42 negativeTest('Member redeclared', () { | 42 negativeTest('Member redeclared', (TestHarness test) { |
| 43 Field field = new Field(new Name('field'), initializer: new NullLiteral()); | 43 Field field = new Field(new Name('field'), initializer: new NullLiteral()); |
| 44 return new Class( | 44 return new Class( |
| 45 name: 'Test', | 45 name: 'Test', |
| 46 supertype: objectClass.asRawSupertype, | 46 supertype: test.objectClass.asRawSupertype, |
| 47 fields: [field, field]); | 47 fields: [field, field]); |
| 48 }); | 48 }); |
| 49 negativeTest('Class redeclared', () { | 49 negativeTest('Class redeclared', (TestHarness test) { |
| 50 return otherClass; // Test harness also adds otherClass to program. | 50 return test.otherClass; // Test harness also adds otherClass to program. |
| 51 }); | 51 }); |
| 52 negativeTest('Class type parameter redeclared', () { | 52 negativeTest('Class type parameter redeclared', (TestHarness test) { |
| 53 var parameter = makeTypeParameter(); | 53 var parameter = test.makeTypeParameter(); |
| 54 return new Class( | 54 return new Class( |
| 55 name: 'Test', | 55 name: 'Test', |
| 56 supertype: objectClass.asRawSupertype, | 56 supertype: test.objectClass.asRawSupertype, |
| 57 typeParameters: [parameter, parameter]); | 57 typeParameters: [parameter, parameter]); |
| 58 }); | 58 }); |
| 59 negativeTest('Member type parameter redeclared', () { | 59 negativeTest('Member type parameter redeclared', (TestHarness test) { |
| 60 var parameter = makeTypeParameter(); | 60 var parameter = test.makeTypeParameter(); |
| 61 return new Procedure( | 61 return new Procedure( |
| 62 new Name('test'), | 62 new Name('bar'), |
| 63 ProcedureKind.Method, | 63 ProcedureKind.Method, |
| 64 new FunctionNode(new ReturnStatement(new NullLiteral()), | 64 new FunctionNode(new ReturnStatement(new NullLiteral()), |
| 65 typeParameters: [parameter, parameter])); | 65 typeParameters: [parameter, parameter])); |
| 66 }); | 66 }); |
| 67 negativeTest('Type parameter out of scope', () { | 67 negativeTest('Type parameter out of scope', (TestHarness test) { |
| 68 var parameter = makeTypeParameter(); | 68 var parameter = test.makeTypeParameter(); |
| 69 return new ListLiteral([], typeArgument: new TypeParameterType(parameter)); | 69 return new ListLiteral([], typeArgument: new TypeParameterType(parameter)); |
| 70 }); | 70 }); |
| 71 negativeTest('Class type parameter from another class', () { | 71 negativeTest('Class type parameter from another class', (TestHarness test) { |
| 72 return new TypeLiteral(new TypeParameterType(otherClass.typeParameters[0])); | 72 return new TypeLiteral( |
| 73 new TypeParameterType(test.otherClass.typeParameters[0])); |
| 73 }); | 74 }); |
| 74 negativeTest('Class type parameter in static method', () { | 75 negativeTest('Class type parameter in static method', (TestHarness test) { |
| 75 return new Procedure( | 76 return new Procedure( |
| 76 new Name('test'), | 77 new Name('bar'), |
| 77 ProcedureKind.Method, | 78 ProcedureKind.Method, |
| 78 new FunctionNode(new ReturnStatement( | 79 new FunctionNode(new ReturnStatement( |
| 79 new TypeLiteral(new TypeParameterType(classTypeParameter)))), | 80 new TypeLiteral(new TypeParameterType(test.classTypeParameter)))), |
| 80 isStatic: true); | 81 isStatic: true); |
| 81 }); | 82 }); |
| 82 negativeTest('Class type parameter in static field', () { | 83 negativeTest('Class type parameter in static field', (TestHarness test) { |
| 83 return new Field(new Name('field'), | 84 return new Field(new Name('field'), |
| 84 initializer: new TypeLiteral(new TypeParameterType(classTypeParameter)), | 85 initializer: |
| 86 new TypeLiteral(new TypeParameterType(test.classTypeParameter)), |
| 85 isStatic: true); | 87 isStatic: true); |
| 86 }); | 88 }); |
| 87 negativeTest('Method type parameter out of scope', () { | 89 negativeTest('Method type parameter out of scope', (TestHarness test) { |
| 88 var parameter = makeTypeParameter(); | 90 var parameter = test.makeTypeParameter(); |
| 89 return new Class( | 91 return new Class( |
| 90 name: 'Test', | 92 name: 'Test', |
| 91 supertype: objectClass.asRawSupertype, | 93 supertype: test.objectClass.asRawSupertype, |
| 92 procedures: [ | 94 procedures: [ |
| 93 new Procedure( | 95 new Procedure( |
| 94 new Name('generic'), | 96 new Name('generic'), |
| 95 ProcedureKind.Method, | 97 ProcedureKind.Method, |
| 96 new FunctionNode(new EmptyStatement(), | 98 new FunctionNode(new EmptyStatement(), |
| 97 typeParameters: [parameter])), | 99 typeParameters: [parameter])), |
| 98 new Procedure( | 100 new Procedure( |
| 99 new Name('use'), | 101 new Name('use'), |
| 100 ProcedureKind.Method, | 102 ProcedureKind.Method, |
| 101 new FunctionNode(new ReturnStatement( | 103 new FunctionNode(new ReturnStatement( |
| 102 new TypeLiteral(new TypeParameterType(parameter))))) | 104 new TypeLiteral(new TypeParameterType(parameter))))) |
| 103 ]); | 105 ]); |
| 104 }); | 106 }); |
| 105 negativeTest('Interface type arity too low', () { | 107 negativeTest('Interface type arity too low', (TestHarness test) { |
| 106 return new TypeLiteral(new InterfaceType(otherClass, [])); | 108 return new TypeLiteral(new InterfaceType(test.otherClass, [])); |
| 107 }); | 109 }); |
| 108 negativeTest('Interface type arity too high', () { | 110 negativeTest('Interface type arity too high', (TestHarness test) { |
| 109 return new TypeLiteral( | 111 return new TypeLiteral(new InterfaceType( |
| 110 new InterfaceType(otherClass, [new DynamicType(), new DynamicType()])); | 112 test.otherClass, [new DynamicType(), new DynamicType()])); |
| 111 }); | 113 }); |
| 112 negativeTest('Dangling interface type', () { | 114 negativeTest('Dangling interface type', (TestHarness test) { |
| 113 return new TypeLiteral(new InterfaceType(new Class())); | 115 var orphan = new Class(); |
| 116 test.enclosingLibrary.canonicalName.getChild('foo').linkTo(orphan); |
| 117 return new TypeLiteral(new InterfaceType(orphan)); |
| 114 }); | 118 }); |
| 115 negativeTest('Dangling field get', () { | 119 negativeTest('Dangling field get', (TestHarness test) { |
| 116 return new DirectPropertyGet(new NullLiteral(), new Field(new Name('foo'))); | 120 var orphan = new Field(new Name('foo')); |
| 121 test.enclosingLibrary.canonicalName.getChild('foo').linkTo(orphan); |
| 122 return new DirectPropertyGet(new NullLiteral(), orphan); |
| 117 }); | 123 }); |
| 118 negativeTest('Missing block parent pointer', () { | 124 negativeTest('Missing block parent pointer', (TestHarness test) { |
| 119 var block = new Block([]); | 125 var block = new Block([]); |
| 120 block.statements.add(new ReturnStatement()); | 126 block.statements.add(new ReturnStatement()); |
| 121 return block; | 127 return block; |
| 122 }); | 128 }); |
| 123 negativeTest('Missing function parent pointer', () { | 129 negativeTest('Missing function parent pointer', (TestHarness test) { |
| 124 var procedure = new Procedure(new Name('test'), ProcedureKind.Method, null); | 130 var procedure = new Procedure(new Name('bar'), ProcedureKind.Method, null); |
| 125 procedure.function = new FunctionNode(new EmptyStatement()); | 131 procedure.function = new FunctionNode(new EmptyStatement()); |
| 126 return procedure; | 132 return procedure; |
| 127 }); | 133 }); |
| 128 negativeTest('StaticGet without target', () { | 134 negativeTest('StaticGet without target', (TestHarness test) { |
| 129 return new StaticGet(null); | 135 return new StaticGet(null); |
| 130 }); | 136 }); |
| 131 negativeTest('StaticSet without target', () { | 137 negativeTest('StaticSet without target', (TestHarness test) { |
| 132 return new StaticSet(null, new NullLiteral()); | 138 return new StaticSet(null, new NullLiteral()); |
| 133 }); | 139 }); |
| 134 negativeTest('StaticInvocation without target', () { | 140 negativeTest('StaticInvocation without target', (TestHarness test) { |
| 135 return new StaticInvocation(null, new Arguments.empty()); | 141 return new StaticInvocation(null, new Arguments.empty()); |
| 136 }); | 142 }); |
| 137 positiveTest('Correct StaticInvocation', () { | 143 positiveTest('Correct StaticInvocation', (TestHarness test) { |
| 138 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 144 var method = new Procedure( |
| 145 new Name('foo'), |
| 146 ProcedureKind.Method, |
| 147 new FunctionNode(new EmptyStatement(), |
| 148 positionalParameters: [new VariableDeclaration('p')]), |
| 139 isStatic: true); | 149 isStatic: true); |
| 140 method.function = new FunctionNode( | 150 test.enclosingClass.addMember(method); |
| 141 new ReturnStatement( | 151 return new StaticInvocation(method, new Arguments([new NullLiteral()])); |
| 142 new StaticInvocation(method, new Arguments([new NullLiteral()]))), | |
| 143 positionalParameters: [new VariableDeclaration('p')])..parent = method; | |
| 144 return new Class( | |
| 145 name: 'Test', | |
| 146 supertype: objectClass.asRawSupertype, | |
| 147 procedures: [method]); | |
| 148 }); | 152 }); |
| 149 negativeTest('StaticInvocation with too many parameters', () { | 153 negativeTest('StaticInvocation with too many parameters', (TestHarness test) { |
| 150 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 154 var method = new Procedure(new Name('bar'), ProcedureKind.Method, |
| 155 new FunctionNode(new EmptyStatement()), |
| 151 isStatic: true); | 156 isStatic: true); |
| 152 method.function = new FunctionNode(new ReturnStatement( | 157 test.enclosingClass.addMember(method); |
| 153 new StaticInvocation(method, new Arguments([new NullLiteral()])))) | 158 return new StaticInvocation(method, new Arguments([new NullLiteral()])); |
| 154 ..parent = method; | |
| 155 return new Class( | |
| 156 name: 'Test', | |
| 157 supertype: objectClass.asRawSupertype, | |
| 158 procedures: [method]); | |
| 159 }); | 159 }); |
| 160 negativeTest('StaticInvocation with too few parameters', () { | 160 negativeTest('StaticInvocation with too few parameters', (TestHarness test) { |
| 161 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 161 var method = new Procedure( |
| 162 new Name('bar'), |
| 163 ProcedureKind.Method, |
| 164 new FunctionNode(new EmptyStatement(), |
| 165 positionalParameters: [new VariableDeclaration('p')]), |
| 162 isStatic: true); | 166 isStatic: true); |
| 163 method.function = new FunctionNode( | 167 test.enclosingClass.addMember(method); |
| 164 new ReturnStatement( | 168 return new StaticInvocation(method, new Arguments.empty()); |
| 165 new StaticInvocation(method, new Arguments.empty())), | |
| 166 positionalParameters: [new VariableDeclaration('p')])..parent = method; | |
| 167 return new Class( | |
| 168 name: 'Test', | |
| 169 supertype: objectClass.asRawSupertype, | |
| 170 procedures: [method]); | |
| 171 }); | 169 }); |
| 172 negativeTest('StaticInvocation with unmatched named parameter', () { | 170 negativeTest('StaticInvocation with unmatched named parameter', |
| 173 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 171 (TestHarness test) { |
| 172 var method = new Procedure(new Name('bar'), ProcedureKind.Method, |
| 173 new FunctionNode(new EmptyStatement()), |
| 174 isStatic: true); | 174 isStatic: true); |
| 175 method.function = new FunctionNode(new ReturnStatement(new StaticInvocation( | 175 test.enclosingClass.addMember(method); |
| 176 return new StaticInvocation( |
| 176 method, | 177 method, |
| 177 new Arguments([], | 178 new Arguments([], |
| 178 named: [new NamedExpression('p', new NullLiteral())])))) | 179 named: [new NamedExpression('p', new NullLiteral())])); |
| 179 ..parent = method; | 180 }); |
| 180 return new Class( | 181 negativeTest('StaticInvocation with missing type argument', |
| 182 (TestHarness test) { |
| 183 var method = new Procedure( |
| 184 new Name('bar'), |
| 185 ProcedureKind.Method, |
| 186 new FunctionNode(new EmptyStatement(), |
| 187 typeParameters: [test.makeTypeParameter()]), |
| 188 isStatic: true); |
| 189 test.enclosingClass.addMember(method); |
| 190 return new StaticInvocation(method, new Arguments.empty()); |
| 191 }); |
| 192 negativeTest('ConstructorInvocation with missing type argument', |
| 193 (TestHarness test) { |
| 194 var class_ = new Class( |
| 181 name: 'Test', | 195 name: 'Test', |
| 182 supertype: objectClass.asRawSupertype, | 196 typeParameters: [test.makeTypeParameter()], |
| 183 procedures: [method]); | 197 supertype: test.objectClass.asRawSupertype); |
| 184 }); | 198 test.enclosingLibrary.addClass(class_); |
| 185 negativeTest('StaticInvocation with missing type argument', () { | 199 var constructor = new Constructor(new FunctionNode(new EmptyStatement()), |
| 186 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 200 name: new Name('foo')); |
| 187 isStatic: true); | 201 test.enclosingClass.addMember(constructor); |
| 188 method.function = new FunctionNode( | 202 return new ConstructorInvocation(constructor, new Arguments.empty()); |
| 189 new ReturnStatement( | |
| 190 new StaticInvocation(method, new Arguments.empty())), | |
| 191 typeParameters: [makeTypeParameter()])..parent = method; | |
| 192 return new Class( | |
| 193 name: 'Test', | |
| 194 supertype: objectClass.asRawSupertype, | |
| 195 procedures: [method]); | |
| 196 }); | |
| 197 negativeTest('ConstructorInvocation with missing type argument', () { | |
| 198 var constructor = new Constructor(null); | |
| 199 constructor.function = new FunctionNode(new ReturnStatement( | |
| 200 new ConstructorInvocation(constructor, new Arguments.empty()))) | |
| 201 ..parent = constructor; | |
| 202 return new Class( | |
| 203 name: 'Test', | |
| 204 typeParameters: [makeTypeParameter()], | |
| 205 supertype: objectClass.asRawSupertype, | |
| 206 constructors: [constructor]); | |
| 207 }); | 203 }); |
| 208 } | 204 } |
| 209 | 205 |
| 210 checkHasError(Program program) { | 206 checkHasError(Program program) { |
| 211 bool passed = false; | 207 bool passed = false; |
| 212 try { | 208 try { |
| 213 verifyProgram(program); | 209 verifyProgram(program); |
| 214 passed = true; | 210 passed = true; |
| 215 } catch (e) {} | 211 } catch (e) {} |
| 216 if (passed) { | 212 if (passed) { |
| 217 fail('Failed to reject invalid program:\n${programToString(program)}'); | 213 fail('Failed to reject invalid program:\n${programToString(program)}'); |
| 218 } | 214 } |
| 219 } | 215 } |
| 220 | 216 |
| 221 Class objectClass = new Class(name: 'Object'); | 217 class TestHarness { |
| 218 void addNode(TreeNode node) { |
| 219 if (node is Expression) { |
| 220 addExpression(node); |
| 221 } else if (node is Statement) { |
| 222 addStatement(node); |
| 223 } else if (node is Member) { |
| 224 addClassMember(node); |
| 225 } else if (node is Class) { |
| 226 addClass(node); |
| 227 } |
| 228 } |
| 222 | 229 |
| 223 Library stubLibrary = new Library(Uri.parse('dart:core')) | 230 void addExpression(Expression node) { |
| 224 ..addClass(objectClass); | 231 addStatement(new ReturnStatement(node)); |
| 232 } |
| 225 | 233 |
| 226 TypeParameter classTypeParameter = makeTypeParameter('T'); | 234 void addStatement(Statement node) { |
| 235 var function = enclosingMember.function; |
| 236 function.body = node..parent = function; |
| 237 } |
| 227 | 238 |
| 228 Class otherClass = new Class( | 239 void addClassMember(Member node) { |
| 229 name: 'OtherClass', | 240 enclosingClass.addMember(node); |
| 230 typeParameters: [makeTypeParameter('OtherT')], | 241 } |
| 231 supertype: objectClass.asRawSupertype); | |
| 232 | 242 |
| 233 Program makeProgram(TreeNode makeBody()) { | 243 void addTopLevelMember(Member node) { |
| 234 var node = makeBody(); | 244 enclosingLibrary.addMember(node); |
| 235 if (node is Expression) { | |
| 236 node = new ReturnStatement(node); | |
| 237 } | 245 } |
| 238 if (node is Statement) { | 246 |
| 239 node = new FunctionNode(node); | 247 void addClass(Class node) { |
| 248 enclosingLibrary.addClass(node); |
| 240 } | 249 } |
| 241 if (node is FunctionNode) { | 250 |
| 242 node = new Procedure(new Name('test'), ProcedureKind.Method, node); | 251 VariableDeclaration makeVariable() => new VariableDeclaration(null); |
| 252 |
| 253 TypeParameter makeTypeParameter([String name]) { |
| 254 return new TypeParameter(name, new InterfaceType(objectClass)); |
| 243 } | 255 } |
| 244 if (node is Member) { | 256 |
| 245 node = new Class( | 257 Program program; |
| 246 name: 'Test', | 258 Class objectClass; |
| 259 Library stubLibrary; |
| 260 |
| 261 TypeParameter classTypeParameter; |
| 262 |
| 263 Library enclosingLibrary; |
| 264 Class enclosingClass; |
| 265 Procedure enclosingMember; |
| 266 |
| 267 Class otherClass; |
| 268 |
| 269 TestHarness() { |
| 270 setupProgram(); |
| 271 } |
| 272 |
| 273 void setupProgram() { |
| 274 program = new Program(); |
| 275 stubLibrary = program.getLibraryReference(Uri.parse('dart:core')); |
| 276 stubLibrary.name = 'dart.core'; |
| 277 objectClass = new Class(name: 'Object'); |
| 278 stubLibrary.addClass(objectClass); |
| 279 enclosingLibrary = |
| 280 program.getLibraryReference(Uri.parse('file://test.dart')); |
| 281 enclosingLibrary.name = 'test_lib'; |
| 282 classTypeParameter = makeTypeParameter('T'); |
| 283 enclosingClass = new Class( |
| 284 name: 'TestClass', |
| 247 typeParameters: [classTypeParameter], | 285 typeParameters: [classTypeParameter], |
| 248 supertype: objectClass.asRawSupertype)..addMember(node); | 286 supertype: objectClass.asRawSupertype); |
| 287 enclosingLibrary.addClass(enclosingClass); |
| 288 enclosingMember = new Procedure(new Name('test'), ProcedureKind.Method, |
| 289 new FunctionNode(new EmptyStatement())); |
| 290 enclosingClass.addMember(enclosingMember); |
| 291 otherClass = new Class( |
| 292 name: 'OtherClass', |
| 293 typeParameters: [makeTypeParameter('OtherT')], |
| 294 supertype: objectClass.asRawSupertype); |
| 295 enclosingLibrary.addClass(otherClass); |
| 249 } | 296 } |
| 250 if (node is Class) { | |
| 251 node = | |
| 252 new Library(Uri.parse('test.dart'), classes: <Class>[node, otherClass]); | |
| 253 } | |
| 254 if (node is Library) { | |
| 255 node = new Program(<Library>[node, stubLibrary]); | |
| 256 } | |
| 257 assert(node is Program); | |
| 258 return node; | |
| 259 } | 297 } |
| 260 | 298 |
| 261 negativeTest(String name, TreeNode makeBody()) { | 299 negativeTest(String name, TreeNode makeTestCase(TestHarness test)) { |
| 262 test(name, () { | 300 test(name, () { |
| 263 checkHasError(makeProgram(makeBody)); | 301 var test = new TestHarness(); |
| 302 test.addNode(makeTestCase(test)); |
| 303 checkHasError(test.program); |
| 264 }); | 304 }); |
| 265 } | 305 } |
| 266 | 306 |
| 267 positiveTest(String name, TreeNode makeBody()) { | 307 positiveTest(String name, TreeNode makeTestCase(TestHarness test)) { |
| 268 test(name, () { | 308 test(name, () { |
| 269 verifyProgram(makeProgram(makeBody)); | 309 var test = new TestHarness(); |
| 310 test.addNode(makeTestCase(test)); |
| 311 verifyProgram(test.program); |
| 270 }); | 312 }); |
| 271 } | 313 } |
| 272 | |
| 273 VariableDeclaration makeVariable() => new VariableDeclaration(null); | |
| 274 | |
| 275 TypeParameter makeTypeParameter([String name]) { | |
| 276 return new TypeParameter(name, new InterfaceType(objectClass)); | |
| 277 } | |
| OLD | NEW |