| 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 | 4 |
| 5 library analyzer.test.generated.resolver_test_case; | 5 library analyzer.test.generated.resolver_test_case; |
| 6 | 6 |
| 7 import 'package:analyzer/dart/ast/ast.dart'; | 7 import 'package:analyzer/dart/ast/ast.dart'; |
| 8 import 'package:analyzer/dart/ast/visitor.dart'; | 8 import 'package:analyzer/dart/ast/visitor.dart'; |
| 9 import 'package:analyzer/dart/element/element.dart'; | 9 import 'package:analyzer/dart/element/element.dart'; |
| 10 import 'package:analyzer/dart/element/type.dart'; | 10 import 'package:analyzer/dart/element/type.dart'; |
| 11 import 'package:analyzer/src/dart/element/element.dart'; | 11 import 'package:analyzer/src/dart/element/element.dart'; |
| 12 import 'package:analyzer/src/dart/element/type.dart'; | 12 import 'package:analyzer/src/dart/element/type.dart'; |
| 13 import 'package:analyzer/src/generated/engine.dart'; | 13 import 'package:analyzer/src/generated/engine.dart'; |
| 14 import 'package:analyzer/src/generated/error.dart'; | 14 import 'package:analyzer/src/generated/error.dart'; |
| 15 import 'package:analyzer/src/generated/java_core.dart'; | 15 import 'package:analyzer/src/generated/java_core.dart'; |
| 16 import 'package:analyzer/src/generated/java_engine.dart'; | 16 import 'package:analyzer/src/generated/java_engine.dart'; |
| 17 import 'package:analyzer/src/generated/java_engine_io.dart'; | 17 import 'package:analyzer/src/generated/java_engine_io.dart'; |
| 18 import 'package:analyzer/src/generated/resolver.dart'; | 18 import 'package:analyzer/src/generated/resolver.dart'; |
| 19 import 'package:analyzer/src/generated/source_io.dart'; | 19 import 'package:analyzer/src/generated/source_io.dart'; |
| 20 import 'package:analyzer/src/generated/testing/ast_factory.dart'; | 20 import 'package:analyzer/src/generated/testing/ast_factory.dart'; |
| 21 import 'package:analyzer/src/generated/testing/element_factory.dart'; | 21 import 'package:analyzer/src/generated/testing/element_factory.dart'; |
| 22 import 'package:unittest/unittest.dart'; | 22 import 'package:unittest/unittest.dart'; |
| 23 | 23 |
| 24 import 'analysis_context_factory.dart'; | 24 import 'analysis_context_factory.dart'; |
| 25 import 'test_support.dart'; | 25 import 'test_support.dart'; |
| 26 | 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 |
| 27 class ResolverTestCase extends EngineTestCase { | 300 class ResolverTestCase extends EngineTestCase { |
| 28 /** | 301 /** |
| 29 * The analysis context used to parse the compilation units being resolved. | 302 * The analysis context used to parse the compilation units being resolved. |
| 30 */ | 303 */ |
| 31 InternalAnalysisContext analysisContext2; | 304 InternalAnalysisContext analysisContext2; |
| 32 | 305 |
| 33 /** | 306 /** |
| 34 * Specifies if [assertErrors] should check for [HintCode.UNUSED_ELEMENT] and | 307 * Specifies if [assertErrors] should check for [HintCode.UNUSED_ELEMENT] and |
| 35 * [HintCode.UNUSED_FIELD]. | 308 * [HintCode.UNUSED_FIELD]. |
| 36 */ | 309 */ |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 */ | 427 */ |
| 155 // TODO(rnystrom): Use this in more tests that have the same structure. | 428 // TODO(rnystrom): Use this in more tests that have the same structure. |
| 156 void assertNoErrorsInCode(String code) { | 429 void assertNoErrorsInCode(String code) { |
| 157 Source source = addSource(code); | 430 Source source = addSource(code); |
| 158 computeLibrarySourceErrors(source); | 431 computeLibrarySourceErrors(source); |
| 159 assertNoErrors(source); | 432 assertNoErrors(source); |
| 160 verify([source]); | 433 verify([source]); |
| 161 } | 434 } |
| 162 | 435 |
| 163 /** | 436 /** |
| 164 * Cache the source file content in the source factory but don't add the sourc
e to the analysis | |
| 165 * context. The file path should be absolute. | |
| 166 * | |
| 167 * @param filePath the path of the file being cached | |
| 168 * @param contents the contents to be returned by the content provider for the
specified file | |
| 169 * @return the source object representing the cached file | |
| 170 */ | |
| 171 Source cacheSource(String filePath, String contents) { | |
| 172 Source source = new FileBasedSource(FileUtilities2.createFile(filePath)); | |
| 173 analysisContext2.setContents(source, contents); | |
| 174 return source; | |
| 175 } | |
| 176 | |
| 177 /** | |
| 178 * Change the contents of the given [source] to the given [contents]. | |
| 179 */ | |
| 180 void changeSource(Source source, String contents) { | |
| 181 analysisContext2.setContents(source, contents); | |
| 182 ChangeSet changeSet = new ChangeSet(); | |
| 183 changeSet.changedSource(source); | |
| 184 analysisContext2.applyChanges(changeSet); | |
| 185 } | |
| 186 | |
| 187 /** | |
| 188 * Computes errors for the given [librarySource]. | |
| 189 * This assumes that the given [librarySource] and its parts have already | |
| 190 * been added to the content provider using the method [addNamedSource]. | |
| 191 */ | |
| 192 void computeLibrarySourceErrors(Source librarySource) { | |
| 193 analysisContext.computeErrors(librarySource); | |
| 194 } | |
| 195 | |
| 196 /** | |
| 197 * Create a library element that represents a library named `"test"` containin
g a single | |
| 198 * empty compilation unit. | |
| 199 * | |
| 200 * @return the library element that was created | |
| 201 */ | |
| 202 LibraryElementImpl createDefaultTestLibrary() => | |
| 203 createTestLibrary(AnalysisContextFactory.contextWithCore(), "test"); | |
| 204 | |
| 205 /** | |
| 206 * Create a library element that represents a library with the given name cont
aining a single | |
| 207 * empty compilation unit. | |
| 208 * | |
| 209 * @param libraryName the name of the library to be created | |
| 210 * @return the library element that was created | |
| 211 */ | |
| 212 LibraryElementImpl createTestLibrary( | |
| 213 AnalysisContext context, String libraryName, | |
| 214 [List<String> typeNames]) { | |
| 215 String fileName = "$libraryName.dart"; | |
| 216 FileBasedSource definingCompilationUnitSource = | |
| 217 createNamedSource(fileName); | |
| 218 List<CompilationUnitElement> sourcedCompilationUnits; | |
| 219 if (typeNames == null) { | |
| 220 sourcedCompilationUnits = CompilationUnitElement.EMPTY_LIST; | |
| 221 } else { | |
| 222 int count = typeNames.length; | |
| 223 sourcedCompilationUnits = new List<CompilationUnitElement>(count); | |
| 224 for (int i = 0; i < count; i++) { | |
| 225 String typeName = typeNames[i]; | |
| 226 ClassElementImpl type = | |
| 227 new ClassElementImpl.forNode(AstFactory.identifier3(typeName)); | |
| 228 String fileName = "$typeName.dart"; | |
| 229 CompilationUnitElementImpl compilationUnit = | |
| 230 new CompilationUnitElementImpl(fileName); | |
| 231 compilationUnit.source = createNamedSource(fileName); | |
| 232 compilationUnit.librarySource = definingCompilationUnitSource; | |
| 233 compilationUnit.types = <ClassElement>[type]; | |
| 234 sourcedCompilationUnits[i] = compilationUnit; | |
| 235 } | |
| 236 } | |
| 237 CompilationUnitElementImpl compilationUnit = | |
| 238 new CompilationUnitElementImpl(fileName); | |
| 239 compilationUnit.librarySource = | |
| 240 compilationUnit.source = definingCompilationUnitSource; | |
| 241 LibraryElementImpl library = new LibraryElementImpl.forNode( | |
| 242 context, AstFactory.libraryIdentifier2([libraryName])); | |
| 243 library.definingCompilationUnit = compilationUnit; | |
| 244 library.parts = sourcedCompilationUnits; | |
| 245 return library; | |
| 246 } | |
| 247 | |
| 248 Expression findTopLevelConstantExpression( | |
| 249 CompilationUnit compilationUnit, String name) => | |
| 250 findTopLevelDeclaration(compilationUnit, name).initializer; | |
| 251 | |
| 252 VariableDeclaration findTopLevelDeclaration( | |
| 253 CompilationUnit compilationUnit, String name) { | |
| 254 for (CompilationUnitMember member in compilationUnit.declarations) { | |
| 255 if (member is TopLevelVariableDeclaration) { | |
| 256 for (VariableDeclaration variable in member.variables.variables) { | |
| 257 if (variable.name.name == name) { | |
| 258 return variable; | |
| 259 } | |
| 260 } | |
| 261 } | |
| 262 } | |
| 263 return null; | |
| 264 // Not found | |
| 265 } | |
| 266 | |
| 267 /** | |
| 268 * In the rare cases we want to group several tests into single "test_" method
, so need a way to | |
| 269 * reset test instance to reuse it. | |
| 270 */ | |
| 271 void reset() { | |
| 272 analysisContext2 = AnalysisContextFactory.contextWithCore(); | |
| 273 } | |
| 274 | |
| 275 /** | |
| 276 * Reset the analysis context to have the given options applied. | |
| 277 * | |
| 278 * @param options the analysis options to be applied to the context | |
| 279 */ | |
| 280 void resetWithOptions(AnalysisOptions options) { | |
| 281 analysisContext2 = | |
| 282 AnalysisContextFactory.contextWithCoreAndOptions(options); | |
| 283 } | |
| 284 | |
| 285 /** | |
| 286 * Given a library and all of its parts, resolve the contents of the library a
nd the contents of | |
| 287 * the parts. This assumes that the sources for the library and its parts have
already been added | |
| 288 * to the content provider using the method [addNamedSource]. | |
| 289 * | |
| 290 * @param librarySource the source for the compilation unit that defines the l
ibrary | |
| 291 * @return the element representing the resolved library | |
| 292 * @throws AnalysisException if the analysis could not be performed | |
| 293 */ | |
| 294 LibraryElement resolve2(Source librarySource) => | |
| 295 analysisContext2.computeLibraryElement(librarySource); | |
| 296 | |
| 297 /** | |
| 298 * Return the resolved compilation unit corresponding to the given source in t
he given library. | |
| 299 * | |
| 300 * @param source the source of the compilation unit to be returned | |
| 301 * @param library the library in which the compilation unit is to be resolved | |
| 302 * @return the resolved compilation unit | |
| 303 * @throws Exception if the compilation unit could not be resolved | |
| 304 */ | |
| 305 CompilationUnit resolveCompilationUnit( | |
| 306 Source source, LibraryElement library) => | |
| 307 analysisContext2.resolveCompilationUnit(source, library); | |
| 308 | |
| 309 CompilationUnit resolveSource(String sourceText) => | |
| 310 resolveSource2("/test.dart", sourceText); | |
| 311 | |
| 312 CompilationUnit resolveSource2(String fileName, String sourceText) { | |
| 313 Source source = addNamedSource(fileName, sourceText); | |
| 314 LibraryElement library = analysisContext.computeLibraryElement(source); | |
| 315 return analysisContext.resolveCompilationUnit(source, library); | |
| 316 } | |
| 317 | |
| 318 Source resolveSources(List<String> sourceTexts) { | |
| 319 for (int i = 0; i < sourceTexts.length; i++) { | |
| 320 CompilationUnit unit = | |
| 321 resolveSource2("/lib${i + 1}.dart", sourceTexts[i]); | |
| 322 // reference the source if this is the last source | |
| 323 if (i + 1 == sourceTexts.length) { | |
| 324 return unit.element.source; | |
| 325 } | |
| 326 } | |
| 327 return null; | |
| 328 } | |
| 329 | |
| 330 void resolveWithAndWithoutExperimental( | |
| 331 List<String> strSources, | |
| 332 List<ErrorCode> codesWithoutExperimental, | |
| 333 List<ErrorCode> codesWithExperimental) { | |
| 334 // Setup analysis context as non-experimental | |
| 335 AnalysisOptionsImpl options = new AnalysisOptionsImpl(); | |
| 336 // options.enableDeferredLoading = false; | |
| 337 resetWithOptions(options); | |
| 338 // Analysis and assertions | |
| 339 Source source = resolveSources(strSources); | |
| 340 assertErrors(source, codesWithoutExperimental); | |
| 341 verify([source]); | |
| 342 // Setup analysis context as experimental | |
| 343 reset(); | |
| 344 // Analysis and assertions | |
| 345 source = resolveSources(strSources); | |
| 346 assertErrors(source, codesWithExperimental); | |
| 347 verify([source]); | |
| 348 } | |
| 349 | |
| 350 void resolveWithErrors(List<String> strSources, List<ErrorCode> codes) { | |
| 351 // Analysis and assertions | |
| 352 Source source = resolveSources(strSources); | |
| 353 assertErrors(source, codes); | |
| 354 verify([source]); | |
| 355 } | |
| 356 | |
| 357 @override | |
| 358 void setUp() { | |
| 359 ElementFactory.flushStaticState(); | |
| 360 super.setUp(); | |
| 361 reset(); | |
| 362 } | |
| 363 | |
| 364 @override | |
| 365 void tearDown() { | |
| 366 analysisContext2 = null; | |
| 367 super.tearDown(); | |
| 368 } | |
| 369 | |
| 370 /** | |
| 371 * Verify that all of the identifiers in the compilation units associated with | |
| 372 * the given [sources] have been resolved. | |
| 373 */ | |
| 374 void verify(List<Source> sources) { | |
| 375 ResolutionVerifier verifier = new ResolutionVerifier(); | |
| 376 for (Source source in sources) { | |
| 377 List<Source> libraries = analysisContext2.getLibrariesContaining(source); | |
| 378 for (Source library in libraries) { | |
| 379 analysisContext2 | |
| 380 .resolveCompilationUnit2(source, library) | |
| 381 .accept(verifier); | |
| 382 } | |
| 383 } | |
| 384 verifier.assertResolved(); | |
| 385 } | |
| 386 | |
| 387 /** | |
| 388 * @param code the code that assigns the value to the variable "v", no matter
how. We check that | 437 * @param code the code that assigns the value to the variable "v", no matter
how. We check that |
| 389 * "v" has expected static and propagated type. | 438 * "v" has expected static and propagated type. |
| 390 */ | 439 */ |
| 391 void assertPropagatedAssignedType(String code, DartType expectedStaticType, | 440 void assertPropagatedAssignedType(String code, DartType expectedStaticType, |
| 392 DartType expectedPropagatedType) { | 441 DartType expectedPropagatedType) { |
| 393 SimpleIdentifier identifier = findMarkedIdentifier(code, "v = "); | 442 SimpleIdentifier identifier = findMarkedIdentifier(code, "v = "); |
| 394 expect(identifier.staticType, same(expectedStaticType)); | 443 expect(identifier.staticType, same(expectedStaticType)); |
| 395 expect(identifier.propagatedType, same(expectedPropagatedType)); | 444 expect(identifier.propagatedType, same(expectedPropagatedType)); |
| 396 } | 445 } |
| 397 | 446 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 417 void assertTypeOfMarkedExpression(String code, DartType expectedStaticType, | 466 void assertTypeOfMarkedExpression(String code, DartType expectedStaticType, |
| 418 DartType expectedPropagatedType) { | 467 DartType expectedPropagatedType) { |
| 419 SimpleIdentifier identifier = findMarkedIdentifier(code, "; // marker"); | 468 SimpleIdentifier identifier = findMarkedIdentifier(code, "; // marker"); |
| 420 if (expectedStaticType != null) { | 469 if (expectedStaticType != null) { |
| 421 expect(identifier.staticType, expectedStaticType); | 470 expect(identifier.staticType, expectedStaticType); |
| 422 } | 471 } |
| 423 expect(identifier.propagatedType, expectedPropagatedType); | 472 expect(identifier.propagatedType, expectedPropagatedType); |
| 424 } | 473 } |
| 425 | 474 |
| 426 /** | 475 /** |
| 476 * Cache the source file content in the source factory but don't add the sourc
e to the analysis |
| 477 * context. The file path should be absolute. |
| 478 * |
| 479 * @param filePath the path of the file being cached |
| 480 * @param contents the contents to be returned by the content provider for the
specified file |
| 481 * @return the source object representing the cached file |
| 482 */ |
| 483 Source cacheSource(String filePath, String contents) { |
| 484 Source source = new FileBasedSource(FileUtilities2.createFile(filePath)); |
| 485 analysisContext2.setContents(source, contents); |
| 486 return source; |
| 487 } |
| 488 |
| 489 /** |
| 490 * Change the contents of the given [source] to the given [contents]. |
| 491 */ |
| 492 void changeSource(Source source, String contents) { |
| 493 analysisContext2.setContents(source, contents); |
| 494 ChangeSet changeSet = new ChangeSet(); |
| 495 changeSet.changedSource(source); |
| 496 analysisContext2.applyChanges(changeSet); |
| 497 } |
| 498 |
| 499 /** |
| 500 * Computes errors for the given [librarySource]. |
| 501 * This assumes that the given [librarySource] and its parts have already |
| 502 * been added to the content provider using the method [addNamedSource]. |
| 503 */ |
| 504 void computeLibrarySourceErrors(Source librarySource) { |
| 505 analysisContext.computeErrors(librarySource); |
| 506 } |
| 507 |
| 508 /** |
| 509 * Create a library element that represents a library named `"test"` containin
g a single |
| 510 * empty compilation unit. |
| 511 * |
| 512 * @return the library element that was created |
| 513 */ |
| 514 LibraryElementImpl createDefaultTestLibrary() => |
| 515 createTestLibrary(AnalysisContextFactory.contextWithCore(), "test"); |
| 516 |
| 517 /** |
| 427 * Create a source object representing a file with the given [fileName] and | 518 * Create a source object representing a file with the given [fileName] and |
| 428 * give it an empty content. Return the source that was created. | 519 * give it an empty content. Return the source that was created. |
| 429 */ | 520 */ |
| 430 FileBasedSource createNamedSource(String fileName) { | 521 FileBasedSource createNamedSource(String fileName) { |
| 431 FileBasedSource source = | 522 FileBasedSource source = |
| 432 new FileBasedSource(FileUtilities2.createFile(fileName)); | 523 new FileBasedSource(FileUtilities2.createFile(fileName)); |
| 433 analysisContext2.setContents(source, ""); | 524 analysisContext2.setContents(source, ""); |
| 434 return source; | 525 return source; |
| 435 } | 526 } |
| 436 | 527 |
| 437 /** | 528 /** |
| 529 * Create a library element that represents a library with the given name cont
aining a single |
| 530 * empty compilation unit. |
| 531 * |
| 532 * @param libraryName the name of the library to be created |
| 533 * @return the library element that was created |
| 534 */ |
| 535 LibraryElementImpl createTestLibrary( |
| 536 AnalysisContext context, String libraryName, |
| 537 [List<String> typeNames]) { |
| 538 String fileName = "$libraryName.dart"; |
| 539 FileBasedSource definingCompilationUnitSource = |
| 540 createNamedSource(fileName); |
| 541 List<CompilationUnitElement> sourcedCompilationUnits; |
| 542 if (typeNames == null) { |
| 543 sourcedCompilationUnits = CompilationUnitElement.EMPTY_LIST; |
| 544 } else { |
| 545 int count = typeNames.length; |
| 546 sourcedCompilationUnits = new List<CompilationUnitElement>(count); |
| 547 for (int i = 0; i < count; i++) { |
| 548 String typeName = typeNames[i]; |
| 549 ClassElementImpl type = |
| 550 new ClassElementImpl.forNode(AstFactory.identifier3(typeName)); |
| 551 String fileName = "$typeName.dart"; |
| 552 CompilationUnitElementImpl compilationUnit = |
| 553 new CompilationUnitElementImpl(fileName); |
| 554 compilationUnit.source = createNamedSource(fileName); |
| 555 compilationUnit.librarySource = definingCompilationUnitSource; |
| 556 compilationUnit.types = <ClassElement>[type]; |
| 557 sourcedCompilationUnits[i] = compilationUnit; |
| 558 } |
| 559 } |
| 560 CompilationUnitElementImpl compilationUnit = |
| 561 new CompilationUnitElementImpl(fileName); |
| 562 compilationUnit.librarySource = |
| 563 compilationUnit.source = definingCompilationUnitSource; |
| 564 LibraryElementImpl library = new LibraryElementImpl.forNode( |
| 565 context, AstFactory.libraryIdentifier2([libraryName])); |
| 566 library.definingCompilationUnit = compilationUnit; |
| 567 library.parts = sourcedCompilationUnits; |
| 568 return library; |
| 569 } |
| 570 |
| 571 /** |
| 438 * Return the `SimpleIdentifier` marked by `marker`. The source code must have
no | 572 * Return the `SimpleIdentifier` marked by `marker`. The source code must have
no |
| 439 * errors and be verifiable. | 573 * errors and be verifiable. |
| 440 * | 574 * |
| 441 * @param code source code to analyze. | 575 * @param code source code to analyze. |
| 442 * @param marker marker identifying sought after expression in source code. | 576 * @param marker marker identifying sought after expression in source code. |
| 443 * @return expression marked by the marker. | 577 * @return expression marked by the marker. |
| 444 * @throws Exception | 578 * @throws Exception |
| 445 */ | 579 */ |
| 446 SimpleIdentifier findMarkedIdentifier(String code, String marker) { | 580 SimpleIdentifier findMarkedIdentifier(String code, String marker) { |
| 447 try { | 581 try { |
| 448 Source source = addSource(code); | 582 Source source = addSource(code); |
| 449 LibraryElement library = resolve2(source); | 583 LibraryElement library = resolve2(source); |
| 450 assertNoErrors(source); | 584 assertNoErrors(source); |
| 451 verify([source]); | 585 verify([source]); |
| 452 CompilationUnit unit = resolveCompilationUnit(source, library); | 586 CompilationUnit unit = resolveCompilationUnit(source, library); |
| 453 // Could generalize this further by making [SimpleIdentifier.class] a | 587 // Could generalize this further by making [SimpleIdentifier.class] a |
| 454 // parameter. | 588 // parameter. |
| 455 return EngineTestCase.findNode( | 589 return EngineTestCase.findNode( |
| 456 unit, code, marker, (node) => node is SimpleIdentifier); | 590 unit, code, marker, (node) => node is SimpleIdentifier); |
| 457 } catch (exception) { | 591 } catch (exception) { |
| 458 // Is there a better exception to throw here? The point is that an | 592 // Is there a better exception to throw here? The point is that an |
| 459 // assertion failure here should be a failure, in both "test_*" and | 593 // assertion failure here should be a failure, in both "test_*" and |
| 460 // "fail_*" tests. However, an assertion failure is success for the | 594 // "fail_*" tests. However, an assertion failure is success for the |
| 461 // purpose of "fail_*" tests, so without catching them here "fail_*" tests | 595 // purpose of "fail_*" tests, so without catching them here "fail_*" tests |
| 462 // can succeed by failing for the wrong reason. | 596 // can succeed by failing for the wrong reason. |
| 463 throw new JavaException("Unexexpected assertion failure: $exception"); | 597 throw new JavaException("Unexexpected assertion failure: $exception"); |
| 464 } | 598 } |
| 465 } | 599 } |
| 466 } | |
| 467 | 600 |
| 468 /** | 601 Expression findTopLevelConstantExpression( |
| 469 * An AST visitor used to verify that all of the nodes in an AST structure that | 602 CompilationUnit compilationUnit, String name) => |
| 470 * should have been resolved were resolved. | 603 findTopLevelDeclaration(compilationUnit, name).initializer; |
| 471 */ | |
| 472 class ResolutionVerifier extends RecursiveAstVisitor<Object> { | |
| 473 /** | |
| 474 * A set containing nodes that are known to not be resolvable and should | |
| 475 * therefore not cause the test to fail. | |
| 476 */ | |
| 477 final Set<AstNode> _knownExceptions; | |
| 478 | 604 |
| 479 /** | 605 VariableDeclaration findTopLevelDeclaration( |
| 480 * A list containing all of the AST nodes that were not resolved. | 606 CompilationUnit compilationUnit, String name) { |
| 481 */ | 607 for (CompilationUnitMember member in compilationUnit.declarations) { |
| 482 List<AstNode> _unresolvedNodes = new List<AstNode>(); | 608 if (member is TopLevelVariableDeclaration) { |
| 483 | 609 for (VariableDeclaration variable in member.variables.variables) { |
| 484 /** | 610 if (variable.name.name == name) { |
| 485 * A list containing all of the AST nodes that were resolved to an element of | 611 return variable; |
| 486 * the wrong type. | 612 } |
| 487 */ | |
| 488 List<AstNode> _wrongTypedNodes = new List<AstNode>(); | |
| 489 | |
| 490 /** | |
| 491 * Initialize a newly created verifier to verify that all of the identifiers | |
| 492 * in the visited AST structures that are expected to have been resolved have | |
| 493 * an element associated with them. Nodes in the set of [_knownExceptions] are | |
| 494 * not expected to have been resolved, even if they normally would have been | |
| 495 * expected to have been resolved. | |
| 496 */ | |
| 497 ResolutionVerifier([this._knownExceptions]); | |
| 498 | |
| 499 /** | |
| 500 * Assert that all of the visited identifiers were resolved. | |
| 501 */ | |
| 502 void assertResolved() { | |
| 503 if (!_unresolvedNodes.isEmpty || !_wrongTypedNodes.isEmpty) { | |
| 504 StringBuffer buffer = new StringBuffer(); | |
| 505 if (!_unresolvedNodes.isEmpty) { | |
| 506 buffer.write("Failed to resolve "); | |
| 507 buffer.write(_unresolvedNodes.length); | |
| 508 buffer.writeln(" nodes:"); | |
| 509 _printNodes(buffer, _unresolvedNodes); | |
| 510 } | |
| 511 if (!_wrongTypedNodes.isEmpty) { | |
| 512 buffer.write("Resolved "); | |
| 513 buffer.write(_wrongTypedNodes.length); | |
| 514 buffer.writeln(" to the wrong type of element:"); | |
| 515 _printNodes(buffer, _wrongTypedNodes); | |
| 516 } | |
| 517 fail(buffer.toString()); | |
| 518 } | |
| 519 } | |
| 520 | |
| 521 @override | |
| 522 Object visitAnnotation(Annotation node) { | |
| 523 node.visitChildren(this); | |
| 524 ElementAnnotation elementAnnotation = node.elementAnnotation; | |
| 525 if (elementAnnotation == null) { | |
| 526 if (_knownExceptions == null || !_knownExceptions.contains(node)) { | |
| 527 _unresolvedNodes.add(node); | |
| 528 } | |
| 529 } else if (elementAnnotation is! ElementAnnotation) { | |
| 530 _wrongTypedNodes.add(node); | |
| 531 } | |
| 532 return null; | |
| 533 } | |
| 534 | |
| 535 @override | |
| 536 Object visitBinaryExpression(BinaryExpression node) { | |
| 537 node.visitChildren(this); | |
| 538 if (!node.operator.isUserDefinableOperator) { | |
| 539 return null; | |
| 540 } | |
| 541 DartType operandType = node.leftOperand.staticType; | |
| 542 if (operandType == null || operandType.isDynamic) { | |
| 543 return null; | |
| 544 } | |
| 545 return _checkResolved( | |
| 546 node, node.staticElement, (node) => node is MethodElement); | |
| 547 } | |
| 548 | |
| 549 @override | |
| 550 Object visitCommentReference(CommentReference node) => null; | |
| 551 | |
| 552 @override | |
| 553 Object visitCompilationUnit(CompilationUnit node) { | |
| 554 node.visitChildren(this); | |
| 555 return _checkResolved( | |
| 556 node, node.element, (node) => node is CompilationUnitElement); | |
| 557 } | |
| 558 | |
| 559 @override | |
| 560 Object visitExportDirective(ExportDirective node) => | |
| 561 _checkResolved(node, node.element, (node) => node is ExportElement); | |
| 562 | |
| 563 @override | |
| 564 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 565 node.visitChildren(this); | |
| 566 if (node.element is LibraryElement) { | |
| 567 _wrongTypedNodes.add(node); | |
| 568 } | |
| 569 return null; | |
| 570 } | |
| 571 | |
| 572 @override | |
| 573 Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | |
| 574 node.visitChildren(this); | |
| 575 // TODO(brianwilkerson) If we start resolving function expressions, then | |
| 576 // conditionally check to see whether the node was resolved correctly. | |
| 577 return null; | |
| 578 //checkResolved(node, node.getElement(), FunctionElement.class); | |
| 579 } | |
| 580 | |
| 581 @override | |
| 582 Object visitImportDirective(ImportDirective node) { | |
| 583 // Not sure how to test the combinators given that it isn't an error if the | |
| 584 // names are not defined. | |
| 585 _checkResolved(node, node.element, (node) => node is ImportElement); | |
| 586 SimpleIdentifier prefix = node.prefix; | |
| 587 if (prefix == null) { | |
| 588 return null; | |
| 589 } | |
| 590 return _checkResolved( | |
| 591 prefix, prefix.staticElement, (node) => node is PrefixElement); | |
| 592 } | |
| 593 | |
| 594 @override | |
| 595 Object visitIndexExpression(IndexExpression node) { | |
| 596 node.visitChildren(this); | |
| 597 DartType targetType = node.realTarget.staticType; | |
| 598 if (targetType == null || targetType.isDynamic) { | |
| 599 return null; | |
| 600 } | |
| 601 return _checkResolved( | |
| 602 node, node.staticElement, (node) => node is MethodElement); | |
| 603 } | |
| 604 | |
| 605 @override | |
| 606 Object visitLibraryDirective(LibraryDirective node) => | |
| 607 _checkResolved(node, node.element, (node) => node is LibraryElement); | |
| 608 | |
| 609 @override | |
| 610 Object visitNamedExpression(NamedExpression node) => | |
| 611 node.expression.accept(this); | |
| 612 | |
| 613 @override | |
| 614 Object visitPartDirective(PartDirective node) => _checkResolved( | |
| 615 node, node.element, (node) => node is CompilationUnitElement); | |
| 616 | |
| 617 @override | |
| 618 Object visitPartOfDirective(PartOfDirective node) => | |
| 619 _checkResolved(node, node.element, (node) => node is LibraryElement); | |
| 620 | |
| 621 @override | |
| 622 Object visitPostfixExpression(PostfixExpression node) { | |
| 623 node.visitChildren(this); | |
| 624 if (!node.operator.isUserDefinableOperator) { | |
| 625 return null; | |
| 626 } | |
| 627 DartType operandType = node.operand.staticType; | |
| 628 if (operandType == null || operandType.isDynamic) { | |
| 629 return null; | |
| 630 } | |
| 631 return _checkResolved( | |
| 632 node, node.staticElement, (node) => node is MethodElement); | |
| 633 } | |
| 634 | |
| 635 @override | |
| 636 Object visitPrefixedIdentifier(PrefixedIdentifier node) { | |
| 637 SimpleIdentifier prefix = node.prefix; | |
| 638 prefix.accept(this); | |
| 639 DartType prefixType = prefix.staticType; | |
| 640 if (prefixType == null || prefixType.isDynamic) { | |
| 641 return null; | |
| 642 } | |
| 643 return _checkResolved(node, node.staticElement, null); | |
| 644 } | |
| 645 | |
| 646 @override | |
| 647 Object visitPrefixExpression(PrefixExpression node) { | |
| 648 node.visitChildren(this); | |
| 649 if (!node.operator.isUserDefinableOperator) { | |
| 650 return null; | |
| 651 } | |
| 652 DartType operandType = node.operand.staticType; | |
| 653 if (operandType == null || operandType.isDynamic) { | |
| 654 return null; | |
| 655 } | |
| 656 return _checkResolved( | |
| 657 node, node.staticElement, (node) => node is MethodElement); | |
| 658 } | |
| 659 | |
| 660 @override | |
| 661 Object visitPropertyAccess(PropertyAccess node) { | |
| 662 Expression target = node.realTarget; | |
| 663 target.accept(this); | |
| 664 DartType targetType = target.staticType; | |
| 665 if (targetType == null || targetType.isDynamic) { | |
| 666 return null; | |
| 667 } | |
| 668 return node.propertyName.accept(this); | |
| 669 } | |
| 670 | |
| 671 @override | |
| 672 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
| 673 if (node.name == "void") { | |
| 674 return null; | |
| 675 } | |
| 676 if (node.staticType != null && | |
| 677 node.staticType.isDynamic && | |
| 678 node.staticElement == null) { | |
| 679 return null; | |
| 680 } | |
| 681 AstNode parent = node.parent; | |
| 682 if (parent is MethodInvocation) { | |
| 683 MethodInvocation invocation = parent; | |
| 684 if (identical(invocation.methodName, node)) { | |
| 685 Expression target = invocation.realTarget; | |
| 686 DartType targetType = target == null ? null : target.staticType; | |
| 687 if (targetType == null || targetType.isDynamic) { | |
| 688 return null; | |
| 689 } | 613 } |
| 690 } | 614 } |
| 691 } | 615 } |
| 692 return _checkResolved(node, node.staticElement, null); | 616 return null; |
| 617 // Not found |
| 693 } | 618 } |
| 694 | 619 |
| 695 Object _checkResolved( | 620 /** |
| 696 AstNode node, Element element, Predicate<Element> predicate) { | 621 * In the rare cases we want to group several tests into single "test_" method
, so need a way to |
| 697 if (element == null) { | 622 * reset test instance to reuse it. |
| 698 if (_knownExceptions == null || !_knownExceptions.contains(node)) { | 623 */ |
| 699 _unresolvedNodes.add(node); | 624 void reset() { |
| 700 } | 625 analysisContext2 = AnalysisContextFactory.contextWithCore(); |
| 701 } else if (predicate != null) { | 626 } |
| 702 if (!predicate(element)) { | 627 |
| 703 _wrongTypedNodes.add(node); | 628 /** |
| 629 * Reset the analysis context to have the given options applied. |
| 630 * |
| 631 * @param options the analysis options to be applied to the context |
| 632 */ |
| 633 void resetWithOptions(AnalysisOptions options) { |
| 634 analysisContext2 = |
| 635 AnalysisContextFactory.contextWithCoreAndOptions(options); |
| 636 } |
| 637 |
| 638 /** |
| 639 * Given a library and all of its parts, resolve the contents of the library a
nd the contents of |
| 640 * the parts. This assumes that the sources for the library and its parts have
already been added |
| 641 * to the content provider using the method [addNamedSource]. |
| 642 * |
| 643 * @param librarySource the source for the compilation unit that defines the l
ibrary |
| 644 * @return the element representing the resolved library |
| 645 * @throws AnalysisException if the analysis could not be performed |
| 646 */ |
| 647 LibraryElement resolve2(Source librarySource) => |
| 648 analysisContext2.computeLibraryElement(librarySource); |
| 649 |
| 650 /** |
| 651 * Return the resolved compilation unit corresponding to the given source in t
he given library. |
| 652 * |
| 653 * @param source the source of the compilation unit to be returned |
| 654 * @param library the library in which the compilation unit is to be resolved |
| 655 * @return the resolved compilation unit |
| 656 * @throws Exception if the compilation unit could not be resolved |
| 657 */ |
| 658 CompilationUnit resolveCompilationUnit( |
| 659 Source source, LibraryElement library) => |
| 660 analysisContext2.resolveCompilationUnit(source, library); |
| 661 |
| 662 CompilationUnit resolveSource(String sourceText) => |
| 663 resolveSource2("/test.dart", sourceText); |
| 664 |
| 665 CompilationUnit resolveSource2(String fileName, String sourceText) { |
| 666 Source source = addNamedSource(fileName, sourceText); |
| 667 LibraryElement library = analysisContext.computeLibraryElement(source); |
| 668 return analysisContext.resolveCompilationUnit(source, library); |
| 669 } |
| 670 |
| 671 Source resolveSources(List<String> sourceTexts) { |
| 672 for (int i = 0; i < sourceTexts.length; i++) { |
| 673 CompilationUnit unit = |
| 674 resolveSource2("/lib${i + 1}.dart", sourceTexts[i]); |
| 675 // reference the source if this is the last source |
| 676 if (i + 1 == sourceTexts.length) { |
| 677 return unit.element.source; |
| 704 } | 678 } |
| 705 } | 679 } |
| 706 return null; | 680 return null; |
| 707 } | 681 } |
| 708 | 682 |
| 709 String _getFileName(AstNode node) { | 683 void resolveWithAndWithoutExperimental( |
| 710 // TODO (jwren) there are two copies of this method, one here and one in | 684 List<String> strSources, |
| 711 // StaticTypeVerifier, they should be resolved into a single method | 685 List<ErrorCode> codesWithoutExperimental, |
| 712 if (node != null) { | 686 List<ErrorCode> codesWithExperimental) { |
| 713 AstNode root = node.root; | 687 // Setup analysis context as non-experimental |
| 714 if (root is CompilationUnit) { | 688 AnalysisOptionsImpl options = new AnalysisOptionsImpl(); |
| 715 CompilationUnit rootCU = root; | 689 // options.enableDeferredLoading = false; |
| 716 if (rootCU.element != null) { | 690 resetWithOptions(options); |
| 717 return rootCU.element.source.fullName; | 691 // Analysis and assertions |
| 718 } else { | 692 Source source = resolveSources(strSources); |
| 719 return "<unknown file- CompilationUnit.getElement() returned null>"; | 693 assertErrors(source, codesWithoutExperimental); |
| 720 } | 694 verify([source]); |
| 721 } else { | 695 // Setup analysis context as experimental |
| 722 return "<unknown file- CompilationUnit.getRoot() is not a CompilationUni
t>"; | 696 reset(); |
| 697 // Analysis and assertions |
| 698 source = resolveSources(strSources); |
| 699 assertErrors(source, codesWithExperimental); |
| 700 verify([source]); |
| 701 } |
| 702 |
| 703 void resolveWithErrors(List<String> strSources, List<ErrorCode> codes) { |
| 704 // Analysis and assertions |
| 705 Source source = resolveSources(strSources); |
| 706 assertErrors(source, codes); |
| 707 verify([source]); |
| 708 } |
| 709 |
| 710 @override |
| 711 void setUp() { |
| 712 ElementFactory.flushStaticState(); |
| 713 super.setUp(); |
| 714 reset(); |
| 715 } |
| 716 |
| 717 @override |
| 718 void tearDown() { |
| 719 analysisContext2 = null; |
| 720 super.tearDown(); |
| 721 } |
| 722 |
| 723 /** |
| 724 * Verify that all of the identifiers in the compilation units associated with |
| 725 * the given [sources] have been resolved. |
| 726 */ |
| 727 void verify(List<Source> sources) { |
| 728 ResolutionVerifier verifier = new ResolutionVerifier(); |
| 729 for (Source source in sources) { |
| 730 List<Source> libraries = analysisContext2.getLibrariesContaining(source); |
| 731 for (Source library in libraries) { |
| 732 analysisContext2 |
| 733 .resolveCompilationUnit2(source, library) |
| 734 .accept(verifier); |
| 723 } | 735 } |
| 724 } | 736 } |
| 725 return "<unknown file- ASTNode is null>"; | 737 verifier.assertResolved(); |
| 726 } | |
| 727 | |
| 728 void _printNodes(StringBuffer buffer, List<AstNode> nodes) { | |
| 729 for (AstNode identifier in nodes) { | |
| 730 buffer.write(" "); | |
| 731 buffer.write(identifier.toString()); | |
| 732 buffer.write(" ("); | |
| 733 buffer.write(_getFileName(identifier)); | |
| 734 buffer.write(" : "); | |
| 735 buffer.write(identifier.offset); | |
| 736 buffer.writeln(")"); | |
| 737 } | |
| 738 } | 738 } |
| 739 } | 739 } |
| 740 | 740 |
| 741 /** | 741 /** |
| 742 * Shared infrastructure for [StaticTypeAnalyzer2Test] and | 742 * Shared infrastructure for [StaticTypeAnalyzer2Test] and |
| 743 * [StrongModeStaticTypeAnalyzer2Test]. | 743 * [StrongModeStaticTypeAnalyzer2Test]. |
| 744 */ | 744 */ |
| 745 class StaticTypeAnalyzer2TestShared extends ResolverTestCase { | 745 class StaticTypeAnalyzer2TestShared extends ResolverTestCase { |
| 746 String testCode; | 746 String testCode; |
| 747 Source testSource; | 747 Source testSource; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 801 SimpleIdentifier identifier = findIdentifier(name); | 801 SimpleIdentifier identifier = findIdentifier(name); |
| 802 VariableDeclaration declaration = | 802 VariableDeclaration declaration = |
| 803 identifier.getAncestor((node) => node is VariableDeclaration); | 803 identifier.getAncestor((node) => node is VariableDeclaration); |
| 804 Expression initializer = declaration.initializer; | 804 Expression initializer = declaration.initializer; |
| 805 _expectType(initializer.staticType, type); | 805 _expectType(initializer.staticType, type); |
| 806 if (propagatedType != null) { | 806 if (propagatedType != null) { |
| 807 _expectType(initializer.propagatedType, propagatedType); | 807 _expectType(initializer.propagatedType, propagatedType); |
| 808 } | 808 } |
| 809 } | 809 } |
| 810 | 810 |
| 811 /** | |
| 812 * Validates that [type] matches [expected]. | |
| 813 * | |
| 814 * If [expected] is a string, validates that the type stringifies to that | |
| 815 * text. Otherwise, [expected] is used directly a [Matcher] to match the type. | |
| 816 */ | |
| 817 _expectType(DartType type, expected) { | |
| 818 if (expected is String) { | |
| 819 expect(type.toString(), expected); | |
| 820 } else { | |
| 821 expect(type, expected); | |
| 822 } | |
| 823 } | |
| 824 | |
| 825 SimpleIdentifier findIdentifier(String search) { | 811 SimpleIdentifier findIdentifier(String search) { |
| 826 SimpleIdentifier identifier = EngineTestCase.findNode( | 812 SimpleIdentifier identifier = EngineTestCase.findNode( |
| 827 testUnit, testCode, search, (node) => node is SimpleIdentifier); | 813 testUnit, testCode, search, (node) => node is SimpleIdentifier); |
| 828 return identifier; | 814 return identifier; |
| 829 } | 815 } |
| 830 | 816 |
| 831 void resolveTestUnit(String code) { | 817 void resolveTestUnit(String code) { |
| 832 testCode = code; | 818 testCode = code; |
| 833 testSource = addSource(testCode); | 819 testSource = addSource(testCode); |
| 834 LibraryElement library = resolve2(testSource); | 820 LibraryElement library = resolve2(testSource); |
| 835 assertNoErrors(testSource); | 821 assertNoErrors(testSource); |
| 836 verify([testSource]); | 822 verify([testSource]); |
| 837 testUnit = resolveCompilationUnit(testSource, library); | 823 testUnit = resolveCompilationUnit(testSource, library); |
| 838 } | 824 } |
| 825 |
| 826 /** |
| 827 * Validates that [type] matches [expected]. |
| 828 * |
| 829 * If [expected] is a string, validates that the type stringifies to that |
| 830 * text. Otherwise, [expected] is used directly a [Matcher] to match the type. |
| 831 */ |
| 832 _expectType(DartType type, expected) { |
| 833 if (expected is String) { |
| 834 expect(type.toString(), expected); |
| 835 } else { |
| 836 expect(type, expected); |
| 837 } |
| 838 } |
| 839 } | 839 } |
| OLD | NEW |