| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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.src.generated.incremental_resolver; | 5 library analyzer.src.generated.incremental_resolver; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'dart:math' as math; | 8 import 'dart:math' as math; |
| 9 | 9 |
| 10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
| 11 import 'package:analyzer/dart/ast/token.dart'; | 11 import 'package:analyzer/dart/ast/token.dart'; |
| 12 import 'package:analyzer/dart/ast/visitor.dart'; | |
| 13 import 'package:analyzer/dart/element/element.dart'; | 12 import 'package:analyzer/dart/element/element.dart'; |
| 14 import 'package:analyzer/dart/element/type.dart'; | |
| 15 import 'package:analyzer/dart/element/visitor.dart'; | 13 import 'package:analyzer/dart/element/visitor.dart'; |
| 16 import 'package:analyzer/src/context/cache.dart'; | 14 import 'package:analyzer/src/context/cache.dart'; |
| 17 import 'package:analyzer/src/dart/ast/token.dart'; | 15 import 'package:analyzer/src/dart/ast/token.dart'; |
| 18 import 'package:analyzer/src/dart/ast/utilities.dart'; | 16 import 'package:analyzer/src/dart/ast/utilities.dart'; |
| 19 import 'package:analyzer/src/dart/element/builder.dart'; | 17 import 'package:analyzer/src/dart/element/builder.dart'; |
| 20 import 'package:analyzer/src/dart/element/element.dart'; | 18 import 'package:analyzer/src/dart/element/element.dart'; |
| 21 import 'package:analyzer/src/dart/resolver/inheritance_manager.dart'; | 19 import 'package:analyzer/src/dart/resolver/inheritance_manager.dart'; |
| 22 import 'package:analyzer/src/dart/scanner/reader.dart'; | 20 import 'package:analyzer/src/dart/scanner/reader.dart'; |
| 23 import 'package:analyzer/src/dart/scanner/scanner.dart'; | 21 import 'package:analyzer/src/dart/scanner/scanner.dart'; |
| 24 import 'package:analyzer/src/generated/constant.dart'; | 22 import 'package:analyzer/src/generated/constant.dart'; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 43 bool _resolveApiChanges = false; | 41 bool _resolveApiChanges = false; |
| 44 | 42 |
| 45 /** | 43 /** |
| 46 * This method is used to enable/disable API-changing modifications resolution. | 44 * This method is used to enable/disable API-changing modifications resolution. |
| 47 */ | 45 */ |
| 48 void set test_resolveApiChanges(bool value) { | 46 void set test_resolveApiChanges(bool value) { |
| 49 _resolveApiChanges = value; | 47 _resolveApiChanges = value; |
| 50 } | 48 } |
| 51 | 49 |
| 52 /** | 50 /** |
| 53 * Instances of the class [DeclarationMatcher] determine whether the element | |
| 54 * model defined by a given AST structure matches an existing element model. | |
| 55 */ | |
| 56 class DeclarationMatcher extends RecursiveAstVisitor { | |
| 57 /** | |
| 58 * The library containing the AST nodes being visited. | |
| 59 */ | |
| 60 LibraryElement _enclosingLibrary; | |
| 61 | |
| 62 /** | |
| 63 * The compilation unit containing the AST nodes being visited. | |
| 64 */ | |
| 65 CompilationUnitElement _enclosingUnit; | |
| 66 | |
| 67 /** | |
| 68 * The function type alias containing the AST nodes being visited, or `null` i
f we are not | |
| 69 * in the scope of a function type alias. | |
| 70 */ | |
| 71 FunctionTypeAliasElement _enclosingAlias; | |
| 72 | |
| 73 /** | |
| 74 * The class containing the AST nodes being visited, or `null` if we are not | |
| 75 * in the scope of a class. | |
| 76 */ | |
| 77 ClassElementImpl _enclosingClass; | |
| 78 | |
| 79 /** | |
| 80 * The enum containing the AST nodes being visited, or `null` if we are not | |
| 81 * in the scope of an enum. | |
| 82 */ | |
| 83 EnumElementImpl _enclosingEnum; | |
| 84 | |
| 85 /** | |
| 86 * The parameter containing the AST nodes being visited, or `null` if we are n
ot in the | |
| 87 * scope of a parameter. | |
| 88 */ | |
| 89 ParameterElement _enclosingParameter; | |
| 90 | |
| 91 FieldDeclaration _enclosingFieldNode = null; | |
| 92 bool _inTopLevelVariableDeclaration = false; | |
| 93 | |
| 94 /** | |
| 95 * Is `true` if the current class declaration has a constructor. | |
| 96 */ | |
| 97 bool _hasConstructor = false; | |
| 98 | |
| 99 /** | |
| 100 * A set containing all of the elements in the element model that were defined
by the old AST node | |
| 101 * corresponding to the AST node being visited. | |
| 102 */ | |
| 103 HashSet<Element> _allElements = new HashSet<Element>(); | |
| 104 | |
| 105 /** | |
| 106 * A set containing all of the elements were defined in the old element model, | |
| 107 * but are not defined in the new element model. | |
| 108 */ | |
| 109 HashSet<Element> _removedElements = new HashSet<Element>(); | |
| 110 | |
| 111 /** | |
| 112 * A set containing all of the elements are defined in the new element model, | |
| 113 * but were not defined in the old element model. | |
| 114 */ | |
| 115 HashSet<Element> _addedElements = new HashSet<Element>(); | |
| 116 | |
| 117 /** | |
| 118 * Determines how elements model corresponding to the given [node] differs | |
| 119 * from the [element]. | |
| 120 */ | |
| 121 DeclarationMatchKind matches(AstNode node, Element element) { | |
| 122 logger.enter('match $element @ ${element.nameOffset}'); | |
| 123 try { | |
| 124 _captureEnclosingElements(element); | |
| 125 _gatherElements(element); | |
| 126 node.accept(this); | |
| 127 } on _DeclarationMismatchException { | |
| 128 logger.log("mismatched"); | |
| 129 return DeclarationMatchKind.MISMATCH; | |
| 130 } finally { | |
| 131 logger.exit(); | |
| 132 } | |
| 133 // no API changes | |
| 134 if (_removedElements.isEmpty && _addedElements.isEmpty) { | |
| 135 logger.log("no API changes"); | |
| 136 return DeclarationMatchKind.MATCH; | |
| 137 } | |
| 138 // simple API change | |
| 139 logger.log('_removedElements: $_removedElements'); | |
| 140 logger.log('_addedElements: $_addedElements'); | |
| 141 _removedElements.forEach(_removeElement); | |
| 142 if (_removedElements.length <= 1 && _addedElements.length == 1) { | |
| 143 return DeclarationMatchKind.MISMATCH_OK; | |
| 144 } | |
| 145 // something more complex | |
| 146 return DeclarationMatchKind.MISMATCH; | |
| 147 } | |
| 148 | |
| 149 @override | |
| 150 visitBlockFunctionBody(BlockFunctionBody node) { | |
| 151 // ignore bodies | |
| 152 } | |
| 153 | |
| 154 @override | |
| 155 visitClassDeclaration(ClassDeclaration node) { | |
| 156 String name = node.name.name; | |
| 157 ClassElement element = _findElement(_enclosingUnit.types, name); | |
| 158 _enclosingClass = element; | |
| 159 _processElement(element); | |
| 160 _assertSameAnnotations(node, element); | |
| 161 _assertSameTypeParameters(node.typeParameters, element.typeParameters); | |
| 162 // check for missing clauses | |
| 163 if (node.extendsClause == null) { | |
| 164 _assertTrue(element.supertype.name == 'Object'); | |
| 165 } | |
| 166 if (node.implementsClause == null) { | |
| 167 _assertTrue(element.interfaces.isEmpty); | |
| 168 } | |
| 169 if (node.withClause == null) { | |
| 170 _assertTrue(element.mixins.isEmpty); | |
| 171 } | |
| 172 // process clauses and members | |
| 173 _hasConstructor = false; | |
| 174 super.visitClassDeclaration(node); | |
| 175 // process default constructor | |
| 176 if (!_hasConstructor) { | |
| 177 ConstructorElement constructor = element.unnamedConstructor; | |
| 178 _processElement(constructor); | |
| 179 if (!constructor.isSynthetic) { | |
| 180 _assertEquals(constructor.parameters.length, 0); | |
| 181 } | |
| 182 } | |
| 183 // matches, set the element | |
| 184 node.name.staticElement = element; | |
| 185 } | |
| 186 | |
| 187 @override | |
| 188 visitClassTypeAlias(ClassTypeAlias node) { | |
| 189 String name = node.name.name; | |
| 190 ClassElement element = _findElement(_enclosingUnit.types, name); | |
| 191 _enclosingClass = element; | |
| 192 _processElement(element); | |
| 193 _assertSameTypeParameters(node.typeParameters, element.typeParameters); | |
| 194 super.visitClassTypeAlias(node); | |
| 195 } | |
| 196 | |
| 197 @override | |
| 198 visitCompilationUnit(CompilationUnit node) { | |
| 199 _processElement(_enclosingUnit); | |
| 200 super.visitCompilationUnit(node); | |
| 201 } | |
| 202 | |
| 203 @override | |
| 204 visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 205 _hasConstructor = true; | |
| 206 SimpleIdentifier constructorName = node.name; | |
| 207 ConstructorElementImpl element = constructorName == null | |
| 208 ? _enclosingClass.unnamedConstructor | |
| 209 : _enclosingClass.getNamedConstructor(constructorName.name); | |
| 210 _processElement(element); | |
| 211 _assertEquals(node.constKeyword != null, element.isConst); | |
| 212 _assertEquals(node.factoryKeyword != null, element.isFactory); | |
| 213 _assertCompatibleParameters(node.parameters, element.parameters); | |
| 214 // matches, update the existing element | |
| 215 ExecutableElement newElement = node.element; | |
| 216 node.element = element; | |
| 217 _setLocalElements(element, newElement); | |
| 218 } | |
| 219 | |
| 220 @override | |
| 221 visitEnumConstantDeclaration(EnumConstantDeclaration node) { | |
| 222 String name = node.name.name; | |
| 223 FieldElement element = _findElement(_enclosingEnum.fields, name); | |
| 224 _processElement(element); | |
| 225 } | |
| 226 | |
| 227 @override | |
| 228 visitEnumDeclaration(EnumDeclaration node) { | |
| 229 String name = node.name.name; | |
| 230 ClassElement element = _findElement(_enclosingUnit.enums, name); | |
| 231 _enclosingEnum = element; | |
| 232 _processElement(element); | |
| 233 _assertTrue(element.isEnum); | |
| 234 super.visitEnumDeclaration(node); | |
| 235 } | |
| 236 | |
| 237 @override | |
| 238 visitExportDirective(ExportDirective node) { | |
| 239 String uri = _getStringValue(node.uri); | |
| 240 if (uri != null) { | |
| 241 ExportElement element = | |
| 242 _findUriReferencedElement(_enclosingLibrary.exports, uri); | |
| 243 _processElement(element); | |
| 244 _assertCombinators(node.combinators, element.combinators); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 @override | |
| 249 visitExpressionFunctionBody(ExpressionFunctionBody node) { | |
| 250 // ignore bodies | |
| 251 } | |
| 252 | |
| 253 @override | |
| 254 visitExtendsClause(ExtendsClause node) { | |
| 255 _assertSameType(node.superclass, _enclosingClass.supertype); | |
| 256 } | |
| 257 | |
| 258 @override | |
| 259 visitFieldDeclaration(FieldDeclaration node) { | |
| 260 _enclosingFieldNode = node; | |
| 261 try { | |
| 262 super.visitFieldDeclaration(node); | |
| 263 } finally { | |
| 264 _enclosingFieldNode = null; | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 @override | |
| 269 visitFunctionDeclaration(FunctionDeclaration node) { | |
| 270 // prepare element name | |
| 271 String name = node.name.name; | |
| 272 if (node.isSetter) { | |
| 273 name += '='; | |
| 274 } | |
| 275 // prepare element | |
| 276 Token property = node.propertyKeyword; | |
| 277 ExecutableElementImpl element; | |
| 278 if (property == null) { | |
| 279 element = _findElement(_enclosingUnit.functions, name); | |
| 280 } else { | |
| 281 element = _findElement(_enclosingUnit.accessors, name); | |
| 282 } | |
| 283 // process element | |
| 284 _processElement(element); | |
| 285 _assertSameAnnotations(node, element); | |
| 286 _assertFalse(element.isSynthetic); | |
| 287 _assertSameType(node.returnType, element.returnType); | |
| 288 _assertCompatibleParameters( | |
| 289 node.functionExpression.parameters, element.parameters); | |
| 290 _assertBody(node.functionExpression.body, element); | |
| 291 // matches, update the existing element | |
| 292 ExecutableElement newElement = node.element; | |
| 293 node.name.staticElement = element; | |
| 294 node.functionExpression.element = element; | |
| 295 _setLocalElements(element, newElement); | |
| 296 } | |
| 297 | |
| 298 @override | |
| 299 visitFunctionTypeAlias(FunctionTypeAlias node) { | |
| 300 String name = node.name.name; | |
| 301 FunctionTypeAliasElement element = | |
| 302 _findElement(_enclosingUnit.functionTypeAliases, name); | |
| 303 _processElement(element); | |
| 304 _assertSameTypeParameters(node.typeParameters, element.typeParameters); | |
| 305 _assertSameType(node.returnType, element.returnType); | |
| 306 _assertCompatibleParameters(node.parameters, element.parameters); | |
| 307 } | |
| 308 | |
| 309 @override | |
| 310 visitImplementsClause(ImplementsClause node) { | |
| 311 List<TypeName> nodes = node.interfaces; | |
| 312 List<InterfaceType> types = _enclosingClass.interfaces; | |
| 313 _assertSameTypes(nodes, types); | |
| 314 } | |
| 315 | |
| 316 @override | |
| 317 visitImportDirective(ImportDirective node) { | |
| 318 String uri = _getStringValue(node.uri); | |
| 319 if (uri != null) { | |
| 320 ImportElement element = | |
| 321 _findUriReferencedElement(_enclosingLibrary.imports, uri); | |
| 322 _processElement(element); | |
| 323 // match the prefix | |
| 324 SimpleIdentifier prefixNode = node.prefix; | |
| 325 PrefixElement prefixElement = element.prefix; | |
| 326 if (prefixNode == null) { | |
| 327 _assertNull(prefixElement); | |
| 328 } else { | |
| 329 _assertNotNull(prefixElement); | |
| 330 _assertEquals(prefixNode.name, prefixElement.name); | |
| 331 } | |
| 332 // match combinators | |
| 333 _assertCombinators(node.combinators, element.combinators); | |
| 334 } | |
| 335 } | |
| 336 | |
| 337 @override | |
| 338 visitMethodDeclaration(MethodDeclaration node) { | |
| 339 // prepare element name | |
| 340 String name = node.name.name; | |
| 341 if (name == TokenType.MINUS.lexeme && | |
| 342 node.parameters.parameters.length == 0) { | |
| 343 name = "unary-"; | |
| 344 } | |
| 345 if (node.isSetter) { | |
| 346 name += '='; | |
| 347 } | |
| 348 // prepare element | |
| 349 Token property = node.propertyKeyword; | |
| 350 ExecutableElementImpl element; | |
| 351 if (property == null) { | |
| 352 element = _findElement(_enclosingClass.methods, name); | |
| 353 } else { | |
| 354 element = _findElement(_enclosingClass.accessors, name); | |
| 355 } | |
| 356 // process element | |
| 357 ExecutableElement newElement = node.element; | |
| 358 try { | |
| 359 _assertNotNull(element); | |
| 360 _assertSameAnnotations(node, element); | |
| 361 _assertEquals(node.isStatic, element.isStatic); | |
| 362 _assertSameType(node.returnType, element.returnType); | |
| 363 _assertCompatibleParameters(node.parameters, element.parameters); | |
| 364 _assertBody(node.body, element); | |
| 365 _removedElements.remove(element); | |
| 366 // matches, update the existing element | |
| 367 node.name.staticElement = element; | |
| 368 _setLocalElements(element, newElement); | |
| 369 } on _DeclarationMismatchException { | |
| 370 _removeElement(element); | |
| 371 // add new element | |
| 372 if (newElement != null) { | |
| 373 _addedElements.add(newElement); | |
| 374 if (newElement is MethodElement) { | |
| 375 List<MethodElement> methods = _enclosingClass.methods.toList(); | |
| 376 methods.add(newElement); | |
| 377 _enclosingClass.methods = methods; | |
| 378 } else { | |
| 379 List<PropertyAccessorElement> accessors = | |
| 380 _enclosingClass.accessors.toList(); | |
| 381 accessors.add(newElement); | |
| 382 _enclosingClass.accessors = accessors; | |
| 383 } | |
| 384 } | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 @override | |
| 389 visitPartDirective(PartDirective node) { | |
| 390 String uri = _getStringValue(node.uri); | |
| 391 if (uri != null) { | |
| 392 CompilationUnitElement element = | |
| 393 _findUriReferencedElement(_enclosingLibrary.parts, uri); | |
| 394 _processElement(element); | |
| 395 } | |
| 396 super.visitPartDirective(node); | |
| 397 } | |
| 398 | |
| 399 @override | |
| 400 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
| 401 _inTopLevelVariableDeclaration = true; | |
| 402 try { | |
| 403 super.visitTopLevelVariableDeclaration(node); | |
| 404 } finally { | |
| 405 _inTopLevelVariableDeclaration = false; | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 @override | |
| 410 visitVariableDeclaration(VariableDeclaration node) { | |
| 411 // prepare variable | |
| 412 String name = node.name.name; | |
| 413 PropertyInducingElement element; | |
| 414 if (_inTopLevelVariableDeclaration) { | |
| 415 element = _findElement(_enclosingUnit.topLevelVariables, name); | |
| 416 } else { | |
| 417 element = _findElement(_enclosingClass.fields, name); | |
| 418 } | |
| 419 // verify | |
| 420 PropertyInducingElement newElement = node.name.staticElement; | |
| 421 _processElement(element); | |
| 422 _assertSameAnnotations(node, element); | |
| 423 _assertEquals(node.isConst, element.isConst); | |
| 424 _assertEquals(node.isFinal, element.isFinal); | |
| 425 if (_enclosingFieldNode != null) { | |
| 426 _assertEquals(_enclosingFieldNode.isStatic, element.isStatic); | |
| 427 } | |
| 428 _assertSameType( | |
| 429 (node.parent as VariableDeclarationList).type, element.type); | |
| 430 // matches, restore the existing element | |
| 431 node.name.staticElement = element; | |
| 432 Element variable = element; | |
| 433 if (variable is VariableElementImpl) { | |
| 434 variable.initializer = newElement.initializer; | |
| 435 } | |
| 436 } | |
| 437 | |
| 438 @override | |
| 439 visitWithClause(WithClause node) { | |
| 440 List<TypeName> nodes = node.mixinTypes; | |
| 441 List<InterfaceType> types = _enclosingClass.mixins; | |
| 442 _assertSameTypes(nodes, types); | |
| 443 } | |
| 444 | |
| 445 /** | |
| 446 * Assert that the given [body] is compatible with the given [element]. | |
| 447 * It should not be empty if the [element] is not an abstract class member. | |
| 448 * If it is present, it should have the same async / generator modifiers. | |
| 449 */ | |
| 450 void _assertBody(FunctionBody body, ExecutableElementImpl element) { | |
| 451 if (body is EmptyFunctionBody) { | |
| 452 _assertTrue(element.isAbstract); | |
| 453 } else { | |
| 454 _assertFalse(element.isAbstract); | |
| 455 _assertEquals(body.isSynchronous, element.isSynchronous); | |
| 456 _assertEquals(body.isGenerator, element.isGenerator); | |
| 457 } | |
| 458 } | |
| 459 | |
| 460 void _assertCombinators(List<Combinator> nodeCombinators, | |
| 461 List<NamespaceCombinator> elementCombinators) { | |
| 462 // prepare shown/hidden names in the element | |
| 463 Set<String> showNames = new Set<String>(); | |
| 464 Set<String> hideNames = new Set<String>(); | |
| 465 for (NamespaceCombinator combinator in elementCombinators) { | |
| 466 if (combinator is ShowElementCombinator) { | |
| 467 showNames.addAll(combinator.shownNames); | |
| 468 } else if (combinator is HideElementCombinator) { | |
| 469 hideNames.addAll(combinator.hiddenNames); | |
| 470 } | |
| 471 } | |
| 472 // match combinators with the node | |
| 473 for (Combinator combinator in nodeCombinators) { | |
| 474 if (combinator is ShowCombinator) { | |
| 475 for (SimpleIdentifier nameNode in combinator.shownNames) { | |
| 476 String name = nameNode.name; | |
| 477 _assertTrue(showNames.remove(name)); | |
| 478 } | |
| 479 } else if (combinator is HideCombinator) { | |
| 480 for (SimpleIdentifier nameNode in combinator.hiddenNames) { | |
| 481 String name = nameNode.name; | |
| 482 _assertTrue(hideNames.remove(name)); | |
| 483 } | |
| 484 } | |
| 485 } | |
| 486 _assertTrue(showNames.isEmpty); | |
| 487 _assertTrue(hideNames.isEmpty); | |
| 488 } | |
| 489 | |
| 490 void _assertCompatibleParameter( | |
| 491 FormalParameter node, ParameterElement element) { | |
| 492 _assertEquals(node.kind, element.parameterKind); | |
| 493 if (node.kind == ParameterKind.NAMED || | |
| 494 element.enclosingElement is ConstructorElement) { | |
| 495 _assertEquals(node.identifier.name, element.name); | |
| 496 } | |
| 497 // check parameter type specific properties | |
| 498 if (node is DefaultFormalParameter) { | |
| 499 Expression nodeDefault = node.defaultValue; | |
| 500 if (nodeDefault == null) { | |
| 501 _assertNull(element.defaultValueCode); | |
| 502 } else { | |
| 503 _assertEquals(nodeDefault.toSource(), element.defaultValueCode); | |
| 504 } | |
| 505 _assertCompatibleParameter(node.parameter, element); | |
| 506 } else if (node is FieldFormalParameter) { | |
| 507 _assertTrue(element.isInitializingFormal); | |
| 508 DartType parameterType = element.type; | |
| 509 if (node.type == null && node.parameters == null) { | |
| 510 FieldFormalParameterElement parameterElement = element; | |
| 511 if (!parameterElement.hasImplicitType) { | |
| 512 _assertTrue(parameterType == null || parameterType.isDynamic); | |
| 513 } | |
| 514 if (parameterElement.field != null) { | |
| 515 _assertEquals(node.identifier.name, element.name); | |
| 516 } | |
| 517 } else { | |
| 518 if (node.parameters != null) { | |
| 519 _assertTrue(parameterType is FunctionType); | |
| 520 FunctionType parameterFunctionType = parameterType; | |
| 521 _assertSameType(node.type, parameterFunctionType.returnType); | |
| 522 } else { | |
| 523 _assertSameType(node.type, parameterType); | |
| 524 } | |
| 525 } | |
| 526 _assertCompatibleParameters(node.parameters, element.parameters); | |
| 527 } else if (node is FunctionTypedFormalParameter) { | |
| 528 _assertFalse(element.isInitializingFormal); | |
| 529 _assertTrue(element.type is FunctionType); | |
| 530 FunctionType elementType = element.type; | |
| 531 _assertCompatibleParameters(node.parameters, element.parameters); | |
| 532 _assertSameType(node.returnType, elementType.returnType); | |
| 533 } else if (node is SimpleFormalParameter) { | |
| 534 _assertFalse(element.isInitializingFormal); | |
| 535 _assertSameType(node.type, element.type); | |
| 536 } | |
| 537 } | |
| 538 | |
| 539 void _assertCompatibleParameters( | |
| 540 FormalParameterList nodes, List<ParameterElement> elements) { | |
| 541 if (nodes == null) { | |
| 542 return _assertEquals(elements.length, 0); | |
| 543 } | |
| 544 List<FormalParameter> parameters = nodes.parameters; | |
| 545 int length = parameters.length; | |
| 546 _assertEquals(length, elements.length); | |
| 547 for (int i = 0; i < length; i++) { | |
| 548 _assertCompatibleParameter(parameters[i], elements[i]); | |
| 549 } | |
| 550 } | |
| 551 | |
| 552 /** | |
| 553 * Asserts that there is an import with the same prefix as the given | |
| 554 * [prefixNode], which exposes the given [element]. | |
| 555 */ | |
| 556 void _assertElementVisibleWithPrefix( | |
| 557 SimpleIdentifier prefixNode, Element element) { | |
| 558 if (prefixNode == null) { | |
| 559 return; | |
| 560 } | |
| 561 String prefixName = prefixNode.name; | |
| 562 for (ImportElement import in _enclosingLibrary.imports) { | |
| 563 if (import.prefix != null && import.prefix.name == prefixName) { | |
| 564 Namespace namespace = | |
| 565 new NamespaceBuilder().createImportNamespaceForDirective(import); | |
| 566 Iterable<Element> visibleElements = namespace.definedNames.values; | |
| 567 if (visibleElements.contains(element)) { | |
| 568 return; | |
| 569 } | |
| 570 } | |
| 571 } | |
| 572 _assertTrue(false); | |
| 573 } | |
| 574 | |
| 575 void _assertEquals(Object a, Object b) { | |
| 576 if (a != b) { | |
| 577 throw new _DeclarationMismatchException(); | |
| 578 } | |
| 579 } | |
| 580 | |
| 581 void _assertFalse(bool condition) { | |
| 582 if (condition) { | |
| 583 throw new _DeclarationMismatchException(); | |
| 584 } | |
| 585 } | |
| 586 | |
| 587 void _assertNotNull(Object object) { | |
| 588 if (object == null) { | |
| 589 throw new _DeclarationMismatchException(); | |
| 590 } | |
| 591 } | |
| 592 | |
| 593 void _assertNull(Object object) { | |
| 594 if (object != null) { | |
| 595 throw new _DeclarationMismatchException(); | |
| 596 } | |
| 597 } | |
| 598 | |
| 599 void _assertSameAnnotation(Annotation node, ElementAnnotation annotation) { | |
| 600 Element element = annotation.element; | |
| 601 if (element is ConstructorElement) { | |
| 602 _assertTrue(node.name is SimpleIdentifier); | |
| 603 _assertNull(node.constructorName); | |
| 604 TypeName nodeType = new TypeName(node.name, null); | |
| 605 _assertSameType(nodeType, element.returnType); | |
| 606 // TODO(scheglov) validate arguments | |
| 607 } | |
| 608 if (element is PropertyAccessorElement) { | |
| 609 _assertTrue(node.name is SimpleIdentifier); | |
| 610 String nodeName = node.name.name; | |
| 611 String elementName = element.displayName; | |
| 612 _assertEquals(nodeName, elementName); | |
| 613 } | |
| 614 } | |
| 615 | |
| 616 void _assertSameAnnotations(AnnotatedNode node, Element element) { | |
| 617 List<Annotation> nodeAnnotations = node.metadata; | |
| 618 List<ElementAnnotation> elementAnnotations = element.metadata; | |
| 619 int length = nodeAnnotations.length; | |
| 620 _assertEquals(elementAnnotations.length, length); | |
| 621 for (int i = 0; i < length; i++) { | |
| 622 _assertSameAnnotation(nodeAnnotations[i], elementAnnotations[i]); | |
| 623 } | |
| 624 } | |
| 625 | |
| 626 void _assertSameType(TypeName node, DartType type) { | |
| 627 // no type == dynamic | |
| 628 if (node == null) { | |
| 629 return _assertTrue(type == null || type.isDynamic); | |
| 630 } | |
| 631 if (type == null) { | |
| 632 return _assertTrue(false); | |
| 633 } | |
| 634 // prepare name | |
| 635 SimpleIdentifier prefixIdentifier = null; | |
| 636 Identifier nameIdentifier = node.name; | |
| 637 if (nameIdentifier is PrefixedIdentifier) { | |
| 638 PrefixedIdentifier prefixedIdentifier = nameIdentifier; | |
| 639 prefixIdentifier = prefixedIdentifier.prefix; | |
| 640 nameIdentifier = prefixedIdentifier.identifier; | |
| 641 } | |
| 642 String nodeName = nameIdentifier.name; | |
| 643 // check specific type kinds | |
| 644 if (type is ParameterizedType) { | |
| 645 _assertEquals(nodeName, type.name); | |
| 646 _assertElementVisibleWithPrefix(prefixIdentifier, type.element); | |
| 647 // check arguments | |
| 648 TypeArgumentList nodeArgumentList = node.typeArguments; | |
| 649 List<DartType> typeArguments = type.typeArguments; | |
| 650 if (nodeArgumentList == null) { | |
| 651 // Node doesn't have type arguments, so all type arguments of the | |
| 652 // element must be "dynamic". | |
| 653 for (DartType typeArgument in typeArguments) { | |
| 654 _assertTrue(typeArgument.isDynamic); | |
| 655 } | |
| 656 } else { | |
| 657 List<TypeName> nodeArguments = nodeArgumentList.arguments; | |
| 658 _assertSameTypes(nodeArguments, typeArguments); | |
| 659 } | |
| 660 } else if (type is TypeParameterType) { | |
| 661 _assertEquals(nodeName, type.name); | |
| 662 // TODO(scheglov) it should be possible to rename type parameters | |
| 663 } else if (type.isVoid) { | |
| 664 _assertEquals(nodeName, 'void'); | |
| 665 } else if (type.isDynamic) { | |
| 666 _assertEquals(nodeName, 'dynamic'); | |
| 667 } else { | |
| 668 // TODO(scheglov) support other types | |
| 669 logger.log('node: $node type: $type type.type: ${type.runtimeType}'); | |
| 670 _assertTrue(false); | |
| 671 } | |
| 672 } | |
| 673 | |
| 674 void _assertSameTypeParameter( | |
| 675 TypeParameter node, TypeParameterElement element) { | |
| 676 _assertSameType(node.bound, element.bound); | |
| 677 } | |
| 678 | |
| 679 void _assertSameTypeParameters( | |
| 680 TypeParameterList nodesList, List<TypeParameterElement> elements) { | |
| 681 if (nodesList == null) { | |
| 682 return _assertEquals(elements.length, 0); | |
| 683 } | |
| 684 List<TypeParameter> nodes = nodesList.typeParameters; | |
| 685 int length = nodes.length; | |
| 686 _assertEquals(length, elements.length); | |
| 687 for (int i = 0; i < length; i++) { | |
| 688 _assertSameTypeParameter(nodes[i], elements[i]); | |
| 689 } | |
| 690 } | |
| 691 | |
| 692 void _assertSameTypes(List<TypeName> nodes, List<DartType> types) { | |
| 693 int length = nodes.length; | |
| 694 _assertEquals(length, types.length); | |
| 695 for (int i = 0; i < length; i++) { | |
| 696 _assertSameType(nodes[i], types[i]); | |
| 697 } | |
| 698 } | |
| 699 | |
| 700 void _assertTrue(bool condition) { | |
| 701 if (!condition) { | |
| 702 throw new _DeclarationMismatchException(); | |
| 703 } | |
| 704 } | |
| 705 | |
| 706 /** | |
| 707 * Given that the comparison is to begin with the given [element], capture | |
| 708 * the enclosing elements that might be used while performing the comparison. | |
| 709 */ | |
| 710 void _captureEnclosingElements(Element element) { | |
| 711 Element parent = | |
| 712 element is CompilationUnitElement ? element : element.enclosingElement; | |
| 713 while (parent != null) { | |
| 714 if (parent is CompilationUnitElement) { | |
| 715 _enclosingUnit = parent; | |
| 716 _enclosingLibrary = element.library; | |
| 717 } else if (parent is ClassElement) { | |
| 718 if (_enclosingClass == null) { | |
| 719 _enclosingClass = parent; | |
| 720 } | |
| 721 } else if (parent is FunctionTypeAliasElement) { | |
| 722 if (_enclosingAlias == null) { | |
| 723 _enclosingAlias = parent; | |
| 724 } | |
| 725 } else if (parent is ParameterElement) { | |
| 726 if (_enclosingParameter == null) { | |
| 727 _enclosingParameter = parent; | |
| 728 } | |
| 729 } | |
| 730 parent = parent.enclosingElement; | |
| 731 } | |
| 732 } | |
| 733 | |
| 734 void _gatherElements(Element element) { | |
| 735 _ElementsGatherer gatherer = new _ElementsGatherer(this); | |
| 736 element.accept(gatherer); | |
| 737 // TODO(scheglov) what if a change in a directive? | |
| 738 if (identical(element, _enclosingLibrary.definingCompilationUnit)) { | |
| 739 gatherer.addElements(_enclosingLibrary.imports); | |
| 740 gatherer.addElements(_enclosingLibrary.exports); | |
| 741 gatherer.addElements(_enclosingLibrary.parts); | |
| 742 } | |
| 743 } | |
| 744 | |
| 745 void _processElement(Element element) { | |
| 746 _assertNotNull(element); | |
| 747 if (!_allElements.contains(element)) { | |
| 748 throw new _DeclarationMismatchException(); | |
| 749 } | |
| 750 _removedElements.remove(element); | |
| 751 } | |
| 752 | |
| 753 void _removeElement(Element element) { | |
| 754 if (element != null) { | |
| 755 Element enclosingElement = element.enclosingElement; | |
| 756 if (element is MethodElement) { | |
| 757 ClassElement classElement = enclosingElement; | |
| 758 _removeIdenticalElement(classElement.methods, element); | |
| 759 } else if (element is PropertyAccessorElement) { | |
| 760 if (enclosingElement is ClassElement) { | |
| 761 _removeIdenticalElement(enclosingElement.accessors, element); | |
| 762 } | |
| 763 if (enclosingElement is CompilationUnitElement) { | |
| 764 _removeIdenticalElement(enclosingElement.accessors, element); | |
| 765 } | |
| 766 } | |
| 767 } | |
| 768 } | |
| 769 | |
| 770 /** | |
| 771 * Return the [Element] in [elements] with the given [name]. | |
| 772 */ | |
| 773 static Element _findElement(List<Element> elements, String name) { | |
| 774 for (Element element in elements) { | |
| 775 if (element.name == name) { | |
| 776 return element; | |
| 777 } | |
| 778 } | |
| 779 return null; | |
| 780 } | |
| 781 | |
| 782 /** | |
| 783 * Return the [UriReferencedElement] from [elements] with the given [uri], or | |
| 784 * `null` if there is no such element. | |
| 785 */ | |
| 786 static UriReferencedElement _findUriReferencedElement( | |
| 787 List<UriReferencedElement> elements, String uri) { | |
| 788 for (UriReferencedElement element in elements) { | |
| 789 if (element.uri == uri) { | |
| 790 return element; | |
| 791 } | |
| 792 } | |
| 793 return null; | |
| 794 } | |
| 795 | |
| 796 /** | |
| 797 * Return the value of [literal], or `null` if the string is not a constant | |
| 798 * string without any string interpolation. | |
| 799 */ | |
| 800 static String _getStringValue(StringLiteral literal) { | |
| 801 if (literal is StringInterpolation) { | |
| 802 return null; | |
| 803 } | |
| 804 return literal.stringValue; | |
| 805 } | |
| 806 | |
| 807 /** | |
| 808 * Removes the first element identical to the given [element] from [elements]. | |
| 809 */ | |
| 810 static void _removeIdenticalElement(List elements, Object element) { | |
| 811 int length = elements.length; | |
| 812 for (int i = 0; i < length; i++) { | |
| 813 if (identical(elements[i], element)) { | |
| 814 elements.removeAt(i); | |
| 815 return; | |
| 816 } | |
| 817 } | |
| 818 } | |
| 819 | |
| 820 static void _setLocalElements( | |
| 821 ExecutableElementImpl to, ExecutableElement from) { | |
| 822 if (from != null) { | |
| 823 to.functions = from.functions; | |
| 824 to.labels = from.labels; | |
| 825 to.localVariables = from.localVariables; | |
| 826 to.parameters = from.parameters; | |
| 827 } | |
| 828 } | |
| 829 } | |
| 830 | |
| 831 /** | |
| 832 * Describes how declarations match an existing elements model. | |
| 833 */ | |
| 834 class DeclarationMatchKind { | |
| 835 /** | |
| 836 * Complete match, no API changes. | |
| 837 */ | |
| 838 static const MATCH = const DeclarationMatchKind('MATCH'); | |
| 839 | |
| 840 /** | |
| 841 * Has API changes that we might be able to resolve incrementally. | |
| 842 */ | |
| 843 static const MISMATCH_OK = const DeclarationMatchKind('MISMATCH_OK'); | |
| 844 | |
| 845 /** | |
| 846 * Has API changes that we cannot resolve incrementally. | |
| 847 */ | |
| 848 static const MISMATCH = const DeclarationMatchKind('MISMATCH'); | |
| 849 | |
| 850 final String name; | |
| 851 | |
| 852 const DeclarationMatchKind(this.name); | |
| 853 | |
| 854 @override | |
| 855 String toString() => name; | |
| 856 } | |
| 857 | |
| 858 /** | |
| 859 * The [Delta] implementation used by incremental resolver. | 51 * The [Delta] implementation used by incremental resolver. |
| 860 * It keeps Dart results that are either don't change or are updated. | 52 * It keeps Dart results that are either don't change or are updated. |
| 861 */ | 53 */ |
| 862 class IncrementalBodyDelta extends Delta { | 54 class IncrementalBodyDelta extends Delta { |
| 863 /** | 55 /** |
| 864 * The offset of the changed contents. | 56 * The offset of the changed contents. |
| 865 */ | 57 */ |
| 866 final int updateOffset; | 58 final int updateOffset; |
| 867 | 59 |
| 868 /** | 60 /** |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1055 _typeProvider = definingUnit.context.typeProvider, | 247 _typeProvider = definingUnit.context.typeProvider, |
| 1056 _typeSystem = definingUnit.context.typeSystem, | 248 _typeSystem = definingUnit.context.typeSystem, |
| 1057 _definingLibrary = definingUnit.library, | 249 _definingLibrary = definingUnit.library, |
| 1058 _source = definingUnit.source, | 250 _source = definingUnit.source, |
| 1059 _librarySource = definingUnit.library.source, | 251 _librarySource = definingUnit.library.source, |
| 1060 _updateEndOld = updateEndOld, | 252 _updateEndOld = updateEndOld, |
| 1061 _updateEndNew = updateEndNew, | 253 _updateEndNew = updateEndNew, |
| 1062 _updateDelta = updateEndNew - updateEndOld; | 254 _updateDelta = updateEndNew - updateEndOld; |
| 1063 | 255 |
| 1064 /** | 256 /** |
| 1065 * Resolve [node], reporting any errors or warnings to the given listener. | 257 * Resolve [body], reporting any errors or warnings to the given listener. |
| 1066 * | 258 * |
| 1067 * [node] - the root of the AST structure to be resolved. | 259 * [body] - the root of the AST structure to be resolved. |
| 1068 * | 260 * |
| 1069 * Returns `true` if resolution was successful. | 261 * Returns `true` if resolution was successful. |
| 1070 */ | 262 */ |
| 1071 bool resolve(AstNode node) { | 263 bool resolve(BlockFunctionBody body) { |
| 1072 logger.enter('resolve: $_definingUnit'); | 264 logger.enter('resolve: $_definingUnit'); |
| 1073 try { | 265 try { |
| 1074 AstNode rootNode = _findResolutionRoot(node); | 266 Declaration executable = _findResolutionRoot(body); |
| 1075 _prepareResolutionContext(rootNode); | 267 _prepareResolutionContext(executable); |
| 1076 // update elements | 268 // update elements |
| 1077 _updateCache(); | 269 _updateCache(); |
| 1078 _updateElementNameOffsets(); | 270 _updateElementNameOffsets(); |
| 1079 _buildElements(rootNode); | 271 _buildElements(executable, body); |
| 1080 if (!_canBeIncrementallyResolved(rootNode)) { | |
| 1081 return false; | |
| 1082 } | |
| 1083 // resolve | 272 // resolve |
| 1084 _resolveReferences(rootNode); | 273 _resolveReferences(executable); |
| 1085 _computeConstants(rootNode); | 274 _computeConstants(executable); |
| 1086 _resolveErrors = errorListener.getErrorsForSource(_source); | 275 _resolveErrors = errorListener.getErrorsForSource(_source); |
| 1087 // verify | 276 // verify |
| 1088 _verify(rootNode); | 277 _verify(executable); |
| 1089 _context.invalidateLibraryHints(_librarySource); | 278 _context.invalidateLibraryHints(_librarySource); |
| 1090 // update entry errors | 279 // update entry errors |
| 1091 _updateEntry(); | 280 _updateEntry(); |
| 1092 // OK | 281 // OK |
| 1093 return true; | 282 return true; |
| 1094 } finally { | 283 } finally { |
| 1095 logger.exit(); | 284 logger.exit(); |
| 1096 } | 285 } |
| 1097 } | 286 } |
| 1098 | 287 |
| 1099 void _buildElements(AstNode node) { | 288 void _buildElements(Declaration executable, AstNode node) { |
| 1100 LoggingTimer timer = logger.startTimer(); | 289 LoggingTimer timer = logger.startTimer(); |
| 1101 try { | 290 try { |
| 1102 ElementHolder holder = new ElementHolder(); | 291 ElementHolder holder = new ElementHolder(); |
| 1103 ElementBuilder builder = new ElementBuilder(holder, _definingUnit); | 292 ElementBuilder builder = new ElementBuilder(holder, _definingUnit); |
| 1104 if (_resolutionContext.enclosingClassDeclaration != null) { | 293 builder.initForFunctionBodyIncrementalResolution(); |
| 1105 builder.visitClassDeclarationIncrementally( | |
| 1106 _resolutionContext.enclosingClassDeclaration); | |
| 1107 } | |
| 1108 node.accept(builder); | 294 node.accept(builder); |
| 295 // Move local elements into the ExecutableElementImpl. |
| 296 ExecutableElementImpl executableElement = |
| 297 executable.element as ExecutableElementImpl; |
| 298 executableElement.localVariables = holder.localVariables; |
| 299 executableElement.functions = holder.functions; |
| 300 executableElement.labels = holder.labels; |
| 301 holder.validate(); |
| 1109 } finally { | 302 } finally { |
| 1110 timer.stop('build elements'); | 303 timer.stop('build elements'); |
| 1111 } | 304 } |
| 1112 } | 305 } |
| 1113 | 306 |
| 1114 /** | 307 /** |
| 1115 * Return `true` if [node] does not have element model changes, or these | |
| 1116 * changes can be incrementally propagated. | |
| 1117 */ | |
| 1118 bool _canBeIncrementallyResolved(AstNode node) { | |
| 1119 // If we are replacing the whole declaration, this means that its signature | |
| 1120 // is changed. It might be an API change, or not. | |
| 1121 // | |
| 1122 // If, for example, a required parameter is changed, it is not an API | |
| 1123 // change, but we want to find the existing corresponding Element in the | |
| 1124 // enclosing one, set it for the node and update as needed. | |
| 1125 // | |
| 1126 // If, for example, the name of a method is changed, it is an API change, | |
| 1127 // we need to know the old Element and the new Element. Again, we need to | |
| 1128 // check the whole enclosing Element. | |
| 1129 if (node is Declaration) { | |
| 1130 node = node.parent; | |
| 1131 } | |
| 1132 Element element = _getElement(node); | |
| 1133 DeclarationMatcher matcher = new DeclarationMatcher(); | |
| 1134 DeclarationMatchKind matchKind = matcher.matches(node, element); | |
| 1135 if (matchKind == DeclarationMatchKind.MATCH) { | |
| 1136 return true; | |
| 1137 } | |
| 1138 // mismatch that cannot be incrementally fixed | |
| 1139 return false; | |
| 1140 } | |
| 1141 | |
| 1142 /** | |
| 1143 * Return `true` if the given node can be resolved independently of any other | |
| 1144 * nodes. | |
| 1145 * | |
| 1146 * *Note*: This method needs to be kept in sync with | |
| 1147 * [ScopeBuilder.ContextBuilder]. | |
| 1148 * | |
| 1149 * [node] - the node being tested. | |
| 1150 */ | |
| 1151 bool _canBeResolved(AstNode node) => | |
| 1152 node is ClassDeclaration || | |
| 1153 node is ClassTypeAlias || | |
| 1154 node is CompilationUnit || | |
| 1155 node is ConstructorDeclaration || | |
| 1156 node is FunctionDeclaration || | |
| 1157 node is FunctionTypeAlias || | |
| 1158 node is MethodDeclaration || | |
| 1159 node is TopLevelVariableDeclaration; | |
| 1160 | |
| 1161 /** | |
| 1162 * Compute a value for all of the constants in the given [node]. | 308 * Compute a value for all of the constants in the given [node]. |
| 1163 */ | 309 */ |
| 1164 void _computeConstants(AstNode node) { | 310 void _computeConstants(AstNode node) { |
| 1165 // compute values | 311 // compute values |
| 1166 { | 312 { |
| 1167 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit); | 313 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit); |
| 1168 ConstantValueComputer computer = new ConstantValueComputer(_context, | 314 ConstantValueComputer computer = new ConstantValueComputer(_context, |
| 1169 _typeProvider, _context.declaredVariables, null, _typeSystem); | 315 _typeProvider, _context.declaredVariables, null, _typeSystem); |
| 1170 computer.add(unit, _source, _librarySource); | 316 computer.add(unit, _source, _librarySource); |
| 1171 computer.computeValues(); | 317 computer.computeValues(); |
| 1172 } | 318 } |
| 1173 // validate | 319 // validate |
| 1174 { | 320 { |
| 1175 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source); | 321 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source); |
| 1176 ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter, | 322 ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter, |
| 1177 _definingLibrary, _typeProvider, _context.declaredVariables); | 323 _definingLibrary, _typeProvider, _context.declaredVariables); |
| 1178 node.accept(constantVerifier); | 324 node.accept(constantVerifier); |
| 1179 } | 325 } |
| 1180 } | 326 } |
| 1181 | 327 |
| 1182 /** | 328 /** |
| 1183 * Starting at [node], find the smallest AST node that can be resolved | 329 * Starting at [node], find the smallest AST node that can be resolved |
| 1184 * independently of any other nodes. Return the node that was found. | 330 * independently of any other nodes. Return the node that was found. |
| 1185 * | 331 * |
| 1186 * [node] - the node at which the search is to begin | 332 * [node] - the node at which the search is to begin |
| 1187 * | 333 * |
| 1188 * Throws [AnalysisException] if there is no such node. | 334 * Throws [AnalysisException] if there is no such node. |
| 1189 */ | 335 */ |
| 1190 AstNode _findResolutionRoot(AstNode node) { | 336 Declaration _findResolutionRoot(AstNode node) { |
| 1191 while (node != null) { | 337 while (node != null) { |
| 1192 if (_canBeResolved(node)) { | 338 if (node is ConstructorDeclaration || |
| 339 node is FunctionDeclaration || |
| 340 node is MethodDeclaration) { |
| 1193 return node; | 341 return node; |
| 1194 } | 342 } |
| 1195 node = node.parent; | 343 node = node.parent; |
| 1196 } | 344 } |
| 1197 throw new AnalysisException("Cannot resolve node: no resolvable node"); | 345 throw new AnalysisException("Cannot resolve node: no resolvable node"); |
| 1198 } | 346 } |
| 1199 | 347 |
| 1200 /** | |
| 1201 * Return the element defined by [node], or `null` if the node does not | |
| 1202 * define an element. | |
| 1203 */ | |
| 1204 Element _getElement(AstNode node) { | |
| 1205 if (node is Declaration) { | |
| 1206 return node.element; | |
| 1207 } else if (node is CompilationUnit) { | |
| 1208 return node.element; | |
| 1209 } | |
| 1210 return null; | |
| 1211 } | |
| 1212 | |
| 1213 void _prepareResolutionContext(AstNode node) { | 348 void _prepareResolutionContext(AstNode node) { |
| 1214 if (_resolutionContext == null) { | 349 if (_resolutionContext == null) { |
| 1215 _resolutionContext = | 350 _resolutionContext = |
| 1216 ResolutionContextBuilder.contextFor(node, errorListener); | 351 ResolutionContextBuilder.contextFor(node, errorListener); |
| 1217 } | 352 } |
| 1218 } | 353 } |
| 1219 | 354 |
| 1220 _resolveReferences(AstNode node) { | 355 _resolveReferences(AstNode node) { |
| 1221 LoggingTimer timer = logger.startTimer(); | 356 LoggingTimer timer = logger.startTimer(); |
| 1222 try { | 357 try { |
| (...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1525 logger.log( | 660 logger.log( |
| 1526 'Failure: class declarations mismatch $oldLength vs. $newLen
gth'); | 661 'Failure: class declarations mismatch $oldLength vs. $newLen
gth'); |
| 1527 return false; | 662 return false; |
| 1528 } | 663 } |
| 1529 } else if (oldParent is FunctionDeclaration && | 664 } else if (oldParent is FunctionDeclaration && |
| 1530 newParent is FunctionDeclaration || | 665 newParent is FunctionDeclaration || |
| 1531 oldParent is ConstructorDeclaration && | 666 oldParent is ConstructorDeclaration && |
| 1532 newParent is ConstructorDeclaration || | 667 newParent is ConstructorDeclaration || |
| 1533 oldParent is MethodDeclaration && | 668 oldParent is MethodDeclaration && |
| 1534 newParent is MethodDeclaration) { | 669 newParent is MethodDeclaration) { |
| 1535 Element oldElement = (oldParent as Declaration).element; | 670 if (oldParents.length == i || newParents.length == i) { |
| 1536 if (new DeclarationMatcher().matches(newParent, oldElement) == | |
| 1537 DeclarationMatchKind.MATCH) { | |
| 1538 oldNode = oldParent; | |
| 1539 newNode = newParent; | |
| 1540 found = true; | |
| 1541 } else { | |
| 1542 return false; | 671 return false; |
| 1543 } | 672 } |
| 1544 } else if (oldParent is FunctionBody && newParent is FunctionBody) { | 673 } else if (oldParent is FunctionBody && newParent is FunctionBody) { |
| 1545 if (oldParent is BlockFunctionBody && | 674 if (oldParent is BlockFunctionBody && |
| 1546 newParent is BlockFunctionBody) { | 675 newParent is BlockFunctionBody) { |
| 1547 oldNode = oldParent; | 676 oldNode = oldParent; |
| 1548 newNode = newParent; | 677 newNode = newParent; |
| 1549 found = true; | 678 found = true; |
| 1550 break; | 679 break; |
| 1551 } | 680 } |
| (...skipping 513 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2065 ResolutionContext context = new ResolutionContext(); | 1194 ResolutionContext context = new ResolutionContext(); |
| 2066 context.scope = scope; | 1195 context.scope = scope; |
| 2067 context.enclosingUnit = builder._enclosingUnit; | 1196 context.enclosingUnit = builder._enclosingUnit; |
| 2068 context.enclosingClassDeclaration = builder._enclosingClassDeclaration; | 1197 context.enclosingClassDeclaration = builder._enclosingClassDeclaration; |
| 2069 context.enclosingClass = builder._enclosingClass; | 1198 context.enclosingClass = builder._enclosingClass; |
| 2070 return context; | 1199 return context; |
| 2071 } | 1200 } |
| 2072 } | 1201 } |
| 2073 | 1202 |
| 2074 /** | 1203 /** |
| 2075 * Instances of the class [_DeclarationMismatchException] represent an exception | |
| 2076 * that is thrown when the element model defined by a given AST structure does | |
| 2077 * not match an existing element model. | |
| 2078 */ | |
| 2079 class _DeclarationMismatchException {} | |
| 2080 | |
| 2081 /** | |
| 2082 * Adjusts the location of each Element that moved. | 1204 * Adjusts the location of each Element that moved. |
| 2083 * | 1205 * |
| 2084 * Since `==` and `hashCode` of a local variable or function Element are based | 1206 * Since `==` and `hashCode` of a local variable or function Element are based |
| 2085 * on the element name offsets, we also need to remove these elements from the | 1207 * on the element name offsets, we also need to remove these elements from the |
| 2086 * cache to avoid a memory leak. TODO(scheglov) fix and remove this | 1208 * cache to avoid a memory leak. TODO(scheglov) fix and remove this |
| 2087 */ | 1209 */ |
| 2088 class _ElementOffsetUpdater extends GeneralizingElementVisitor { | 1210 class _ElementOffsetUpdater extends GeneralizingElementVisitor { |
| 2089 final int updateOffset; | 1211 final int updateOffset; |
| 2090 final int updateDelta; | 1212 final int updateDelta; |
| 2091 final AnalysisCache cache; | 1213 final AnalysisCache cache; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2166 } | 1288 } |
| 2167 // next | 1289 // next |
| 2168 if (token.type == TokenType.EOF) { | 1290 if (token.type == TokenType.EOF) { |
| 2169 break; | 1291 break; |
| 2170 } | 1292 } |
| 2171 token = token.next; | 1293 token = token.next; |
| 2172 } | 1294 } |
| 2173 } | 1295 } |
| 2174 } | 1296 } |
| 2175 | 1297 |
| 2176 class _ElementsGatherer extends GeneralizingElementVisitor { | |
| 2177 final DeclarationMatcher matcher; | |
| 2178 | |
| 2179 _ElementsGatherer(this.matcher); | |
| 2180 | |
| 2181 void addElements(List<Element> elements) { | |
| 2182 for (Element element in elements) { | |
| 2183 if (!element.isSynthetic) { | |
| 2184 _addElement(element); | |
| 2185 } | |
| 2186 } | |
| 2187 } | |
| 2188 | |
| 2189 @override | |
| 2190 visitElement(Element element) { | |
| 2191 _addElement(element); | |
| 2192 super.visitElement(element); | |
| 2193 } | |
| 2194 | |
| 2195 @override | |
| 2196 visitExecutableElement(ExecutableElement element) { | |
| 2197 _addElement(element); | |
| 2198 } | |
| 2199 | |
| 2200 @override | |
| 2201 visitParameterElement(ParameterElement element) {} | |
| 2202 | |
| 2203 @override | |
| 2204 visitPropertyAccessorElement(PropertyAccessorElement element) { | |
| 2205 if (!element.isSynthetic) { | |
| 2206 _addElement(element); | |
| 2207 } | |
| 2208 // Don't visit children (such as synthetic setter parameters). | |
| 2209 } | |
| 2210 | |
| 2211 @override | |
| 2212 visitPropertyInducingElement(PropertyInducingElement element) { | |
| 2213 if (!element.isSynthetic) { | |
| 2214 _addElement(element); | |
| 2215 } | |
| 2216 // Don't visit children (such as property accessors). | |
| 2217 } | |
| 2218 | |
| 2219 @override | |
| 2220 visitTypeParameterElement(TypeParameterElement element) {} | |
| 2221 | |
| 2222 void _addElement(Element element) { | |
| 2223 if (element != null) { | |
| 2224 matcher._allElements.add(element); | |
| 2225 matcher._removedElements.add(element); | |
| 2226 } | |
| 2227 } | |
| 2228 } | |
| 2229 | |
| 2230 /** | 1298 /** |
| 2231 * Describes how two [Token]s are different. | 1299 * Describes how two [Token]s are different. |
| 2232 */ | 1300 */ |
| 2233 class _TokenDifferenceKind { | 1301 class _TokenDifferenceKind { |
| 2234 static const COMMENT = const _TokenDifferenceKind('COMMENT'); | 1302 static const COMMENT = const _TokenDifferenceKind('COMMENT'); |
| 2235 static const COMMENT_DOC = const _TokenDifferenceKind('COMMENT_DOC'); | 1303 static const COMMENT_DOC = const _TokenDifferenceKind('COMMENT_DOC'); |
| 2236 static const CONTENT = const _TokenDifferenceKind('CONTENT'); | 1304 static const CONTENT = const _TokenDifferenceKind('CONTENT'); |
| 2237 static const OFFSET = const _TokenDifferenceKind('OFFSET'); | 1305 static const OFFSET = const _TokenDifferenceKind('OFFSET'); |
| 2238 | 1306 |
| 2239 final String name; | 1307 final String name; |
| 2240 | 1308 |
| 2241 const _TokenDifferenceKind(this.name); | 1309 const _TokenDifferenceKind(this.name); |
| 2242 | 1310 |
| 2243 @override | 1311 @override |
| 2244 String toString() => name; | 1312 String toString() => name; |
| 2245 } | 1313 } |
| 2246 | 1314 |
| 2247 class _TokenPair { | 1315 class _TokenPair { |
| 2248 final _TokenDifferenceKind kind; | 1316 final _TokenDifferenceKind kind; |
| 2249 final Token oldToken; | 1317 final Token oldToken; |
| 2250 final Token newToken; | 1318 final Token newToken; |
| 2251 _TokenPair(this.kind, this.oldToken, this.newToken); | 1319 _TokenPair(this.kind, this.oldToken, this.newToken); |
| 2252 } | 1320 } |
| OLD | NEW |