OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 #import("../../../lib/compiler/implementation/leg.dart"); | |
6 #import("../../../lib/compiler/implementation/elements/elements.dart"); | |
7 #import("../../../lib/compiler/implementation/tree/tree.dart"); | |
8 #import("../../../lib/compiler/implementation/util/util.dart"); | |
9 #import("mock_compiler.dart"); | |
10 #import("parser_helper.dart"); | |
11 | |
12 Node buildIdentifier(String name) => new Identifier(scan(name)); | |
13 | |
14 Node buildInitialization(String name) => | |
15 parseBodyCode('$name = 1', | |
16 (parser, tokens) => parser.parseOptionallyInitializedIdentifier(tokens)); | |
17 | |
18 createLocals(List variables) { | |
19 var locals = []; | |
20 for (final variable in variables) { | |
21 String name = variable[0]; | |
22 bool init = variable[1]; | |
23 if (init) { | |
24 locals.add(buildInitialization(name)); | |
25 } else { | |
26 locals.add(buildIdentifier(name)); | |
27 } | |
28 } | |
29 var definitions = new NodeList(null, new Link.fromList(locals), null, null); | |
30 return new VariableDefinitions(null, null, definitions, null); | |
31 } | |
32 | |
33 testLocals(List variables) { | |
34 MockCompiler compiler = new MockCompiler(); | |
35 ResolverVisitor visitor = compiler.resolverVisitor(); | |
36 Element element = visitor.visit(createLocals(variables)); | |
37 // A VariableDefinitions does not have an element. | |
38 Expect.equals(null, element); | |
39 Expect.equals(variables.length, map(visitor).length); | |
40 | |
41 for (final variable in variables) { | |
42 final name = variable[0]; | |
43 Identifier id = buildIdentifier(name); | |
44 final VariableElement variableElement = visitor.visit(id); | |
45 MethodScope scope = visitor.context; | |
46 Expect.equals(variableElement, scope.elements[buildSourceString(name)]); | |
47 } | |
48 return compiler; | |
49 } | |
50 | |
51 main() { | |
52 testLocalsOne(); | |
53 testLocalsTwo(); | |
54 testLocalsThree(); | |
55 testLocalsFour(); | |
56 testLocalsFive(); | |
57 testParametersOne(); | |
58 testFor(); | |
59 testTypeAnnotation(); | |
60 testSuperclass(); | |
61 // testVarSuperclass(); // The parser crashes with 'class Foo extends var'. | |
62 // testOneInterface(); // Generates unexpected error message. | |
63 // testTwoInterfaces(); // Generates unexpected error message. | |
64 testFunctionExpression(); | |
65 testNewExpression(); | |
66 testTopLevelFields(); | |
67 testClassHierarchy(); | |
68 testInitializers(); | |
69 testThis(); | |
70 testSuperCalls(); | |
71 testTypeVariables(); | |
72 } | |
73 | |
74 testTypeVariables() { | |
75 matchResolvedTypes(visitor, text, name, expectedElements) { | |
76 VariableDefinitions definition = parseStatement(text); | |
77 visitor.visit(definition.type); | |
78 InterfaceType type = visitor.mapping.getType(definition.type); | |
79 Expect.equals(definition.type.typeArguments.length(), | |
80 length(type.arguments)); | |
81 int index = 0; | |
82 Link<Type> arguments = type.arguments; | |
83 while (!arguments.isEmpty()) { | |
84 Expect.equals(true, index < expectedElements.length); | |
85 Expect.equals(expectedElements[index], arguments.head.element); | |
86 index++; | |
87 arguments = arguments.tail; | |
88 } | |
89 Expect.equals(index, expectedElements.length); | |
90 } | |
91 | |
92 MockCompiler compiler = new MockCompiler(); | |
93 ResolverVisitor visitor = compiler.resolverVisitor(); | |
94 compiler.parseScript('class Foo<T, U> {}'); | |
95 ClassElement foo = compiler.mainApp.find(buildSourceString('Foo')); | |
96 matchResolvedTypes(visitor, 'Foo<int, String> x;', 'Foo', | |
97 [compiler.intClass, compiler.stringClass]); | |
98 matchResolvedTypes(visitor, 'Foo<Foo, Foo> x;', 'Foo', | |
99 [foo, foo]); | |
100 | |
101 compiler = new MockCompiler(); | |
102 compiler.parseScript('class Foo<T, U> {}'); | |
103 compiler.resolveStatement('Foo<notype, int> x;'); | |
104 Expect.equals(1, compiler.warnings.length); | |
105 Expect.equals(MessageKind.CANNOT_RESOLVE_TYPE, | |
106 compiler.warnings[0].message.kind); | |
107 Expect.equals(0, compiler.errors.length); | |
108 | |
109 compiler = new MockCompiler(); | |
110 compiler.parseScript('class Foo<T, U> {}'); | |
111 compiler.resolveStatement('var x = new Foo<notype, int>();'); | |
112 Expect.equals(0, compiler.warnings.length); | |
113 Expect.equals(1, compiler.errors.length); | |
114 Expect.equals(MessageKind.CANNOT_RESOLVE_TYPE, | |
115 compiler.errors[0].message.kind); | |
116 | |
117 compiler = new MockCompiler(); | |
118 compiler.parseScript('class Foo<T> {' | |
119 ' Foo<T> t;' | |
120 ' foo(Foo<T> f) {}' | |
121 ' bar() { g(Foo<T> f) {}; g(); }' | |
122 '}'); | |
123 foo = compiler.mainApp.find(buildSourceString('Foo')); | |
124 compiler.resolveClass(foo); | |
125 foo.lookupLocalMember(buildSourceString('t')).computeType(compiler);; | |
126 foo.lookupLocalMember(buildSourceString('foo')).computeType(compiler);; | |
127 compiler.resolver.resolve(foo.lookupLocalMember(buildSourceString('bar'))); | |
128 Expect.equals(0, compiler.warnings.length); | |
129 Expect.equals(0, compiler.errors.length); | |
130 } | |
131 | |
132 testSuperCalls() { | |
133 MockCompiler compiler = new MockCompiler(); | |
134 String script = """class A { foo() {} } | |
135 class B extends A { foo() => super.foo(); }"""; | |
136 compiler.parseScript(script); | |
137 compiler.resolveStatement("B b;"); | |
138 | |
139 ClassElement classB = compiler.mainApp.find(buildSourceString("B")); | |
140 FunctionElement fooB = classB.lookupLocalMember(buildSourceString("foo")); | |
141 ClassElement classA = compiler.mainApp.find(buildSourceString("A")); | |
142 FunctionElement fooA = classA.lookupLocalMember(buildSourceString("foo")); | |
143 | |
144 ResolverVisitor visitor = new ResolverVisitor(compiler, fooB); | |
145 FunctionExpression node = fooB.parseNode(compiler); | |
146 visitor.visit(node.body); | |
147 Map mapping = map(visitor); | |
148 | |
149 Send superCall = node.body.asReturn().expression; | |
150 FunctionElement called = mapping[superCall]; | |
151 Expect.isTrue(called !== null); | |
152 Expect.equals(fooA, called); | |
153 } | |
154 | |
155 testThis() { | |
156 MockCompiler compiler = new MockCompiler(); | |
157 compiler.parseScript("class Foo { foo() { return this; } }"); | |
158 compiler.resolveStatement("Foo foo;"); | |
159 ClassElement fooElement = compiler.mainApp.find(buildSourceString("Foo")); | |
160 FunctionElement funElement = | |
161 fooElement.lookupLocalMember(buildSourceString("foo")); | |
162 ResolverVisitor visitor = new ResolverVisitor(compiler, funElement); | |
163 FunctionExpression function = funElement.parseNode(compiler); | |
164 visitor.visit(function.body); | |
165 Map mapping = map(visitor); | |
166 List<Element> values = mapping.getValues(); | |
167 Expect.equals(0, mapping.length); | |
168 Expect.equals(0, compiler.warnings.length); | |
169 | |
170 compiler = new MockCompiler(); | |
171 compiler.resolveStatement("main() { return this; }"); | |
172 Expect.equals(0, compiler.warnings.length); | |
173 Expect.equals(1, compiler.errors.length); | |
174 Expect.equals(MessageKind.NO_INSTANCE_AVAILABLE, | |
175 compiler.errors[0].message.kind); | |
176 | |
177 compiler = new MockCompiler(); | |
178 compiler.parseScript("class Foo { static foo() { return this; } }"); | |
179 compiler.resolveStatement("Foo foo;"); | |
180 fooElement = compiler.mainApp.find(buildSourceString("Foo")); | |
181 funElement = | |
182 fooElement.lookupLocalMember(buildSourceString("foo")); | |
183 visitor = new ResolverVisitor(compiler, funElement); | |
184 function = funElement.parseNode(compiler); | |
185 visitor.visit(function.body); | |
186 Expect.equals(0, compiler.warnings.length); | |
187 Expect.equals(1, compiler.errors.length); | |
188 Expect.equals(MessageKind.NO_INSTANCE_AVAILABLE, | |
189 compiler.errors[0].message.kind); | |
190 } | |
191 | |
192 testLocalsOne() { | |
193 testLocals([["foo", false]]); | |
194 testLocals([["foo", false], ["bar", false]]); | |
195 testLocals([["foo", false], ["bar", false], ["foobar", false]]); | |
196 | |
197 testLocals([["foo", true]]); | |
198 testLocals([["foo", false], ["bar", true]]); | |
199 testLocals([["foo", true], ["bar", true]]); | |
200 | |
201 testLocals([["foo", false], ["bar", false], ["foobar", true]]); | |
202 testLocals([["foo", false], ["bar", true], ["foobar", true]]); | |
203 testLocals([["foo", true], ["bar", true], ["foobar", true]]); | |
204 | |
205 MockCompiler compiler = testLocals([["foo", false], ["foo", false]]); | |
206 Expect.equals(1, compiler.errors.length); | |
207 Expect.equals( | |
208 new Message(MessageKind.DUPLICATE_DEFINITION, ['foo']), | |
209 compiler.errors[0].message); | |
210 } | |
211 | |
212 | |
213 testLocalsTwo() { | |
214 MockCompiler compiler = new MockCompiler(); | |
215 ResolverVisitor visitor = compiler.resolverVisitor(); | |
216 Node tree = parseStatement("if (true) { var a = 1; var b = 2; }"); | |
217 Element element = visitor.visit(tree); | |
218 Expect.equals(null, element); | |
219 BlockScope scope = visitor.context; | |
220 Expect.equals(0, scope.elements.length); | |
221 Expect.equals(2, map(visitor).length); | |
222 | |
223 List<Element> elements = map(visitor).getValues(); | |
224 Expect.notEquals(elements[0], elements[1]); | |
225 } | |
226 | |
227 testLocalsThree() { | |
228 MockCompiler compiler = new MockCompiler(); | |
229 ResolverVisitor visitor = compiler.resolverVisitor(); | |
230 Node tree = parseStatement("{ var a = 1; if (true) { a; } }"); | |
231 Element element = visitor.visit(tree); | |
232 Expect.equals(null, element); | |
233 BlockScope scope = visitor.context; | |
234 Expect.equals(0, scope.elements.length); | |
235 Expect.equals(3, map(visitor).length); | |
236 List<Element> elements = map(visitor).getValues(); | |
237 Expect.equals(elements[0], elements[1]); | |
238 } | |
239 | |
240 testLocalsFour() { | |
241 MockCompiler compiler = new MockCompiler(); | |
242 ResolverVisitor visitor = compiler.resolverVisitor(); | |
243 Node tree = parseStatement("{ var a = 1; if (true) { var a = 1; } }"); | |
244 Element element = visitor.visit(tree); | |
245 Expect.equals(null, element); | |
246 BlockScope scope = visitor.context; | |
247 Expect.equals(0, scope.elements.length); | |
248 Expect.equals(2, map(visitor).length); | |
249 List<Element> elements = map(visitor).getValues(); | |
250 Expect.notEquals(elements[0], elements[1]); | |
251 } | |
252 | |
253 testLocalsFive() { | |
254 MockCompiler compiler = new MockCompiler(); | |
255 ResolverVisitor visitor = compiler.resolverVisitor(); | |
256 If tree = parseStatement("if (true) { var a = 1; a; } else { var a = 2; a;}"); | |
257 Element element = visitor.visit(tree); | |
258 Expect.equals(null, element); | |
259 BlockScope scope = visitor.context; | |
260 Expect.equals(0, scope.elements.length); | |
261 Expect.equals(6, map(visitor).length); | |
262 | |
263 Block thenPart = tree.thenPart; | |
264 List statements1 = thenPart.statements.nodes.toList(); | |
265 Node def1 = statements1[0].definitions.nodes.head; | |
266 Node id1 = statements1[1].expression; | |
267 Expect.equals(visitor.mapping[def1], visitor.mapping[id1]); | |
268 | |
269 Block elsePart = tree.elsePart; | |
270 List statements2 = elsePart.statements.nodes.toList(); | |
271 Node def2 = statements2[0].definitions.nodes.head; | |
272 Node id2 = statements2[1].expression; | |
273 Expect.equals(visitor.mapping[def2], visitor.mapping[id2]); | |
274 | |
275 Expect.notEquals(visitor.mapping[def1], visitor.mapping[def2]); | |
276 Expect.notEquals(visitor.mapping[id1], visitor.mapping[id2]); | |
277 } | |
278 | |
279 testParametersOne() { | |
280 MockCompiler compiler = new MockCompiler(); | |
281 ResolverVisitor visitor = compiler.resolverVisitor(); | |
282 FunctionExpression tree = | |
283 parseFunction("void foo(int a) { return a; }", compiler); | |
284 visitor.visit(tree); | |
285 | |
286 // Check that an element has been created for the parameter. | |
287 VariableDefinitions vardef = tree.parameters.nodes.head; | |
288 Node param = vardef.definitions.nodes.head; | |
289 Expect.equals(ElementKind.PARAMETER, visitor.mapping[param].kind); | |
290 | |
291 // Check that 'a' in 'return a' is resolved to the parameter. | |
292 Block body = tree.body; | |
293 Return ret = body.statements.nodes.head; | |
294 Send use = ret.expression; | |
295 Expect.equals(ElementKind.PARAMETER, visitor.mapping[use].kind); | |
296 Expect.equals(visitor.mapping[param], visitor.mapping[use]); | |
297 } | |
298 | |
299 testFor() { | |
300 MockCompiler compiler = new MockCompiler(); | |
301 ResolverVisitor visitor = compiler.resolverVisitor(); | |
302 For tree = parseStatement("for (int i = 0; i < 10; i = i + 1) { i = 5; }"); | |
303 visitor.visit(tree); | |
304 | |
305 BlockScope scope = visitor.context; | |
306 Expect.equals(0, scope.elements.length); | |
307 Expect.equals(10, map(visitor).length); | |
308 | |
309 VariableDefinitions initializer = tree.initializer; | |
310 Node iNode = initializer.definitions.nodes.head; | |
311 Element iElement = visitor.mapping[iNode]; | |
312 | |
313 // Check that we have the expected nodes. This test relies on the mapping | |
314 // field to be a linked hash map (preserving insertion order). | |
315 Expect.isTrue(map(visitor) is LinkedHashMap); | |
316 List<Node> nodes = map(visitor).getKeys(); | |
317 List<Element> elements = map(visitor).getValues(); | |
318 | |
319 | |
320 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
321 // ^^^ | |
322 Expect.isTrue(nodes[0] is TypeAnnotation); | |
323 | |
324 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
325 // ^^^^^ | |
326 checkSendSet(iElement, nodes[1], elements[1]); | |
327 | |
328 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
329 // ^ | |
330 checkIdentifier(iElement, nodes[2], elements[2]); | |
331 | |
332 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
333 // ^ | |
334 checkSend(iElement, nodes[3], elements[3]); | |
335 | |
336 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
337 // ^ | |
338 checkIdentifier(iElement, nodes[4], elements[4]); | |
339 | |
340 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
341 // ^ | |
342 checkIdentifier(iElement, nodes[5], elements[5]); | |
343 | |
344 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
345 // ^ | |
346 checkSend(iElement, nodes[6], elements[6]); | |
347 | |
348 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
349 // ^^^^^^^^^ | |
350 checkSendSet(iElement, nodes[7], elements[7]); | |
351 | |
352 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
353 // ^ | |
354 checkIdentifier(iElement, nodes[8], elements[8]); | |
355 | |
356 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
357 // ^^^^^ | |
358 checkSendSet(iElement, nodes[9], elements[9]); | |
359 } | |
360 | |
361 checkIdentifier(Element expected, Node node, Element actual) { | |
362 Expect.isTrue(node is Identifier, node.toDebugString()); | |
363 Expect.equals(expected, actual); | |
364 } | |
365 | |
366 checkSend(Element expected, Node node, Element actual) { | |
367 Expect.isTrue(node is Send, node.toDebugString()); | |
368 Expect.isTrue(node is !SendSet, node.toDebugString()); | |
369 Expect.equals(expected, actual); | |
370 } | |
371 | |
372 checkSendSet(Element expected, Node node, Element actual) { | |
373 Expect.isTrue(node is SendSet, node.toDebugString()); | |
374 Expect.equals(expected, actual); | |
375 } | |
376 | |
377 testTypeAnnotation() { | |
378 MockCompiler compiler = new MockCompiler(); | |
379 String statement = "Foo bar;"; | |
380 | |
381 // Test that we get a warning when Foo is not defined. | |
382 Map mapping = compiler.resolveStatement(statement).map; | |
383 | |
384 Expect.equals(1, mapping.length); // bar has an element. | |
385 Expect.equals(1, compiler.warnings.length); | |
386 | |
387 Node warningNode = compiler.warnings[0].node; | |
388 | |
389 Expect.equals( | |
390 new Message(MessageKind.CANNOT_RESOLVE_TYPE, ['Foo']), | |
391 compiler.warnings[0].message); | |
392 VariableDefinitions definition = compiler.parsedTree; | |
393 Expect.equals(warningNode, definition.type); | |
394 compiler.clearWarnings(); | |
395 | |
396 // Test that there is no warning after defining Foo. | |
397 compiler.parseScript("class Foo {}"); | |
398 mapping = compiler.resolveStatement(statement).map; | |
399 Expect.equals(2, mapping.length); | |
400 Expect.equals(0, compiler.warnings.length); | |
401 | |
402 // Test that 'var' does not create a warning. | |
403 mapping = compiler.resolveStatement("var foo;").map; | |
404 Expect.equals(1, mapping.length); | |
405 Expect.equals(0, compiler.warnings.length); | |
406 } | |
407 | |
408 testSuperclass() { | |
409 MockCompiler compiler = new MockCompiler(); | |
410 compiler.parseScript("class Foo extends Bar {}"); | |
411 compiler.resolveStatement("Foo bar;"); | |
412 Expect.equals(1, compiler.errors.length); | |
413 Expect.equals( | |
414 new Message(MessageKind.CANNOT_RESOLVE_TYPE, ['Bar']), | |
415 compiler.errors[0].message); | |
416 compiler.clearErrors(); | |
417 | |
418 compiler = new MockCompiler(); | |
419 compiler.parseScript("class Foo extends Bar {}"); | |
420 compiler.parseScript("class Bar {}"); | |
421 Map mapping = compiler.resolveStatement("Foo bar;").map; | |
422 Expect.equals(2, mapping.length); | |
423 | |
424 ClassElement fooElement = compiler.mainApp.find(buildSourceString('Foo')); | |
425 ClassElement barElement = compiler.mainApp.find(buildSourceString('Bar')); | |
426 Expect.equals(barElement.computeType(compiler), | |
427 fooElement.supertype); | |
428 Expect.isTrue(fooElement.interfaces.isEmpty()); | |
429 Expect.isTrue(barElement.interfaces.isEmpty()); | |
430 } | |
431 | |
432 testVarSuperclass() { | |
433 MockCompiler compiler = new MockCompiler(); | |
434 compiler.parseScript("class Foo extends var {}"); | |
435 compiler.resolveStatement("Foo bar;"); | |
436 Expect.equals(1, compiler.errors.length); | |
437 Expect.equals( | |
438 new Message(MessageKind.CANNOT_RESOLVE_TYPE, ['var']), | |
439 compiler.errors[0].message); | |
440 compiler.clearErrors(); | |
441 } | |
442 | |
443 testOneInterface() { | |
444 MockCompiler compiler = new MockCompiler(); | |
445 compiler.parseScript("class Foo implements Bar {}"); | |
446 compiler.resolveStatement("Foo bar;"); | |
447 Expect.equals(1, compiler.errors.length); | |
448 Expect.equals( | |
449 new Message(MessageKind.CANNOT_RESOLVE_TYPE, ['bar']), | |
450 compiler.errors[0].message); | |
451 compiler.clearErrors(); | |
452 | |
453 // Add the interface to the world and make sure everything is setup correctly. | |
454 compiler.parseScript("interface Bar {}"); | |
455 | |
456 ResolverVisitor visitor = new ResolverVisitor(compiler, null); | |
457 compiler.resolveStatement("Foo bar;"); | |
458 | |
459 ClassElement fooElement = compiler.mainApp.find(buildSourceString('Foo')); | |
460 ClassElement barElement = compiler.mainApp.find(buildSourceString('Bar')); | |
461 | |
462 Expect.equals(null, barElement.supertype); | |
463 Expect.isTrue(barElement.interfaces.isEmpty()); | |
464 | |
465 Expect.equals(barElement.computeType(compiler), | |
466 fooElement.interfaces.head); | |
467 Expect.equals(1, length(fooElement.interfaces)); | |
468 } | |
469 | |
470 testTwoInterfaces() { | |
471 MockCompiler compiler = new MockCompiler(); | |
472 compiler.parseScript( | |
473 "interface I1 {} interface I2 {} class C implements I1, I2 {}"); | |
474 compiler.resolveStatement("Foo bar;"); | |
475 | |
476 ClassElement c = compiler.mainApp.find(buildSourceString('C')); | |
477 Element i1 = compiler.mainApp.find(buildSourceString('I1')); | |
478 Element i2 = compiler.mainApp.find(buildSourceString('I2')); | |
479 | |
480 Expect.equals(2, length(c.interfaces)); | |
481 Expect.equals(i1.computeType(compiler), at(c.interfaces, 0)); | |
482 Expect.equals(i2.computeType(compiler), at(c.interfaces, 1)); | |
483 } | |
484 | |
485 testFunctionExpression() { | |
486 MockCompiler compiler = new MockCompiler(); | |
487 ResolverVisitor visitor = compiler.resolverVisitor(); | |
488 Map mapping = compiler.resolveStatement("int f() {}").map; | |
489 Expect.equals(3, mapping.length); | |
490 Element element; | |
491 Node node; | |
492 mapping.forEach((Node n, Element e) { | |
493 if (n is FunctionExpression) { | |
494 element = e; | |
495 node = n; | |
496 } | |
497 }); | |
498 Expect.equals(ElementKind.FUNCTION, element.kind); | |
499 Expect.equals(buildSourceString('f'), element.name); | |
500 Expect.equals(element.parseNode(compiler), node); | |
501 } | |
502 | |
503 testNewExpression() { | |
504 MockCompiler compiler = new MockCompiler(); | |
505 compiler.parseScript("class A {} foo() { print(new A()); }"); | |
506 ClassElement aElement = compiler.mainApp.find(buildSourceString('A')); | |
507 FunctionElement fooElement = compiler.mainApp.find(buildSourceString('foo')); | |
508 Expect.isTrue(aElement !== null); | |
509 Expect.isTrue(fooElement !== null); | |
510 | |
511 fooElement.parseNode(compiler); | |
512 compiler.resolver.resolve(fooElement); | |
513 | |
514 TreeElements elements = compiler.resolveStatement("new A();"); | |
515 NewExpression expression = | |
516 compiler.parsedTree.asExpressionStatement().expression; | |
517 Element element = elements[expression.send]; | |
518 Expect.equals(ElementKind.GENERATIVE_CONSTRUCTOR, element.kind); | |
519 Expect.isTrue(element is SynthesizedConstructorElement); | |
520 } | |
521 | |
522 testTopLevelFields() { | |
523 MockCompiler compiler = new MockCompiler(); | |
524 compiler.parseScript("int a;"); | |
525 VariableElement element = compiler.mainApp.find(buildSourceString("a")); | |
526 Expect.equals(ElementKind.FIELD, element.kind); | |
527 VariableDefinitions node = element.variables.parseNode(compiler); | |
528 Identifier typeName = node.type.typeName; | |
529 Expect.equals(typeName.source.slowToString(), 'int'); | |
530 | |
531 compiler.parseScript("var b, c;"); | |
532 VariableElement bElement = compiler.mainApp.find(buildSourceString("b")); | |
533 VariableElement cElement = compiler.mainApp.find(buildSourceString("c")); | |
534 Expect.equals(ElementKind.FIELD, bElement.kind); | |
535 Expect.equals(ElementKind.FIELD, cElement.kind); | |
536 Expect.isTrue(bElement != cElement); | |
537 | |
538 VariableDefinitions bNode = bElement.variables.parseNode(compiler); | |
539 VariableDefinitions cNode = cElement.variables.parseNode(compiler); | |
540 Expect.equals(bNode, cNode); | |
541 Expect.isNull(bNode.type); | |
542 Expect.isTrue(bNode.modifiers.isVar()); | |
543 } | |
544 | |
545 resolveConstructor(String script, String statement, String className, | |
546 String constructor, int expectedElementCount, | |
547 [List expectedWarnings = const [], | |
548 List expectedErrors = const [], | |
549 String corelib = DEFAULT_CORELIB]) { | |
550 MockCompiler compiler = new MockCompiler(corelib); | |
551 compiler.parseScript(script); | |
552 compiler.resolveStatement(statement); | |
553 ClassElement classElement = | |
554 compiler.mainApp.find(buildSourceString(className)); | |
555 Element element = | |
556 classElement.lookupConstructor(buildSourceString(constructor)); | |
557 FunctionExpression tree = element.parseNode(compiler); | |
558 ResolverVisitor visitor = new ResolverVisitor(compiler, element); | |
559 new InitializerResolver(visitor).resolveInitializers(element, tree); | |
560 visitor.visit(tree.body); | |
561 Expect.equals(expectedElementCount, map(visitor).length); | |
562 | |
563 compareWarningKinds(script, expectedWarnings, compiler.warnings); | |
564 compareWarningKinds(script, expectedErrors, compiler.errors); | |
565 } | |
566 | |
567 testClassHierarchy() { | |
568 final MAIN = buildSourceString("main"); | |
569 MockCompiler compiler = new MockCompiler(); | |
570 compiler.parseScript("""class A extends B {} | |
571 class B extends A {} | |
572 main() { return new A(); }"""); | |
573 FunctionElement mainElement = compiler.mainApp.find(MAIN); | |
574 compiler.resolver.resolve(mainElement); | |
575 Expect.equals(0, compiler.warnings.length); | |
576 Expect.equals(1, compiler.errors.length); | |
577 Expect.equals(MessageKind.CYCLIC_CLASS_HIERARCHY, | |
578 compiler.errors[0].message.kind); | |
579 | |
580 compiler = new MockCompiler(); | |
581 compiler.parseScript("""interface A extends B {} | |
582 interface B extends A {} | |
583 class C implements A {} | |
584 main() { return new C(); }"""); | |
585 mainElement = compiler.mainApp.find(MAIN); | |
586 compiler.resolver.resolve(mainElement); | |
587 Expect.equals(0, compiler.warnings.length); | |
588 Expect.equals(1, compiler.errors.length); | |
589 Expect.equals(MessageKind.CYCLIC_CLASS_HIERARCHY, | |
590 compiler.errors[0].message.kind); | |
591 | |
592 compiler = new MockCompiler(); | |
593 compiler.parseScript("""class A extends B {} | |
594 class B extends C {} | |
595 class C {} | |
596 main() { return new A(); }"""); | |
597 mainElement = compiler.mainApp.find(MAIN); | |
598 compiler.resolver.resolve(mainElement); | |
599 Expect.equals(0, compiler.warnings.length); | |
600 Expect.equals(0, compiler.errors.length); | |
601 ClassElement aElement = compiler.mainApp.find(buildSourceString("A")); | |
602 Link<Type> supertypes = aElement.allSupertypes; | |
603 Expect.equals(<String>['B', 'C', 'Object'].toString(), | |
604 asSortedStrings(supertypes).toString()); | |
605 } | |
606 | |
607 testInitializers() { | |
608 String script; | |
609 script = """class A { | |
610 int foo; int bar; | |
611 A() : this.foo = 1, bar = 2; | |
612 }"""; | |
613 resolveConstructor(script, "A a = new A();", "A", "A", 2); | |
614 | |
615 script = """class A { | |
616 int foo; A a; | |
617 A() : a.foo = 1; | |
618 }"""; | |
619 resolveConstructor(script, "A a = new A();", "A", "A", 0, | |
620 [], [MessageKind.INVALID_RECEIVER_IN_INITIALIZER]); | |
621 | |
622 script = """class A { | |
623 int foo; | |
624 A() : this.foo = 1, this.foo = 2; | |
625 }"""; | |
626 resolveConstructor(script, "A a = new A();", "A", "A", 2, | |
627 [MessageKind.ALREADY_INITIALIZED], | |
628 [MessageKind.DUPLICATE_INITIALIZER]); | |
629 | |
630 script = """class A { | |
631 A() : this.foo = 1; | |
632 }"""; | |
633 resolveConstructor(script, "A a = new A();", "A", "A", 0, | |
634 [], [MessageKind.CANNOT_RESOLVE]); | |
635 | |
636 script = """class A { | |
637 int foo; | |
638 int bar; | |
639 A() : this.foo = bar; | |
640 }"""; | |
641 resolveConstructor(script, "A a = new A();", "A", "A", 3, | |
642 [], [MessageKind.NO_INSTANCE_AVAILABLE]); | |
643 | |
644 script = """class A { | |
645 int foo() => 42; | |
646 A() : foo(); | |
647 }"""; | |
648 resolveConstructor(script, "A a = new A();", "A", "A", 0, | |
649 [], [MessageKind.CONSTRUCTOR_CALL_EXPECTED]); | |
650 | |
651 script = """class A { | |
652 int i; | |
653 A.a() : this.b(0); | |
654 A.b(int i); | |
655 }"""; | |
656 resolveConstructor(script, "A a = new A.a();", "A", @"A$a", 1, | |
657 [], []); | |
658 | |
659 script = """class A { | |
660 int i; | |
661 A.a() : i = 42, this(0); | |
662 A(int i); | |
663 }"""; | |
664 resolveConstructor(script, "A a = new A.a();", "A", @"A$a", 2, | |
665 [], [MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER]); | |
666 | |
667 script = """class A { | |
668 int i; | |
669 A(i); | |
670 } | |
671 class B extends A { | |
672 B() : super(0); | |
673 }"""; | |
674 resolveConstructor(script, "B a = new B();", "B", "B", 1, | |
675 [], []); | |
676 | |
677 script = """class A { | |
678 int i; | |
679 A(i); | |
680 } | |
681 class B extends A { | |
682 B() : super(0), super(1); | |
683 }"""; | |
684 resolveConstructor(script, "B b = new B();", "B", "B", 2, | |
685 [], [MessageKind.DUPLICATE_SUPER_INITIALIZER]); | |
686 | |
687 script = ""; | |
688 final String CORELIB_WITH_INVALID_OBJECT = | |
689 '''print(var obj) {} | |
690 class int {} | |
691 class double {} | |
692 class bool {} | |
693 class String {} | |
694 class num {} | |
695 class Function {} | |
696 class List {} | |
697 class Closure {} | |
698 class Null {} | |
699 class Dynamic {} | |
700 class Object { Object() : super(); }'''; | |
701 resolveConstructor(script, "Object o = new Object();", "Object", "Object", 1, | |
702 [], [MessageKind.SUPER_INITIALIZER_IN_OBJECT], | |
703 corelib: CORELIB_WITH_INVALID_OBJECT); | |
704 } | |
705 | |
706 map(ResolverVisitor visitor) { | |
707 TreeElementMapping elements = visitor.mapping; | |
708 return elements.map; | |
709 } | |
710 | |
711 length(Link link) => link.isEmpty() ? 0 : length(link.tail) + 1; | |
712 | |
713 at(Link link, int index) => (index == 0) ? link.head : at(link.tail, index - 1); | |
714 | |
715 List<String> asSortedStrings(Link link) { | |
716 List<String> result = <String>[]; | |
717 for (; !link.isEmpty(); link = link.tail) result.add(link.head.toString()); | |
718 result.sort((s1, s2) => s1.compareTo(s2)); | |
719 return result; | |
720 } | |
OLD | NEW |