| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2014, 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 engine.incremental_resolver; |
| 6 |
| 7 import 'dart:collection'; |
| 8 |
| 9 import 'ast.dart'; |
| 10 import 'element.dart'; |
| 11 import 'error.dart'; |
| 12 import 'java_engine.dart'; |
| 13 import 'resolver.dart'; |
| 14 import 'scanner.dart'; |
| 15 import 'source.dart'; |
| 16 |
| 17 |
| 18 /** |
| 19 * Instances of the class [DeclarationMatcher] determine whether the element |
| 20 * model defined by a given AST structure matches an existing element model. |
| 21 */ |
| 22 class DeclarationMatcher extends RecursiveAstVisitor<Object> { |
| 23 /** |
| 24 * The compilation unit containing the AST nodes being visited. |
| 25 */ |
| 26 CompilationUnitElement _enclosingUnit; |
| 27 |
| 28 /** |
| 29 * The function type alias containing the AST nodes being visited, or `null` i
f we are not |
| 30 * in the scope of a function type alias. |
| 31 */ |
| 32 FunctionTypeAliasElement _enclosingAlias; |
| 33 |
| 34 /** |
| 35 * The class containing the AST nodes being visited, or `null` if we are not i
n the scope of |
| 36 * a class. |
| 37 */ |
| 38 ClassElement _enclosingClass; |
| 39 |
| 40 /** |
| 41 * The method or function containing the AST nodes being visited, or `null` if
we are not in |
| 42 * the scope of a method or function. |
| 43 */ |
| 44 ExecutableElement _enclosingExecutable; |
| 45 |
| 46 /** |
| 47 * The parameter containing the AST nodes being visited, or `null` if we are n
ot in the |
| 48 * scope of a parameter. |
| 49 */ |
| 50 ParameterElement _enclosingParameter; |
| 51 |
| 52 /** |
| 53 * A set containing all of the elements in the element model that were defined
by the old AST node |
| 54 * corresponding to the AST node being visited. |
| 55 */ |
| 56 HashSet<Element> _allElements = new HashSet<Element>(); |
| 57 |
| 58 /** |
| 59 * A set containing all of the elements in the element model that were defined
by the old AST node |
| 60 * corresponding to the AST node being visited that have not already been matc
hed to nodes in the |
| 61 * AST structure being visited. |
| 62 */ |
| 63 HashSet<Element> _unmatchedElements = new HashSet<Element>(); |
| 64 |
| 65 /** |
| 66 * Return `true` if the declarations within the given AST structure define an
element model |
| 67 * that is equivalent to the corresponding elements rooted at the given elemen
t. |
| 68 * |
| 69 * @param node the AST structure being compared to the element model |
| 70 * @param element the root of the element model being compared to the AST stru
cture |
| 71 * @return `true` if the AST structure defines the same elements as those in t
he given |
| 72 * element model |
| 73 */ |
| 74 bool matches(AstNode node, Element element) { |
| 75 _captureEnclosingElements(element); |
| 76 _gatherElements(element); |
| 77 try { |
| 78 node.accept(this); |
| 79 } on _DeclarationMismatchException catch (exception) { |
| 80 return false; |
| 81 } |
| 82 return _unmatchedElements.isEmpty; |
| 83 } |
| 84 |
| 85 void processElement(Element element) { |
| 86 if (element == null) { |
| 87 throw new _DeclarationMismatchException(); |
| 88 } |
| 89 if (!_allElements.contains(element)) { |
| 90 throw new _DeclarationMismatchException(); |
| 91 } |
| 92 _unmatchedElements.remove(element); |
| 93 } |
| 94 |
| 95 @override |
| 96 Object visitCatchClause(CatchClause node) { |
| 97 SimpleIdentifier exceptionParameter = node.exceptionParameter; |
| 98 if (exceptionParameter != null) { |
| 99 List<LocalVariableElement> localVariables = |
| 100 _enclosingExecutable.localVariables; |
| 101 LocalVariableElement exceptionElement = |
| 102 _findIdentifier(localVariables, exceptionParameter); |
| 103 processElement(exceptionElement); |
| 104 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; |
| 105 if (stackTraceParameter != null) { |
| 106 LocalVariableElement stackTraceElement = |
| 107 _findIdentifier(localVariables, stackTraceParameter); |
| 108 processElement(stackTraceElement); |
| 109 } |
| 110 } |
| 111 return super.visitCatchClause(node); |
| 112 } |
| 113 |
| 114 @override |
| 115 Object visitClassDeclaration(ClassDeclaration node) { |
| 116 ClassElement outerClass = _enclosingClass; |
| 117 try { |
| 118 SimpleIdentifier className = node.name; |
| 119 _enclosingClass = _findIdentifier(_enclosingUnit.types, className); |
| 120 processElement(_enclosingClass); |
| 121 if (!_hasConstructor(node)) { |
| 122 ConstructorElement constructor = _enclosingClass.unnamedConstructor; |
| 123 if (constructor.isSynthetic) { |
| 124 processElement(constructor); |
| 125 } |
| 126 } |
| 127 return super.visitClassDeclaration(node); |
| 128 } finally { |
| 129 _enclosingClass = outerClass; |
| 130 } |
| 131 } |
| 132 |
| 133 @override |
| 134 Object visitClassTypeAlias(ClassTypeAlias node) { |
| 135 ClassElement outerClass = _enclosingClass; |
| 136 try { |
| 137 SimpleIdentifier className = node.name; |
| 138 _enclosingClass = _findIdentifier(_enclosingUnit.types, className); |
| 139 processElement(_enclosingClass); |
| 140 return super.visitClassTypeAlias(node); |
| 141 } finally { |
| 142 _enclosingClass = outerClass; |
| 143 } |
| 144 } |
| 145 |
| 146 @override |
| 147 Object visitCompilationUnit(CompilationUnit node) { |
| 148 processElement(_enclosingUnit); |
| 149 return super.visitCompilationUnit(node); |
| 150 } |
| 151 |
| 152 @override |
| 153 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 154 ExecutableElement outerExecutable = _enclosingExecutable; |
| 155 try { |
| 156 SimpleIdentifier constructorName = node.name; |
| 157 if (constructorName == null) { |
| 158 _enclosingExecutable = _enclosingClass.unnamedConstructor; |
| 159 } else { |
| 160 _enclosingExecutable = |
| 161 _enclosingClass.getNamedConstructor(constructorName.name); |
| 162 } |
| 163 processElement(_enclosingExecutable); |
| 164 return super.visitConstructorDeclaration(node); |
| 165 } finally { |
| 166 _enclosingExecutable = outerExecutable; |
| 167 } |
| 168 } |
| 169 |
| 170 @override |
| 171 Object visitDeclaredIdentifier(DeclaredIdentifier node) { |
| 172 SimpleIdentifier variableName = node.identifier; |
| 173 LocalVariableElement element = |
| 174 _findIdentifier(_enclosingExecutable.localVariables, variableName); |
| 175 processElement(element); |
| 176 return super.visitDeclaredIdentifier(node); |
| 177 } |
| 178 |
| 179 @override |
| 180 Object visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 181 SimpleIdentifier parameterName = node.parameter.identifier; |
| 182 ParameterElement element = _getElementForParameter(node, parameterName); |
| 183 Expression defaultValue = node.defaultValue; |
| 184 if (defaultValue != null) { |
| 185 ExecutableElement outerExecutable = _enclosingExecutable; |
| 186 try { |
| 187 if (element == null) { |
| 188 // TODO(brianwilkerson) Report this internal error. |
| 189 } else { |
| 190 _enclosingExecutable = element.initializer; |
| 191 } |
| 192 defaultValue.accept(this); |
| 193 } finally { |
| 194 _enclosingExecutable = outerExecutable; |
| 195 } |
| 196 processElement(_enclosingExecutable); |
| 197 } |
| 198 ParameterElement outerParameter = _enclosingParameter; |
| 199 try { |
| 200 _enclosingParameter = element; |
| 201 processElement(_enclosingParameter); |
| 202 return super.visitDefaultFormalParameter(node); |
| 203 } finally { |
| 204 _enclosingParameter = outerParameter; |
| 205 } |
| 206 } |
| 207 |
| 208 @override |
| 209 Object visitEnumDeclaration(EnumDeclaration node) { |
| 210 ClassElement enclosingEnum = |
| 211 _findIdentifier(_enclosingUnit.enums, node.name); |
| 212 processElement(enclosingEnum); |
| 213 List<FieldElement> constants = enclosingEnum.fields; |
| 214 for (EnumConstantDeclaration constant in node.constants) { |
| 215 FieldElement constantElement = _findIdentifier(constants, constant.name); |
| 216 processElement(constantElement); |
| 217 } |
| 218 return super.visitEnumDeclaration(node); |
| 219 } |
| 220 |
| 221 @override |
| 222 Object visitExportDirective(ExportDirective node) { |
| 223 String uri = _getStringValue(node.uri); |
| 224 if (uri != null) { |
| 225 LibraryElement library = _enclosingUnit.library; |
| 226 ExportElement exportElement = _findExport( |
| 227 library.exports, |
| 228 _enclosingUnit.context.sourceFactory.resolveUri(_enclosingUnit.source,
uri)); |
| 229 processElement(exportElement); |
| 230 } |
| 231 return super.visitExportDirective(node); |
| 232 } |
| 233 |
| 234 @override |
| 235 Object visitFieldFormalParameter(FieldFormalParameter node) { |
| 236 if (node.parent is! DefaultFormalParameter) { |
| 237 SimpleIdentifier parameterName = node.identifier; |
| 238 ParameterElement element = _getElementForParameter(node, parameterName); |
| 239 ParameterElement outerParameter = _enclosingParameter; |
| 240 try { |
| 241 _enclosingParameter = element; |
| 242 processElement(_enclosingParameter); |
| 243 return super.visitFieldFormalParameter(node); |
| 244 } finally { |
| 245 _enclosingParameter = outerParameter; |
| 246 } |
| 247 } else { |
| 248 return super.visitFieldFormalParameter(node); |
| 249 } |
| 250 } |
| 251 |
| 252 @override |
| 253 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 254 ExecutableElement outerExecutable = _enclosingExecutable; |
| 255 try { |
| 256 SimpleIdentifier functionName = node.name; |
| 257 Token property = node.propertyKeyword; |
| 258 if (property == null) { |
| 259 if (_enclosingExecutable != null) { |
| 260 _enclosingExecutable = |
| 261 _findIdentifier(_enclosingExecutable.functions, functionName); |
| 262 } else { |
| 263 _enclosingExecutable = |
| 264 _findIdentifier(_enclosingUnit.functions, functionName); |
| 265 } |
| 266 } else { |
| 267 PropertyAccessorElement accessor = |
| 268 _findIdentifier(_enclosingUnit.accessors, functionName); |
| 269 if ((property as KeywordToken).keyword == Keyword.SET) { |
| 270 accessor = accessor.variable.setter; |
| 271 } |
| 272 _enclosingExecutable = accessor; |
| 273 } |
| 274 processElement(_enclosingExecutable); |
| 275 return super.visitFunctionDeclaration(node); |
| 276 } finally { |
| 277 _enclosingExecutable = outerExecutable; |
| 278 } |
| 279 } |
| 280 |
| 281 @override |
| 282 Object visitFunctionExpression(FunctionExpression node) { |
| 283 if (node.parent is! FunctionDeclaration) { |
| 284 FunctionElement element = |
| 285 _findAtOffset(_enclosingExecutable.functions, node.beginToken.offset); |
| 286 processElement(element); |
| 287 } |
| 288 ExecutableElement outerExecutable = _enclosingExecutable; |
| 289 try { |
| 290 _enclosingExecutable = node.element; |
| 291 processElement(_enclosingExecutable); |
| 292 return super.visitFunctionExpression(node); |
| 293 } finally { |
| 294 _enclosingExecutable = outerExecutable; |
| 295 } |
| 296 } |
| 297 |
| 298 @override |
| 299 Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 300 FunctionTypeAliasElement outerAlias = _enclosingAlias; |
| 301 try { |
| 302 SimpleIdentifier aliasName = node.name; |
| 303 _enclosingAlias = |
| 304 _findIdentifier(_enclosingUnit.functionTypeAliases, aliasName); |
| 305 processElement(_enclosingAlias); |
| 306 return super.visitFunctionTypeAlias(node); |
| 307 } finally { |
| 308 _enclosingAlias = outerAlias; |
| 309 } |
| 310 } |
| 311 |
| 312 @override |
| 313 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| 314 if (node.parent is! DefaultFormalParameter) { |
| 315 SimpleIdentifier parameterName = node.identifier; |
| 316 ParameterElement element = _getElementForParameter(node, parameterName); |
| 317 ParameterElement outerParameter = _enclosingParameter; |
| 318 try { |
| 319 _enclosingParameter = element; |
| 320 processElement(_enclosingParameter); |
| 321 return super.visitFunctionTypedFormalParameter(node); |
| 322 } finally { |
| 323 _enclosingParameter = outerParameter; |
| 324 } |
| 325 } else { |
| 326 return super.visitFunctionTypedFormalParameter(node); |
| 327 } |
| 328 } |
| 329 |
| 330 @override |
| 331 Object visitImportDirective(ImportDirective node) { |
| 332 String uri = _getStringValue(node.uri); |
| 333 if (uri != null) { |
| 334 LibraryElement library = _enclosingUnit.library; |
| 335 ImportElement importElement = _findImport( |
| 336 library.imports, |
| 337 _enclosingUnit.context.sourceFactory.resolveUri(_enclosingUnit.source,
uri), |
| 338 node.prefix); |
| 339 processElement(importElement); |
| 340 } |
| 341 return super.visitImportDirective(node); |
| 342 } |
| 343 |
| 344 @override |
| 345 Object visitLabeledStatement(LabeledStatement node) { |
| 346 for (Label label in node.labels) { |
| 347 SimpleIdentifier labelName = label.label; |
| 348 LabelElement element = |
| 349 _findIdentifier(_enclosingExecutable.labels, labelName); |
| 350 processElement(element); |
| 351 } |
| 352 return super.visitLabeledStatement(node); |
| 353 } |
| 354 |
| 355 @override |
| 356 Object visitMethodDeclaration(MethodDeclaration node) { |
| 357 ExecutableElement outerExecutable = _enclosingExecutable; |
| 358 try { |
| 359 Token property = node.propertyKeyword; |
| 360 SimpleIdentifier methodName = node.name; |
| 361 String nameOfMethod = methodName.name; |
| 362 if (nameOfMethod == TokenType.MINUS.lexeme && |
| 363 node.parameters.parameters.length == 0) { |
| 364 nameOfMethod = "unary-"; |
| 365 } |
| 366 if (property == null) { |
| 367 _enclosingExecutable = _findWithNameAndOffset( |
| 368 _enclosingClass.methods, |
| 369 nameOfMethod, |
| 370 methodName.offset); |
| 371 methodName.staticElement = _enclosingExecutable; |
| 372 } else { |
| 373 PropertyAccessorElement accessor = |
| 374 _findIdentifier(_enclosingClass.accessors, methodName); |
| 375 if ((property as KeywordToken).keyword == Keyword.SET) { |
| 376 accessor = accessor.variable.setter; |
| 377 methodName.staticElement = accessor; |
| 378 } |
| 379 _enclosingExecutable = accessor; |
| 380 } |
| 381 processElement(_enclosingExecutable); |
| 382 return super.visitMethodDeclaration(node); |
| 383 } finally { |
| 384 _enclosingExecutable = outerExecutable; |
| 385 } |
| 386 } |
| 387 |
| 388 @override |
| 389 Object visitPartDirective(PartDirective node) { |
| 390 String uri = _getStringValue(node.uri); |
| 391 if (uri != null) { |
| 392 Source partSource = |
| 393 _enclosingUnit.context.sourceFactory.resolveUri(_enclosingUnit.source,
uri); |
| 394 CompilationUnitElement element = |
| 395 _findPart(_enclosingUnit.library.parts, partSource); |
| 396 processElement(element); |
| 397 } |
| 398 return super.visitPartDirective(node); |
| 399 } |
| 400 |
| 401 @override |
| 402 Object visitSimpleFormalParameter(SimpleFormalParameter node) { |
| 403 if (node.parent is! DefaultFormalParameter) { |
| 404 SimpleIdentifier parameterName = node.identifier; |
| 405 ParameterElement element = _getElementForParameter(node, parameterName); |
| 406 ParameterElement outerParameter = _enclosingParameter; |
| 407 try { |
| 408 _enclosingParameter = element; |
| 409 processElement(_enclosingParameter); |
| 410 return super.visitSimpleFormalParameter(node); |
| 411 } finally { |
| 412 _enclosingParameter = outerParameter; |
| 413 } |
| 414 } else { |
| 415 } |
| 416 return super.visitSimpleFormalParameter(node); |
| 417 } |
| 418 |
| 419 @override |
| 420 Object visitSwitchCase(SwitchCase node) { |
| 421 for (Label label in node.labels) { |
| 422 SimpleIdentifier labelName = label.label; |
| 423 LabelElement element = |
| 424 _findIdentifier(_enclosingExecutable.labels, labelName); |
| 425 processElement(element); |
| 426 } |
| 427 return super.visitSwitchCase(node); |
| 428 } |
| 429 |
| 430 @override |
| 431 Object visitSwitchDefault(SwitchDefault node) { |
| 432 for (Label label in node.labels) { |
| 433 SimpleIdentifier labelName = label.label; |
| 434 LabelElement element = |
| 435 _findIdentifier(_enclosingExecutable.labels, labelName); |
| 436 processElement(element); |
| 437 } |
| 438 return super.visitSwitchDefault(node); |
| 439 } |
| 440 |
| 441 @override |
| 442 Object visitTypeParameter(TypeParameter node) { |
| 443 SimpleIdentifier parameterName = node.name; |
| 444 TypeParameterElement element = null; |
| 445 if (_enclosingClass != null) { |
| 446 element = _findIdentifier(_enclosingClass.typeParameters, parameterName); |
| 447 } else if (_enclosingAlias != null) { |
| 448 element = _findIdentifier(_enclosingAlias.typeParameters, parameterName); |
| 449 } |
| 450 processElement(element); |
| 451 return super.visitTypeParameter(node); |
| 452 } |
| 453 |
| 454 @override |
| 455 Object visitVariableDeclaration(VariableDeclaration node) { |
| 456 VariableElement element = null; |
| 457 SimpleIdentifier variableName = node.name; |
| 458 if (_enclosingExecutable != null) { |
| 459 element = |
| 460 _findIdentifier(_enclosingExecutable.localVariables, variableName); |
| 461 } |
| 462 if (element == null && _enclosingClass != null) { |
| 463 element = _findIdentifier(_enclosingClass.fields, variableName); |
| 464 } |
| 465 if (element == null && _enclosingUnit != null) { |
| 466 element = _findIdentifier(_enclosingUnit.topLevelVariables, variableName); |
| 467 } |
| 468 Expression initializer = node.initializer; |
| 469 if (initializer != null) { |
| 470 ExecutableElement outerExecutable = _enclosingExecutable; |
| 471 try { |
| 472 if (element == null) { |
| 473 // TODO(brianwilkerson) Report this internal error. |
| 474 } else { |
| 475 _enclosingExecutable = element.initializer; |
| 476 } |
| 477 processElement(element); |
| 478 processElement(_enclosingExecutable); |
| 479 return super.visitVariableDeclaration(node); |
| 480 } finally { |
| 481 _enclosingExecutable = outerExecutable; |
| 482 } |
| 483 } |
| 484 return super.visitVariableDeclaration(node); |
| 485 } |
| 486 |
| 487 /** |
| 488 * Given that the comparison is to begin with the given element, capture the e
nclosing elements |
| 489 * that might be used while performing the comparison. |
| 490 * |
| 491 * @param element the element corresponding to the AST structure to be compare
d |
| 492 */ |
| 493 void _captureEnclosingElements(Element element) { |
| 494 Element parent = |
| 495 element is CompilationUnitElement ? element : element.enclosingElement; |
| 496 while (parent != null) { |
| 497 if (parent is CompilationUnitElement) { |
| 498 _enclosingUnit = parent as CompilationUnitElement; |
| 499 } else if (parent is ClassElement) { |
| 500 if (_enclosingClass == null) { |
| 501 _enclosingClass = parent as ClassElement; |
| 502 } |
| 503 } else if (parent is FunctionTypeAliasElement) { |
| 504 if (_enclosingAlias == null) { |
| 505 _enclosingAlias = parent as FunctionTypeAliasElement; |
| 506 } |
| 507 } else if (parent is ExecutableElement) { |
| 508 if (_enclosingExecutable == null) { |
| 509 _enclosingExecutable = parent as ExecutableElement; |
| 510 } |
| 511 } else if (parent is ParameterElement) { |
| 512 if (_enclosingParameter == null) { |
| 513 _enclosingParameter = parent as ParameterElement; |
| 514 } |
| 515 } |
| 516 parent = parent.enclosingElement; |
| 517 } |
| 518 } |
| 519 |
| 520 /** |
| 521 * Return the element in the given array of elements that was created for the
declaration at the |
| 522 * given offset. This method should only be used when there is no name |
| 523 * |
| 524 * @param elements the elements of the appropriate kind that exist in the curr
ent context |
| 525 * @param offset the offset of the name of the element to be returned |
| 526 * @return the element at the given offset |
| 527 */ |
| 528 Element _findAtOffset(List<Element> elements, int offset) => |
| 529 _findWithNameAndOffset(elements, "", offset); |
| 530 |
| 531 /** |
| 532 * Return the export element from the given array whose library has the given
source, or |
| 533 * `null` if there is no such export. |
| 534 * |
| 535 * @param exports the export elements being searched |
| 536 * @param source the source of the library associated with the export element
to being searched |
| 537 * for |
| 538 * @return the export element whose library has the given source |
| 539 */ |
| 540 ExportElement _findExport(List<ExportElement> exports, Source source) { |
| 541 for (ExportElement export in exports) { |
| 542 if (export.exportedLibrary.source == source) { |
| 543 return export; |
| 544 } |
| 545 } |
| 546 return null; |
| 547 } |
| 548 |
| 549 /** |
| 550 * Return the element in the given array of elements that was created for the
declaration with the |
| 551 * given name. |
| 552 * |
| 553 * @param elements the elements of the appropriate kind that exist in the curr
ent context |
| 554 * @param identifier the name node in the declaration of the element to be ret
urned |
| 555 * @return the element created for the declaration with the given name |
| 556 */ |
| 557 Element _findIdentifier(List<Element> elements, |
| 558 SimpleIdentifier identifier) => |
| 559 _findWithNameAndOffset(elements, identifier.name, identifier.offset); |
| 560 |
| 561 /** |
| 562 * Return the import element from the given array whose library has the given
source and that has |
| 563 * the given prefix, or `null` if there is no such import. |
| 564 * |
| 565 * @param imports the import elements being searched |
| 566 * @param source the source of the library associated with the import element
to being searched |
| 567 * for |
| 568 * @param prefix the prefix with which the library was imported |
| 569 * @return the import element whose library has the given source and prefix |
| 570 */ |
| 571 ImportElement _findImport(List<ImportElement> imports, Source source, |
| 572 SimpleIdentifier prefix) { |
| 573 for (ImportElement element in imports) { |
| 574 if (element.importedLibrary.source == source) { |
| 575 PrefixElement prefixElement = element.prefix; |
| 576 if (prefix == null) { |
| 577 if (prefixElement == null) { |
| 578 return element; |
| 579 } |
| 580 } else { |
| 581 if (prefixElement != null && |
| 582 prefix.name == prefixElement.displayName) { |
| 583 return element; |
| 584 } |
| 585 } |
| 586 } |
| 587 } |
| 588 return null; |
| 589 } |
| 590 |
| 591 /** |
| 592 * Return the element for the part with the given source, or `null` if there i
s no element |
| 593 * for the given source. |
| 594 * |
| 595 * @param parts the elements for the parts |
| 596 * @param partSource the source for the part whose element is to be returned |
| 597 * @return the element for the part with the given source |
| 598 */ |
| 599 CompilationUnitElement _findPart(List<CompilationUnitElement> parts, |
| 600 Source partSource) { |
| 601 for (CompilationUnitElement part in parts) { |
| 602 if (part.source == partSource) { |
| 603 return part; |
| 604 } |
| 605 } |
| 606 return null; |
| 607 } |
| 608 |
| 609 /** |
| 610 * Return the element in the given array of elements that was created for the
declaration with the |
| 611 * given name at the given offset. |
| 612 * |
| 613 * @param elements the elements of the appropriate kind that exist in the curr
ent context |
| 614 * @param name the name of the element to be returned |
| 615 * @param offset the offset of the name of the element to be returned |
| 616 * @return the element with the given name and offset |
| 617 */ |
| 618 Element _findWithNameAndOffset(List<Element> elements, String name, |
| 619 int offset) { |
| 620 for (Element element in elements) { |
| 621 if (element.displayName == name && element.nameOffset == offset) { |
| 622 return element; |
| 623 } |
| 624 } |
| 625 return null; |
| 626 } |
| 627 |
| 628 void _gatherElements(Element element) { |
| 629 element.accept(new _ElementsGatherer(this)); |
| 630 } |
| 631 |
| 632 /** |
| 633 * Search the most closely enclosing list of parameters for a parameter with t
he given name. |
| 634 * |
| 635 * @param node the node defining the parameter with the given name |
| 636 * @param parameterName the name of the parameter being searched for |
| 637 * @return the element representing the parameter with that name |
| 638 */ |
| 639 ParameterElement _getElementForParameter(FormalParameter node, |
| 640 SimpleIdentifier parameterName) { |
| 641 List<ParameterElement> parameters = null; |
| 642 if (_enclosingParameter != null) { |
| 643 parameters = _enclosingParameter.parameters; |
| 644 } |
| 645 if (parameters == null && _enclosingExecutable != null) { |
| 646 parameters = _enclosingExecutable.parameters; |
| 647 } |
| 648 if (parameters == null && _enclosingAlias != null) { |
| 649 parameters = _enclosingAlias.parameters; |
| 650 } |
| 651 return parameters == null ? |
| 652 null : |
| 653 _findIdentifier(parameters, parameterName); |
| 654 } |
| 655 |
| 656 /** |
| 657 * Return the value of the given string literal, or `null` if the string is no
t a constant |
| 658 * string without any string interpolation. |
| 659 * |
| 660 * @param literal the string literal whose value is to be returned |
| 661 * @return the value of the given string literal |
| 662 */ |
| 663 String _getStringValue(StringLiteral literal) { |
| 664 if (literal is StringInterpolation) { |
| 665 return null; |
| 666 } |
| 667 return literal.stringValue; |
| 668 } |
| 669 |
| 670 /** |
| 671 * Return `true` if the given class defines at least one constructor. |
| 672 * |
| 673 * @param node the class being tested |
| 674 * @return `true` if the class defines at least one constructor |
| 675 */ |
| 676 bool _hasConstructor(ClassDeclaration node) { |
| 677 for (ClassMember member in node.members) { |
| 678 if (member is ConstructorDeclaration) { |
| 679 return true; |
| 680 } |
| 681 } |
| 682 return false; |
| 683 } |
| 684 } |
| 685 |
| 686 |
| 687 /** |
| 688 * Instances of the class [IncrementalResolver] resolve the smallest portion of |
| 689 * an AST structure that we currently know how to resolve. |
| 690 */ |
| 691 class IncrementalResolver { |
| 692 /** |
| 693 * The element for the library containing the compilation unit being visited. |
| 694 */ |
| 695 final LibraryElement _definingLibrary; |
| 696 |
| 697 /** |
| 698 * The source representing the compilation unit being visited. |
| 699 */ |
| 700 final Source _source; |
| 701 |
| 702 /** |
| 703 * The object used to access the types from the core library. |
| 704 */ |
| 705 final TypeProvider _typeProvider; |
| 706 |
| 707 /** |
| 708 * The error listener that will be informed of any errors that are found durin
g resolution. |
| 709 */ |
| 710 final AnalysisErrorListener _errorListener; |
| 711 |
| 712 /** |
| 713 * Initialize a newly created incremental resolver to resolve a node in the gi
ven source in the |
| 714 * given library, reporting errors to the given error listener. |
| 715 * |
| 716 * @param definingLibrary the element for the library containing the compilati
on unit being |
| 717 * visited |
| 718 * @param source the source representing the compilation unit being visited |
| 719 * @param typeProvider the object used to access the types from the core libra
ry |
| 720 * @param errorListener the error listener that will be informed of any errors
that are found |
| 721 * during resolution |
| 722 */ |
| 723 IncrementalResolver(this._definingLibrary, this._source, this._typeProvider, |
| 724 this._errorListener); |
| 725 |
| 726 /** |
| 727 * Resolve the given node, reporting any errors or warnings to the given liste
ner. |
| 728 * |
| 729 * @param node the root of the AST structure to be resolved |
| 730 * @throws AnalysisException if the node could not be resolved |
| 731 */ |
| 732 void resolve(AstNode node) { |
| 733 AstNode rootNode = _findResolutionRoot(node); |
| 734 Scope scope = ScopeBuilder.scopeFor(rootNode, _errorListener); |
| 735 if (_elementModelChanged(rootNode.parent)) { |
| 736 throw new AnalysisException("Cannot resolve node: element model changed"); |
| 737 } |
| 738 _resolveTypes(node, scope); |
| 739 _resolveVariables(node, scope); |
| 740 _resolveReferences(node, scope); |
| 741 } |
| 742 |
| 743 /** |
| 744 * Return `true` if the given node can be resolved independently of any other
nodes. |
| 745 * |
| 746 * <b>Note:</b> This method needs to be kept in sync with [ScopeBuilder.scopeF
orAstNode]. |
| 747 * |
| 748 * @param node the node being tested |
| 749 * @return `true` if the given node can be resolved independently of any other
nodes |
| 750 */ |
| 751 bool _canBeResolved(AstNode node) => |
| 752 node is ClassDeclaration || |
| 753 node is ClassTypeAlias || |
| 754 node is CompilationUnit || |
| 755 node is ConstructorDeclaration || |
| 756 node is FunctionDeclaration || |
| 757 node is FunctionTypeAlias || |
| 758 node is MethodDeclaration; |
| 759 |
| 760 /** |
| 761 * Return `true` if the portion of the element model defined by the given node
has changed. |
| 762 * |
| 763 * @param node the node defining the portion of the element model being tested |
| 764 * @return `true` if the element model defined by the given node has changed |
| 765 * @throws AnalysisException if the correctness of the element model cannot be
determined |
| 766 */ |
| 767 bool _elementModelChanged(AstNode node) { |
| 768 Element element = _getElement(node); |
| 769 if (element == null) { |
| 770 throw new AnalysisException( |
| 771 "Cannot resolve node: a ${node.runtimeType} does not define an element
"); |
| 772 } |
| 773 DeclarationMatcher matcher = new DeclarationMatcher(); |
| 774 return !matcher.matches(node, element); |
| 775 } |
| 776 |
| 777 /** |
| 778 * Starting at the given node, find the smallest AST node that can be resolved
independently of |
| 779 * any other nodes. Return the node that was found. |
| 780 * |
| 781 * @param node the node at which the search is to begin |
| 782 * @return the smallest AST node that can be resolved independently of any oth
er nodes |
| 783 * @throws AnalysisException if there is no such node |
| 784 */ |
| 785 AstNode _findResolutionRoot(AstNode node) { |
| 786 AstNode result = node; |
| 787 AstNode parent = result.parent; |
| 788 while (parent != null && !_canBeResolved(parent)) { |
| 789 result = parent; |
| 790 parent = result.parent; |
| 791 } |
| 792 if (parent == null) { |
| 793 throw new AnalysisException("Cannot resolve node: no resolvable node"); |
| 794 } |
| 795 return result; |
| 796 } |
| 797 |
| 798 /** |
| 799 * Return the element defined by the given node, or `null` if the node does no
t define an |
| 800 * element. |
| 801 * |
| 802 * @param node the node defining the element to be returned |
| 803 * @return the element defined by the given node |
| 804 */ |
| 805 Element _getElement(AstNode node) { |
| 806 if (node is Declaration) { |
| 807 return node.element; |
| 808 } else if (node is CompilationUnit) { |
| 809 return node.element; |
| 810 } |
| 811 return null; |
| 812 } |
| 813 |
| 814 void _resolveReferences(AstNode node, Scope scope) { |
| 815 ResolverVisitor visitor = new ResolverVisitor.con3( |
| 816 _definingLibrary, |
| 817 _source, |
| 818 _typeProvider, |
| 819 scope, |
| 820 _errorListener); |
| 821 node.accept(visitor); |
| 822 } |
| 823 |
| 824 void _resolveTypes(AstNode node, Scope scope) { |
| 825 TypeResolverVisitor visitor = new TypeResolverVisitor.con3( |
| 826 _definingLibrary, |
| 827 _source, |
| 828 _typeProvider, |
| 829 scope, |
| 830 _errorListener); |
| 831 node.accept(visitor); |
| 832 } |
| 833 |
| 834 void _resolveVariables(AstNode node, Scope scope) { |
| 835 VariableResolverVisitor visitor = new VariableResolverVisitor.con2( |
| 836 _definingLibrary, |
| 837 _source, |
| 838 _typeProvider, |
| 839 scope, |
| 840 _errorListener); |
| 841 node.accept(visitor); |
| 842 } |
| 843 } |
| 844 |
| 845 |
| 846 /** |
| 847 * Instances of the class [ScopeBuilder] build the scope for a given node in an |
| 848 * AST structure. At the moment, this class only handles top-level and |
| 849 * class-level declarations. |
| 850 */ |
| 851 class ScopeBuilder { |
| 852 /** |
| 853 * The listener to which analysis errors will be reported. |
| 854 */ |
| 855 final AnalysisErrorListener _errorListener; |
| 856 |
| 857 /** |
| 858 * Initialize a newly created scope builder to generate a scope that will repo
rt errors to the |
| 859 * given listener. |
| 860 * |
| 861 * @param errorListener the listener to which analysis errors will be reported |
| 862 */ |
| 863 ScopeBuilder(this._errorListener); |
| 864 |
| 865 /** |
| 866 * Return the scope in which the given AST structure should be resolved. |
| 867 * |
| 868 * <b>Note:</b> This method needs to be kept in sync with |
| 869 * [IncrementalResolver.canBeResolved]. |
| 870 * |
| 871 * @param node the root of the AST structure to be resolved |
| 872 * @return the scope in which the given AST structure should be resolved |
| 873 * @throws AnalysisException if the AST structure has not been resolved or is
not part of a |
| 874 * [CompilationUnit] |
| 875 */ |
| 876 Scope _scopeForAstNode(AstNode node) { |
| 877 if (node is CompilationUnit) { |
| 878 return _scopeForCompilationUnit(node); |
| 879 } |
| 880 AstNode parent = node.parent; |
| 881 if (parent == null) { |
| 882 throw new AnalysisException( |
| 883 "Cannot create scope: node is not part of a CompilationUnit"); |
| 884 } |
| 885 Scope scope = _scopeForAstNode(parent); |
| 886 if (node is ClassDeclaration) { |
| 887 ClassElement element = node.element; |
| 888 if (element == null) { |
| 889 throw new AnalysisException( |
| 890 "Cannot build a scope for an unresolved class"); |
| 891 } |
| 892 scope = new ClassScope(new TypeParameterScope(scope, element), element); |
| 893 } else if (node is ClassTypeAlias) { |
| 894 ClassElement element = node.element; |
| 895 if (element == null) { |
| 896 throw new AnalysisException( |
| 897 "Cannot build a scope for an unresolved class type alias"); |
| 898 } |
| 899 scope = new ClassScope(new TypeParameterScope(scope, element), element); |
| 900 } else if (node is ConstructorDeclaration) { |
| 901 ConstructorElement element = node.element; |
| 902 if (element == null) { |
| 903 throw new AnalysisException( |
| 904 "Cannot build a scope for an unresolved constructor"); |
| 905 } |
| 906 FunctionScope functionScope = new FunctionScope(scope, element); |
| 907 functionScope.defineParameters(); |
| 908 scope = functionScope; |
| 909 } else if (node is FunctionDeclaration) { |
| 910 ExecutableElement element = node.element; |
| 911 if (element == null) { |
| 912 throw new AnalysisException( |
| 913 "Cannot build a scope for an unresolved function"); |
| 914 } |
| 915 FunctionScope functionScope = new FunctionScope(scope, element); |
| 916 functionScope.defineParameters(); |
| 917 scope = functionScope; |
| 918 } else if (node is FunctionTypeAlias) { |
| 919 scope = new FunctionTypeScope(scope, node.element); |
| 920 } else if (node is MethodDeclaration) { |
| 921 ExecutableElement element = node.element; |
| 922 if (element == null) { |
| 923 throw new AnalysisException( |
| 924 "Cannot build a scope for an unresolved method"); |
| 925 } |
| 926 FunctionScope functionScope = new FunctionScope(scope, element); |
| 927 functionScope.defineParameters(); |
| 928 scope = functionScope; |
| 929 } |
| 930 return scope; |
| 931 } |
| 932 |
| 933 Scope _scopeForCompilationUnit(CompilationUnit node) { |
| 934 CompilationUnitElement unitElement = node.element; |
| 935 if (unitElement == null) { |
| 936 throw new AnalysisException( |
| 937 "Cannot create scope: compilation unit is not resolved"); |
| 938 } |
| 939 LibraryElement libraryElement = unitElement.library; |
| 940 if (libraryElement == null) { |
| 941 throw new AnalysisException( |
| 942 "Cannot create scope: compilation unit is not part of a library"); |
| 943 } |
| 944 return new LibraryScope(libraryElement, _errorListener); |
| 945 } |
| 946 |
| 947 /** |
| 948 * Return the scope in which the given AST structure should be resolved. |
| 949 * |
| 950 * @param node the root of the AST structure to be resolved |
| 951 * @param errorListener the listener to which analysis errors will be reported |
| 952 * @return the scope in which the given AST structure should be resolved |
| 953 * @throws AnalysisException if the AST structure has not been resolved or is
not part of a |
| 954 * [CompilationUnit] |
| 955 */ |
| 956 static Scope scopeFor(AstNode node, AnalysisErrorListener errorListener) { |
| 957 if (node == null) { |
| 958 throw new AnalysisException("Cannot create scope: node is null"); |
| 959 } else if (node is CompilationUnit) { |
| 960 ScopeBuilder builder = new ScopeBuilder(errorListener); |
| 961 return builder._scopeForAstNode(node); |
| 962 } |
| 963 AstNode parent = node.parent; |
| 964 if (parent == null) { |
| 965 throw new AnalysisException( |
| 966 "Cannot create scope: node is not part of a CompilationUnit"); |
| 967 } |
| 968 ScopeBuilder builder = new ScopeBuilder(errorListener); |
| 969 return builder._scopeForAstNode(parent); |
| 970 } |
| 971 } |
| 972 |
| 973 |
| 974 /** |
| 975 * Instances of the class [_DeclarationMismatchException] represent an exception |
| 976 * that is thrown when the element model defined by a given AST structure does |
| 977 * not match an existing element model. |
| 978 */ |
| 979 class _DeclarationMismatchException { |
| 980 } |
| 981 |
| 982 |
| 983 class _ElementsGatherer extends GeneralizingElementVisitor { |
| 984 final DeclarationMatcher matcher; |
| 985 |
| 986 _ElementsGatherer(this.matcher); |
| 987 |
| 988 @override |
| 989 visitElement(Element element) { |
| 990 matcher._allElements.add(element); |
| 991 matcher._unmatchedElements.add(element); |
| 992 super.visitElement(element); |
| 993 } |
| 994 } |
| OLD | NEW |