| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library analyzer.test.generated.resolver_test_case; |
| 6 |
| 7 import 'package:analyzer/dart/ast/ast.dart'; |
| 8 import 'package:analyzer/dart/ast/visitor.dart'; |
| 9 import 'package:analyzer/dart/element/element.dart'; |
| 10 import 'package:analyzer/dart/element/type.dart'; |
| 11 import 'package:analyzer/error/error.dart'; |
| 12 import 'package:analyzer/file_system/memory_file_system.dart'; |
| 13 import 'package:analyzer/src/dart/element/element.dart'; |
| 14 import 'package:analyzer/src/dart/element/type.dart'; |
| 15 import 'package:analyzer/src/error/codes.dart'; |
| 16 import 'package:analyzer/src/generated/engine.dart'; |
| 17 import 'package:analyzer/src/generated/java_engine.dart'; |
| 18 import 'package:analyzer/src/generated/resolver.dart'; |
| 19 import 'package:analyzer/src/generated/source_io.dart'; |
| 20 import 'package:analyzer/src/generated/testing/ast_factory.dart'; |
| 21 import 'package:analyzer/src/generated/testing/element_factory.dart'; |
| 22 import 'package:unittest/unittest.dart'; |
| 23 |
| 24 import 'analysis_context_factory.dart'; |
| 25 import 'test_support.dart'; |
| 26 |
| 27 /** |
| 28 * An AST visitor used to verify that all of the nodes in an AST structure that |
| 29 * should have been resolved were resolved. |
| 30 */ |
| 31 class ResolutionVerifier extends RecursiveAstVisitor<Object> { |
| 32 /** |
| 33 * A set containing nodes that are known to not be resolvable and should |
| 34 * therefore not cause the test to fail. |
| 35 */ |
| 36 final Set<AstNode> _knownExceptions; |
| 37 |
| 38 /** |
| 39 * A list containing all of the AST nodes that were not resolved. |
| 40 */ |
| 41 List<AstNode> _unresolvedNodes = new List<AstNode>(); |
| 42 |
| 43 /** |
| 44 * A list containing all of the AST nodes that were resolved to an element of |
| 45 * the wrong type. |
| 46 */ |
| 47 List<AstNode> _wrongTypedNodes = new List<AstNode>(); |
| 48 |
| 49 /** |
| 50 * Initialize a newly created verifier to verify that all of the identifiers |
| 51 * in the visited AST structures that are expected to have been resolved have |
| 52 * an element associated with them. Nodes in the set of [_knownExceptions] are |
| 53 * not expected to have been resolved, even if they normally would have been |
| 54 * expected to have been resolved. |
| 55 */ |
| 56 ResolutionVerifier([this._knownExceptions]); |
| 57 |
| 58 /** |
| 59 * Assert that all of the visited identifiers were resolved. |
| 60 */ |
| 61 void assertResolved() { |
| 62 if (!_unresolvedNodes.isEmpty || !_wrongTypedNodes.isEmpty) { |
| 63 StringBuffer buffer = new StringBuffer(); |
| 64 if (!_unresolvedNodes.isEmpty) { |
| 65 buffer.write("Failed to resolve "); |
| 66 buffer.write(_unresolvedNodes.length); |
| 67 buffer.writeln(" nodes:"); |
| 68 _printNodes(buffer, _unresolvedNodes); |
| 69 } |
| 70 if (!_wrongTypedNodes.isEmpty) { |
| 71 buffer.write("Resolved "); |
| 72 buffer.write(_wrongTypedNodes.length); |
| 73 buffer.writeln(" to the wrong type of element:"); |
| 74 _printNodes(buffer, _wrongTypedNodes); |
| 75 } |
| 76 fail(buffer.toString()); |
| 77 } |
| 78 } |
| 79 |
| 80 @override |
| 81 Object visitAnnotation(Annotation node) { |
| 82 node.visitChildren(this); |
| 83 ElementAnnotation elementAnnotation = node.elementAnnotation; |
| 84 if (elementAnnotation == null) { |
| 85 if (_knownExceptions == null || !_knownExceptions.contains(node)) { |
| 86 _unresolvedNodes.add(node); |
| 87 } |
| 88 } else if (elementAnnotation is! ElementAnnotation) { |
| 89 _wrongTypedNodes.add(node); |
| 90 } |
| 91 return null; |
| 92 } |
| 93 |
| 94 @override |
| 95 Object visitBinaryExpression(BinaryExpression node) { |
| 96 node.visitChildren(this); |
| 97 if (!node.operator.isUserDefinableOperator) { |
| 98 return null; |
| 99 } |
| 100 DartType operandType = node.leftOperand.staticType; |
| 101 if (operandType == null || operandType.isDynamic) { |
| 102 return null; |
| 103 } |
| 104 return _checkResolved( |
| 105 node, node.staticElement, (node) => node is MethodElement); |
| 106 } |
| 107 |
| 108 @override |
| 109 Object visitCommentReference(CommentReference node) => null; |
| 110 |
| 111 @override |
| 112 Object visitCompilationUnit(CompilationUnit node) { |
| 113 node.visitChildren(this); |
| 114 return _checkResolved( |
| 115 node, node.element, (node) => node is CompilationUnitElement); |
| 116 } |
| 117 |
| 118 @override |
| 119 Object visitExportDirective(ExportDirective node) => |
| 120 _checkResolved(node, node.element, (node) => node is ExportElement); |
| 121 |
| 122 @override |
| 123 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 124 node.visitChildren(this); |
| 125 if (node.element is LibraryElement) { |
| 126 _wrongTypedNodes.add(node); |
| 127 } |
| 128 return null; |
| 129 } |
| 130 |
| 131 @override |
| 132 Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| 133 node.visitChildren(this); |
| 134 // TODO(brianwilkerson) If we start resolving function expressions, then |
| 135 // conditionally check to see whether the node was resolved correctly. |
| 136 return null; |
| 137 //checkResolved(node, node.getElement(), FunctionElement.class); |
| 138 } |
| 139 |
| 140 @override |
| 141 Object visitImportDirective(ImportDirective node) { |
| 142 // Not sure how to test the combinators given that it isn't an error if the |
| 143 // names are not defined. |
| 144 _checkResolved(node, node.element, (node) => node is ImportElement); |
| 145 SimpleIdentifier prefix = node.prefix; |
| 146 if (prefix == null) { |
| 147 return null; |
| 148 } |
| 149 return _checkResolved( |
| 150 prefix, prefix.staticElement, (node) => node is PrefixElement); |
| 151 } |
| 152 |
| 153 @override |
| 154 Object visitIndexExpression(IndexExpression node) { |
| 155 node.visitChildren(this); |
| 156 DartType targetType = node.realTarget.staticType; |
| 157 if (targetType == null || targetType.isDynamic) { |
| 158 return null; |
| 159 } |
| 160 return _checkResolved( |
| 161 node, node.staticElement, (node) => node is MethodElement); |
| 162 } |
| 163 |
| 164 @override |
| 165 Object visitLibraryDirective(LibraryDirective node) => |
| 166 _checkResolved(node, node.element, (node) => node is LibraryElement); |
| 167 |
| 168 @override |
| 169 Object visitNamedExpression(NamedExpression node) => |
| 170 node.expression.accept(this); |
| 171 |
| 172 @override |
| 173 Object visitPartDirective(PartDirective node) => _checkResolved( |
| 174 node, node.element, (node) => node is CompilationUnitElement); |
| 175 |
| 176 @override |
| 177 Object visitPartOfDirective(PartOfDirective node) => |
| 178 _checkResolved(node, node.element, (node) => node is LibraryElement); |
| 179 |
| 180 @override |
| 181 Object visitPostfixExpression(PostfixExpression node) { |
| 182 node.visitChildren(this); |
| 183 if (!node.operator.isUserDefinableOperator) { |
| 184 return null; |
| 185 } |
| 186 DartType operandType = node.operand.staticType; |
| 187 if (operandType == null || operandType.isDynamic) { |
| 188 return null; |
| 189 } |
| 190 return _checkResolved( |
| 191 node, node.staticElement, (node) => node is MethodElement); |
| 192 } |
| 193 |
| 194 @override |
| 195 Object visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 196 SimpleIdentifier prefix = node.prefix; |
| 197 prefix.accept(this); |
| 198 DartType prefixType = prefix.staticType; |
| 199 if (prefixType == null || prefixType.isDynamic) { |
| 200 return null; |
| 201 } |
| 202 return _checkResolved(node, node.staticElement, null); |
| 203 } |
| 204 |
| 205 @override |
| 206 Object visitPrefixExpression(PrefixExpression node) { |
| 207 node.visitChildren(this); |
| 208 if (!node.operator.isUserDefinableOperator) { |
| 209 return null; |
| 210 } |
| 211 DartType operandType = node.operand.staticType; |
| 212 if (operandType == null || operandType.isDynamic) { |
| 213 return null; |
| 214 } |
| 215 return _checkResolved( |
| 216 node, node.staticElement, (node) => node is MethodElement); |
| 217 } |
| 218 |
| 219 @override |
| 220 Object visitPropertyAccess(PropertyAccess node) { |
| 221 Expression target = node.realTarget; |
| 222 target.accept(this); |
| 223 DartType targetType = target.staticType; |
| 224 if (targetType == null || targetType.isDynamic) { |
| 225 return null; |
| 226 } |
| 227 return node.propertyName.accept(this); |
| 228 } |
| 229 |
| 230 @override |
| 231 Object visitSimpleIdentifier(SimpleIdentifier node) { |
| 232 if (node.name == "void") { |
| 233 return null; |
| 234 } |
| 235 if (node.staticType != null && |
| 236 node.staticType.isDynamic && |
| 237 node.staticElement == null) { |
| 238 return null; |
| 239 } |
| 240 AstNode parent = node.parent; |
| 241 if (parent is MethodInvocation) { |
| 242 MethodInvocation invocation = parent; |
| 243 if (identical(invocation.methodName, node)) { |
| 244 Expression target = invocation.realTarget; |
| 245 DartType targetType = target == null ? null : target.staticType; |
| 246 if (targetType == null || targetType.isDynamic) { |
| 247 return null; |
| 248 } |
| 249 } |
| 250 } |
| 251 return _checkResolved(node, node.staticElement, null); |
| 252 } |
| 253 |
| 254 Object _checkResolved( |
| 255 AstNode node, Element element, Predicate<Element> predicate) { |
| 256 if (element == null) { |
| 257 if (_knownExceptions == null || !_knownExceptions.contains(node)) { |
| 258 _unresolvedNodes.add(node); |
| 259 } |
| 260 } else if (predicate != null) { |
| 261 if (!predicate(element)) { |
| 262 _wrongTypedNodes.add(node); |
| 263 } |
| 264 } |
| 265 return null; |
| 266 } |
| 267 |
| 268 String _getFileName(AstNode node) { |
| 269 // TODO (jwren) there are two copies of this method, one here and one in |
| 270 // StaticTypeVerifier, they should be resolved into a single method |
| 271 if (node != null) { |
| 272 AstNode root = node.root; |
| 273 if (root is CompilationUnit) { |
| 274 CompilationUnit rootCU = root; |
| 275 if (rootCU.element != null) { |
| 276 return rootCU.element.source.fullName; |
| 277 } else { |
| 278 return "<unknown file- CompilationUnit.getElement() returned null>"; |
| 279 } |
| 280 } else { |
| 281 return "<unknown file- CompilationUnit.getRoot() is not a CompilationUni
t>"; |
| 282 } |
| 283 } |
| 284 return "<unknown file- ASTNode is null>"; |
| 285 } |
| 286 |
| 287 void _printNodes(StringBuffer buffer, List<AstNode> nodes) { |
| 288 for (AstNode identifier in nodes) { |
| 289 buffer.write(" "); |
| 290 buffer.write(identifier.toString()); |
| 291 buffer.write(" ("); |
| 292 buffer.write(_getFileName(identifier)); |
| 293 buffer.write(" : "); |
| 294 buffer.write(identifier.offset); |
| 295 buffer.writeln(")"); |
| 296 } |
| 297 } |
| 298 } |
| 299 |
| 300 class ResolverTestCase extends EngineTestCase { |
| 301 /** |
| 302 * The resource provider used by the test case. |
| 303 */ |
| 304 MemoryResourceProvider resourceProvider = new MemoryResourceProvider(); |
| 305 |
| 306 /** |
| 307 * The analysis context used to parse the compilation units being resolved. |
| 308 */ |
| 309 InternalAnalysisContext analysisContext2; |
| 310 |
| 311 /** |
| 312 * Specifies if [assertErrors] should check for [HintCode.UNUSED_ELEMENT] and |
| 313 * [HintCode.UNUSED_FIELD]. |
| 314 */ |
| 315 bool enableUnusedElement = false; |
| 316 |
| 317 /** |
| 318 * Specifies if [assertErrors] should check for [HintCode.UNUSED_LOCAL_VARIABL
E]. |
| 319 */ |
| 320 bool enableUnusedLocalVariable = false; |
| 321 |
| 322 AnalysisContext get analysisContext => analysisContext2; |
| 323 |
| 324 /** |
| 325 * Return a type provider that can be used to test the results of resolution. |
| 326 * |
| 327 * @return a type provider |
| 328 * @throws AnalysisException if dart:core cannot be resolved |
| 329 */ |
| 330 TypeProvider get typeProvider => analysisContext2.typeProvider; |
| 331 |
| 332 /** |
| 333 * Return a type system that can be used to test the results of resolution. |
| 334 * |
| 335 * @return a type system |
| 336 */ |
| 337 TypeSystem get typeSystem => analysisContext2.typeSystem; |
| 338 |
| 339 /** |
| 340 * Add a source file with the given [filePath] in the root of the file system. |
| 341 * The file path should be absolute. The file will have the given [contents] |
| 342 * set in the content provider. Return the source representing the added file. |
| 343 */ |
| 344 Source addNamedSource(String filePath, String contents) { |
| 345 Source source = |
| 346 cacheSource(resourceProvider.convertPath(filePath), contents); |
| 347 ChangeSet changeSet = new ChangeSet(); |
| 348 changeSet.addedSource(source); |
| 349 analysisContext2.applyChanges(changeSet); |
| 350 return source; |
| 351 } |
| 352 |
| 353 /** |
| 354 * Add a source file named 'test.dart' in the root of the file system. The |
| 355 * file will have the given [contents] set in the content provider. Return the |
| 356 * source representing the added file. |
| 357 */ |
| 358 Source addSource(String contents) => addNamedSource("/test.dart", contents); |
| 359 |
| 360 /** |
| 361 * Assert that the number of errors reported against the given source matches
the number of errors |
| 362 * that are given and that they have the expected error codes. The order in wh
ich the errors were |
| 363 * gathered is ignored. |
| 364 * |
| 365 * @param source the source against which the errors should have been reported |
| 366 * @param expectedErrorCodes the error codes of the errors that should have be
en reported |
| 367 * @throws AnalysisException if the reported errors could not be computed |
| 368 * @throws AssertionFailedError if a different number of errors have been repo
rted than were |
| 369 * expected |
| 370 */ |
| 371 void assertErrors(Source source, |
| 372 [List<ErrorCode> expectedErrorCodes = ErrorCode.EMPTY_LIST]) { |
| 373 GatheringErrorListener errorListener = new GatheringErrorListener(); |
| 374 for (AnalysisError error in analysisContext2.computeErrors(source)) { |
| 375 expect(error.source, source); |
| 376 ErrorCode errorCode = error.errorCode; |
| 377 if (!enableUnusedElement && |
| 378 (errorCode == HintCode.UNUSED_ELEMENT || |
| 379 errorCode == HintCode.UNUSED_FIELD)) { |
| 380 continue; |
| 381 } |
| 382 if (!enableUnusedLocalVariable && |
| 383 (errorCode == HintCode.UNUSED_CATCH_CLAUSE || |
| 384 errorCode == HintCode.UNUSED_CATCH_STACK || |
| 385 errorCode == HintCode.UNUSED_LOCAL_VARIABLE)) { |
| 386 continue; |
| 387 } |
| 388 errorListener.onError(error); |
| 389 } |
| 390 errorListener.assertErrorsWithCodes(expectedErrorCodes); |
| 391 } |
| 392 |
| 393 /** |
| 394 * Asserts that [code] verifies, but has errors with the given error codes. |
| 395 * |
| 396 * Like [assertErrors], but takes a string of source code. |
| 397 */ |
| 398 // TODO(rnystrom): Use this in more tests that have the same structure. |
| 399 void assertErrorsInCode(String code, List<ErrorCode> errors) { |
| 400 Source source = addSource(code); |
| 401 computeLibrarySourceErrors(source); |
| 402 assertErrors(source, errors); |
| 403 verify([source]); |
| 404 } |
| 405 |
| 406 /** |
| 407 * Asserts that [code] has errors with the given error codes. |
| 408 * |
| 409 * Like [assertErrors], but takes a string of source code. |
| 410 */ |
| 411 void assertErrorsInUnverifiedCode(String code, List<ErrorCode> errors) { |
| 412 Source source = addSource(code); |
| 413 computeLibrarySourceErrors(source); |
| 414 assertErrors(source, errors); |
| 415 } |
| 416 |
| 417 /** |
| 418 * Assert that no errors have been reported against the given source. |
| 419 * |
| 420 * @param source the source against which no errors should have been reported |
| 421 * @throws AnalysisException if the reported errors could not be computed |
| 422 * @throws AssertionFailedError if any errors have been reported |
| 423 */ |
| 424 void assertNoErrors(Source source) { |
| 425 assertErrors(source); |
| 426 } |
| 427 |
| 428 /** |
| 429 * Asserts that [code] has no errors or warnings. |
| 430 */ |
| 431 // TODO(rnystrom): Use this in more tests that have the same structure. |
| 432 void assertNoErrorsInCode(String code) { |
| 433 Source source = addSource(code); |
| 434 computeLibrarySourceErrors(source); |
| 435 assertNoErrors(source); |
| 436 verify([source]); |
| 437 } |
| 438 |
| 439 /** |
| 440 * @param code the code that assigns the value to the variable "v", no matter
how. We check that |
| 441 * "v" has expected static and propagated type. |
| 442 */ |
| 443 void assertPropagatedAssignedType(String code, DartType expectedStaticType, |
| 444 DartType expectedPropagatedType) { |
| 445 SimpleIdentifier identifier = findMarkedIdentifier(code, "v = "); |
| 446 expect(identifier.staticType, same(expectedStaticType)); |
| 447 expect(identifier.propagatedType, same(expectedPropagatedType)); |
| 448 } |
| 449 |
| 450 /** |
| 451 * @param code the code that iterates using variable "v". We check that |
| 452 * "v" has expected static and propagated type. |
| 453 */ |
| 454 void assertPropagatedIterationType(String code, DartType expectedStaticType, |
| 455 DartType expectedPropagatedType) { |
| 456 SimpleIdentifier identifier = findMarkedIdentifier(code, "v in "); |
| 457 expect(identifier.staticType, same(expectedStaticType)); |
| 458 expect(identifier.propagatedType, same(expectedPropagatedType)); |
| 459 } |
| 460 |
| 461 /** |
| 462 * Check the static and propagated types of the expression marked with "; // m
arker" comment. |
| 463 * |
| 464 * @param code source code to analyze, with the expression to check marked wit
h "// marker". |
| 465 * @param expectedStaticType if non-null, check actual static type is equal to
this. |
| 466 * @param expectedPropagatedType if non-null, check actual static type is equa
l to this. |
| 467 * @throws Exception |
| 468 */ |
| 469 void assertTypeOfMarkedExpression(String code, DartType expectedStaticType, |
| 470 DartType expectedPropagatedType) { |
| 471 SimpleIdentifier identifier = findMarkedIdentifier(code, "; // marker"); |
| 472 if (expectedStaticType != null) { |
| 473 expect(identifier.staticType, expectedStaticType); |
| 474 } |
| 475 expect(identifier.propagatedType, expectedPropagatedType); |
| 476 } |
| 477 |
| 478 /** |
| 479 * Cache the [contents] for the file at the given [filePath] but don't add the |
| 480 * source to the analysis context. The file path must be absolute. |
| 481 */ |
| 482 Source cacheSource(String filePath, String contents) { |
| 483 Source source = resourceProvider.getFile(filePath).createSource(); |
| 484 analysisContext2.setContents(source, contents); |
| 485 return source; |
| 486 } |
| 487 |
| 488 /** |
| 489 * Change the contents of the given [source] to the given [contents]. |
| 490 */ |
| 491 void changeSource(Source source, String contents) { |
| 492 analysisContext2.setContents(source, contents); |
| 493 ChangeSet changeSet = new ChangeSet(); |
| 494 changeSet.changedSource(source); |
| 495 analysisContext2.applyChanges(changeSet); |
| 496 } |
| 497 |
| 498 /** |
| 499 * Computes errors for the given [librarySource]. |
| 500 * This assumes that the given [librarySource] and its parts have already |
| 501 * been added to the content provider using the method [addNamedSource]. |
| 502 */ |
| 503 void computeLibrarySourceErrors(Source librarySource) { |
| 504 analysisContext.computeErrors(librarySource); |
| 505 } |
| 506 |
| 507 /** |
| 508 * Create a library element that represents a library named `"test"` containin
g a single |
| 509 * empty compilation unit. |
| 510 * |
| 511 * @return the library element that was created |
| 512 */ |
| 513 LibraryElementImpl createDefaultTestLibrary() => |
| 514 createTestLibrary(AnalysisContextFactory.contextWithCore(), "test"); |
| 515 |
| 516 /** |
| 517 * Create a source object representing a file with the given [fileName] and |
| 518 * give it an empty content. Return the source that was created. |
| 519 */ |
| 520 Source createNamedSource(String fileName) { |
| 521 Source source = resourceProvider.getFile(fileName).createSource(); |
| 522 analysisContext2.setContents(source, ''); |
| 523 return source; |
| 524 } |
| 525 |
| 526 /** |
| 527 * Create a library element that represents a library with the given name cont
aining a single |
| 528 * empty compilation unit. |
| 529 * |
| 530 * @param libraryName the name of the library to be created |
| 531 * @return the library element that was created |
| 532 */ |
| 533 LibraryElementImpl createTestLibrary( |
| 534 AnalysisContext context, String libraryName, |
| 535 [List<String> typeNames]) { |
| 536 String fileName = "/test/$libraryName.dart"; |
| 537 Source definingCompilationUnitSource = createNamedSource(fileName); |
| 538 List<CompilationUnitElement> sourcedCompilationUnits; |
| 539 if (typeNames == null) { |
| 540 sourcedCompilationUnits = CompilationUnitElement.EMPTY_LIST; |
| 541 } else { |
| 542 int count = typeNames.length; |
| 543 sourcedCompilationUnits = new List<CompilationUnitElement>(count); |
| 544 for (int i = 0; i < count; i++) { |
| 545 String typeName = typeNames[i]; |
| 546 ClassElementImpl type = |
| 547 new ClassElementImpl.forNode(AstFactory.identifier3(typeName)); |
| 548 String fileName = "$typeName.dart"; |
| 549 CompilationUnitElementImpl compilationUnit = |
| 550 new CompilationUnitElementImpl(fileName); |
| 551 compilationUnit.source = createNamedSource(fileName); |
| 552 compilationUnit.librarySource = definingCompilationUnitSource; |
| 553 compilationUnit.types = <ClassElement>[type]; |
| 554 sourcedCompilationUnits[i] = compilationUnit; |
| 555 } |
| 556 } |
| 557 CompilationUnitElementImpl compilationUnit = |
| 558 new CompilationUnitElementImpl(fileName); |
| 559 compilationUnit.librarySource = |
| 560 compilationUnit.source = definingCompilationUnitSource; |
| 561 LibraryElementImpl library = new LibraryElementImpl.forNode( |
| 562 context, AstFactory.libraryIdentifier2([libraryName])); |
| 563 library.definingCompilationUnit = compilationUnit; |
| 564 library.parts = sourcedCompilationUnits; |
| 565 return library; |
| 566 } |
| 567 |
| 568 /** |
| 569 * Return the `SimpleIdentifier` marked by `marker`. The source code must have
no |
| 570 * errors and be verifiable. |
| 571 * |
| 572 * @param code source code to analyze. |
| 573 * @param marker marker identifying sought after expression in source code. |
| 574 * @return expression marked by the marker. |
| 575 * @throws Exception |
| 576 */ |
| 577 SimpleIdentifier findMarkedIdentifier(String code, String marker) { |
| 578 try { |
| 579 Source source = addSource(code); |
| 580 LibraryElement library = resolve2(source); |
| 581 assertNoErrors(source); |
| 582 verify([source]); |
| 583 CompilationUnit unit = resolveCompilationUnit(source, library); |
| 584 // Could generalize this further by making [SimpleIdentifier.class] a |
| 585 // parameter. |
| 586 return EngineTestCase.findNode( |
| 587 unit, code, marker, (node) => node is SimpleIdentifier); |
| 588 } catch (exception) { |
| 589 // Is there a better exception to throw here? The point is that an |
| 590 // assertion failure here should be a failure, in both "test_*" and |
| 591 // "fail_*" tests. However, an assertion failure is success for the |
| 592 // purpose of "fail_*" tests, so without catching them here "fail_*" tests |
| 593 // can succeed by failing for the wrong reason. |
| 594 throw new StateError("Unexpected assertion failure: $exception"); |
| 595 } |
| 596 } |
| 597 |
| 598 Expression findTopLevelConstantExpression( |
| 599 CompilationUnit compilationUnit, String name) => |
| 600 findTopLevelDeclaration(compilationUnit, name).initializer; |
| 601 |
| 602 VariableDeclaration findTopLevelDeclaration( |
| 603 CompilationUnit compilationUnit, String name) { |
| 604 for (CompilationUnitMember member in compilationUnit.declarations) { |
| 605 if (member is TopLevelVariableDeclaration) { |
| 606 for (VariableDeclaration variable in member.variables.variables) { |
| 607 if (variable.name.name == name) { |
| 608 return variable; |
| 609 } |
| 610 } |
| 611 } |
| 612 } |
| 613 return null; |
| 614 // Not found |
| 615 } |
| 616 |
| 617 /** |
| 618 * Re-create the analysis context being used by the test case. |
| 619 */ |
| 620 void reset() { |
| 621 analysisContext2 = AnalysisContextFactory.contextWithCore( |
| 622 resourceProvider: resourceProvider); |
| 623 } |
| 624 |
| 625 /** |
| 626 * Re-create the analysis context being used by the test case and set the |
| 627 * [options] in the newly created context to the given [options]. |
| 628 */ |
| 629 void resetWithOptions(AnalysisOptions options) { |
| 630 analysisContext2 = AnalysisContextFactory.contextWithCoreAndOptions(options, |
| 631 resourceProvider: resourceProvider); |
| 632 } |
| 633 |
| 634 /** |
| 635 * Given a library and all of its parts, resolve the contents of the library a
nd the contents of |
| 636 * the parts. This assumes that the sources for the library and its parts have
already been added |
| 637 * to the content provider using the method [addNamedSource]. |
| 638 * |
| 639 * @param librarySource the source for the compilation unit that defines the l
ibrary |
| 640 * @return the element representing the resolved library |
| 641 * @throws AnalysisException if the analysis could not be performed |
| 642 */ |
| 643 LibraryElement resolve2(Source librarySource) => |
| 644 analysisContext2.computeLibraryElement(librarySource); |
| 645 |
| 646 /** |
| 647 * Return the resolved compilation unit corresponding to the given source in t
he given library. |
| 648 * |
| 649 * @param source the source of the compilation unit to be returned |
| 650 * @param library the library in which the compilation unit is to be resolved |
| 651 * @return the resolved compilation unit |
| 652 * @throws Exception if the compilation unit could not be resolved |
| 653 */ |
| 654 CompilationUnit resolveCompilationUnit( |
| 655 Source source, LibraryElement library) => |
| 656 analysisContext2.resolveCompilationUnit(source, library); |
| 657 |
| 658 CompilationUnit resolveSource(String sourceText) => |
| 659 resolveSource2("/test.dart", sourceText); |
| 660 |
| 661 CompilationUnit resolveSource2(String fileName, String sourceText) { |
| 662 Source source = addNamedSource(fileName, sourceText); |
| 663 LibraryElement library = analysisContext.computeLibraryElement(source); |
| 664 return analysisContext.resolveCompilationUnit(source, library); |
| 665 } |
| 666 |
| 667 Source resolveSources(List<String> sourceTexts) { |
| 668 for (int i = 0; i < sourceTexts.length; i++) { |
| 669 CompilationUnit unit = |
| 670 resolveSource2("/lib${i + 1}.dart", sourceTexts[i]); |
| 671 // reference the source if this is the last source |
| 672 if (i + 1 == sourceTexts.length) { |
| 673 return unit.element.source; |
| 674 } |
| 675 } |
| 676 return null; |
| 677 } |
| 678 |
| 679 void resolveWithAndWithoutExperimental( |
| 680 List<String> strSources, |
| 681 List<ErrorCode> codesWithoutExperimental, |
| 682 List<ErrorCode> codesWithExperimental) { |
| 683 // Setup analysis context as non-experimental |
| 684 AnalysisOptionsImpl options = new AnalysisOptionsImpl(); |
| 685 // options.enableDeferredLoading = false; |
| 686 resetWithOptions(options); |
| 687 // Analysis and assertions |
| 688 Source source = resolveSources(strSources); |
| 689 assertErrors(source, codesWithoutExperimental); |
| 690 verify([source]); |
| 691 // Setup analysis context as experimental |
| 692 reset(); |
| 693 // Analysis and assertions |
| 694 source = resolveSources(strSources); |
| 695 assertErrors(source, codesWithExperimental); |
| 696 verify([source]); |
| 697 } |
| 698 |
| 699 void resolveWithErrors(List<String> strSources, List<ErrorCode> codes) { |
| 700 // Analysis and assertions |
| 701 Source source = resolveSources(strSources); |
| 702 assertErrors(source, codes); |
| 703 verify([source]); |
| 704 } |
| 705 |
| 706 @override |
| 707 void setUp() { |
| 708 ElementFactory.flushStaticState(); |
| 709 super.setUp(); |
| 710 reset(); |
| 711 } |
| 712 |
| 713 @override |
| 714 void tearDown() { |
| 715 analysisContext2 = null; |
| 716 super.tearDown(); |
| 717 } |
| 718 |
| 719 /** |
| 720 * Verify that all of the identifiers in the compilation units associated with |
| 721 * the given [sources] have been resolved. |
| 722 */ |
| 723 void verify(List<Source> sources) { |
| 724 ResolutionVerifier verifier = new ResolutionVerifier(); |
| 725 for (Source source in sources) { |
| 726 List<Source> libraries = analysisContext2.getLibrariesContaining(source); |
| 727 for (Source library in libraries) { |
| 728 analysisContext2 |
| 729 .resolveCompilationUnit2(source, library) |
| 730 .accept(verifier); |
| 731 } |
| 732 } |
| 733 verifier.assertResolved(); |
| 734 } |
| 735 } |
| 736 |
| 737 /** |
| 738 * Shared infrastructure for [StaticTypeAnalyzer2Test] and |
| 739 * [StrongModeStaticTypeAnalyzer2Test]. |
| 740 */ |
| 741 class StaticTypeAnalyzer2TestShared extends ResolverTestCase { |
| 742 String testCode; |
| 743 Source testSource; |
| 744 CompilationUnit testUnit; |
| 745 |
| 746 /** |
| 747 * Looks up the identifier with [name] and validates that its type type |
| 748 * stringifies to [type] and that its generics match the given stringified |
| 749 * output. |
| 750 */ |
| 751 expectFunctionType(String name, String type, |
| 752 {String elementTypeParams: '[]', |
| 753 String typeParams: '[]', |
| 754 String typeArgs: '[]', |
| 755 String typeFormals: '[]'}) { |
| 756 typeParameters(Element element) { |
| 757 if (element is ExecutableElement) { |
| 758 return element.typeParameters; |
| 759 } else if (element is ParameterElement) { |
| 760 return element.typeParameters; |
| 761 } |
| 762 fail('Wrong element type: ${element.runtimeType}'); |
| 763 } |
| 764 |
| 765 SimpleIdentifier identifier = findIdentifier(name); |
| 766 // Element is either ExecutableElement or ParameterElement. |
| 767 Element element = identifier.staticElement; |
| 768 FunctionTypeImpl functionType = identifier.staticType; |
| 769 expect(functionType.toString(), type); |
| 770 expect(typeParameters(element).toString(), elementTypeParams); |
| 771 expect(functionType.typeParameters.toString(), typeParams); |
| 772 expect(functionType.typeArguments.toString(), typeArgs); |
| 773 expect(functionType.typeFormals.toString(), typeFormals); |
| 774 } |
| 775 |
| 776 /** |
| 777 * Looks up the identifier with [name] and validates its static [type]. |
| 778 * |
| 779 * If [type] is a string, validates that the identifier's static type |
| 780 * stringifies to that text. Otherwise, [type] is used directly a [Matcher] |
| 781 * to match the type. |
| 782 * |
| 783 * If [propagatedType] is given, also validate's the identifier's propagated |
| 784 * type. |
| 785 */ |
| 786 void expectIdentifierType(String name, type, [propagatedType]) { |
| 787 SimpleIdentifier identifier = findIdentifier(name); |
| 788 _expectType(identifier.staticType, type); |
| 789 if (propagatedType != null) { |
| 790 _expectType(identifier.propagatedType, propagatedType); |
| 791 } |
| 792 } |
| 793 |
| 794 /** |
| 795 * Looks up the initializer for the declaration containing [identifier] and |
| 796 * validates its static [type]. |
| 797 * |
| 798 * If [type] is a string, validates that the identifier's static type |
| 799 * stringifies to that text. Otherwise, [type] is used directly a [Matcher] |
| 800 * to match the type. |
| 801 * |
| 802 * If [propagatedType] is given, also validate's the identifier's propagated |
| 803 * type. |
| 804 */ |
| 805 void expectInitializerType(String name, type, [propagatedType]) { |
| 806 SimpleIdentifier identifier = findIdentifier(name); |
| 807 VariableDeclaration declaration = |
| 808 identifier.getAncestor((node) => node is VariableDeclaration); |
| 809 Expression initializer = declaration.initializer; |
| 810 _expectType(initializer.staticType, type); |
| 811 if (propagatedType != null) { |
| 812 _expectType(initializer.propagatedType, propagatedType); |
| 813 } |
| 814 } |
| 815 |
| 816 SimpleIdentifier findIdentifier(String search) { |
| 817 SimpleIdentifier identifier = EngineTestCase.findNode( |
| 818 testUnit, testCode, search, (node) => node is SimpleIdentifier); |
| 819 return identifier; |
| 820 } |
| 821 |
| 822 void resolveTestUnit(String code) { |
| 823 testCode = code; |
| 824 testSource = addSource(testCode); |
| 825 LibraryElement library = resolve2(testSource); |
| 826 assertNoErrors(testSource); |
| 827 verify([testSource]); |
| 828 testUnit = resolveCompilationUnit(testSource, library); |
| 829 } |
| 830 |
| 831 /** |
| 832 * Validates that [type] matches [expected]. |
| 833 * |
| 834 * If [expected] is a string, validates that the type stringifies to that |
| 835 * text. Otherwise, [expected] is used directly a [Matcher] to match the type. |
| 836 */ |
| 837 _expectType(DartType type, expected) { |
| 838 if (expected is String) { |
| 839 expect(type.toString(), expected); |
| 840 } else { |
| 841 expect(type, expected); |
| 842 } |
| 843 } |
| 844 } |
| OLD | NEW |