| 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 analyzer.src.dart.resolver.scope; |
| 6 |
| 7 import 'dart:collection'; |
| 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/element/element.dart'; |
| 11 import 'package:analyzer/error/error.dart'; |
| 12 import 'package:analyzer/src/dart/element/element.dart'; |
| 13 import 'package:analyzer/src/error/codes.dart'; |
| 14 import 'package:analyzer/src/generated/engine.dart'; |
| 15 import 'package:analyzer/src/generated/java_engine.dart'; |
| 16 import 'package:analyzer/src/generated/source.dart'; |
| 17 |
| 18 /** |
| 19 * The scope defined by a block. |
| 20 */ |
| 21 class BlockScope extends EnclosedScope { |
| 22 /** |
| 23 * Initialize a newly created scope, enclosed within the [enclosingScope], |
| 24 * based on the given [block]. |
| 25 */ |
| 26 BlockScope(Scope enclosingScope, Block block) : super(enclosingScope) { |
| 27 if (block == null) { |
| 28 throw new ArgumentError("block cannot be null"); |
| 29 } |
| 30 _defineElements(block); |
| 31 } |
| 32 |
| 33 void _defineElements(Block block) { |
| 34 for (Element element in elementsInBlock(block)) { |
| 35 define(element); |
| 36 } |
| 37 } |
| 38 |
| 39 /** |
| 40 * Return the elements that are declared directly in the given [block]. This |
| 41 * does not include elements declared in nested blocks. |
| 42 */ |
| 43 static Iterable<Element> elementsInBlock(Block block) sync* { |
| 44 NodeList<Statement> statements = block.statements; |
| 45 int statementCount = statements.length; |
| 46 for (int i = 0; i < statementCount; i++) { |
| 47 Statement statement = statements[i]; |
| 48 if (statement is VariableDeclarationStatement) { |
| 49 NodeList<VariableDeclaration> variables = statement.variables.variables; |
| 50 int variableCount = variables.length; |
| 51 for (int j = 0; j < variableCount; j++) { |
| 52 yield variables[j].element; |
| 53 } |
| 54 } else if (statement is FunctionDeclarationStatement) { |
| 55 yield statement.functionDeclaration.element; |
| 56 } |
| 57 } |
| 58 } |
| 59 } |
| 60 |
| 61 /** |
| 62 * The scope defined by a class. |
| 63 */ |
| 64 class ClassScope extends EnclosedScope { |
| 65 /** |
| 66 * Initialize a newly created scope, enclosed within the [enclosingScope], |
| 67 * based on the given [classElement]. |
| 68 */ |
| 69 ClassScope(Scope enclosingScope, ClassElement classElement) |
| 70 : super(enclosingScope) { |
| 71 if (classElement == null) { |
| 72 throw new ArgumentError("class element cannot be null"); |
| 73 } |
| 74 _defineMembers(classElement); |
| 75 } |
| 76 |
| 77 @override |
| 78 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { |
| 79 if (existing is PropertyAccessorElement && duplicate is MethodElement) { |
| 80 if (existing.nameOffset < duplicate.nameOffset) { |
| 81 return new AnalysisError( |
| 82 duplicate.source, |
| 83 duplicate.nameOffset, |
| 84 duplicate.nameLength, |
| 85 CompileTimeErrorCode.METHOD_AND_GETTER_WITH_SAME_NAME, |
| 86 [existing.displayName]); |
| 87 } else { |
| 88 return new AnalysisError( |
| 89 existing.source, |
| 90 existing.nameOffset, |
| 91 existing.nameLength, |
| 92 CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME, |
| 93 [existing.displayName]); |
| 94 } |
| 95 } |
| 96 return super.getErrorForDuplicate(existing, duplicate); |
| 97 } |
| 98 |
| 99 /** |
| 100 * Define the instance members defined by the given [classElement]. |
| 101 */ |
| 102 void _defineMembers(ClassElement classElement) { |
| 103 List<PropertyAccessorElement> accessors = classElement.accessors; |
| 104 int accessorLength = accessors.length; |
| 105 for (int i = 0; i < accessorLength; i++) { |
| 106 define(accessors[i]); |
| 107 } |
| 108 List<MethodElement> methods = classElement.methods; |
| 109 int methodLength = methods.length; |
| 110 for (int i = 0; i < methodLength; i++) { |
| 111 define(methods[i]); |
| 112 } |
| 113 } |
| 114 } |
| 115 |
| 116 /** |
| 117 * The scope defined for the initializers in a constructor. |
| 118 */ |
| 119 class ConstructorInitializerScope extends EnclosedScope { |
| 120 /** |
| 121 * Initialize a newly created scope, enclosed within the [enclosingScope]. |
| 122 */ |
| 123 ConstructorInitializerScope(Scope enclosingScope, ConstructorElement element) |
| 124 : super(enclosingScope) { |
| 125 _initializeFieldFormalParameters(element); |
| 126 } |
| 127 |
| 128 /** |
| 129 * Initialize the local scope with all of the field formal parameters. |
| 130 */ |
| 131 void _initializeFieldFormalParameters(ConstructorElement element) { |
| 132 for (ParameterElement parameter in element.parameters) { |
| 133 if (parameter is FieldFormalParameterElement) { |
| 134 define(parameter); |
| 135 } |
| 136 } |
| 137 } |
| 138 } |
| 139 |
| 140 /** |
| 141 * A scope that is lexically enclosed in another scope. |
| 142 */ |
| 143 class EnclosedScope extends Scope { |
| 144 /** |
| 145 * The scope in which this scope is lexically enclosed. |
| 146 */ |
| 147 @override |
| 148 final Scope enclosingScope; |
| 149 |
| 150 /** |
| 151 * Initialize a newly created scope, enclosed within the [enclosingScope]. |
| 152 */ |
| 153 EnclosedScope(this.enclosingScope); |
| 154 |
| 155 @override |
| 156 Element internalLookup( |
| 157 Identifier identifier, String name, LibraryElement referencingLibrary) { |
| 158 Element element = localLookup(name, referencingLibrary); |
| 159 if (element != null) { |
| 160 return element; |
| 161 } |
| 162 // Check enclosing scope. |
| 163 return enclosingScope.internalLookup(identifier, name, referencingLibrary); |
| 164 } |
| 165 |
| 166 @override |
| 167 Element _internalLookupPrefixed(PrefixedIdentifier identifier, String prefix, |
| 168 String name, LibraryElement referencingLibrary) { |
| 169 return enclosingScope._internalLookupPrefixed( |
| 170 identifier, prefix, name, referencingLibrary); |
| 171 } |
| 172 } |
| 173 |
| 174 /** |
| 175 * The scope defined by a function. |
| 176 */ |
| 177 class FunctionScope extends EnclosedScope { |
| 178 /** |
| 179 * The element representing the function that defines this scope. |
| 180 */ |
| 181 final ExecutableElement _functionElement; |
| 182 |
| 183 /** |
| 184 * A flag indicating whether the parameters have already been defined, used to |
| 185 * prevent the parameters from being defined multiple times. |
| 186 */ |
| 187 bool _parametersDefined = false; |
| 188 |
| 189 /** |
| 190 * Initialize a newly created scope, enclosed within the [enclosingScope], |
| 191 * that represents the given [_functionElement]. |
| 192 */ |
| 193 FunctionScope(Scope enclosingScope, this._functionElement) |
| 194 : super(new EnclosedScope(new EnclosedScope(enclosingScope))) { |
| 195 if (_functionElement == null) { |
| 196 throw new ArgumentError("function element cannot be null"); |
| 197 } |
| 198 _defineTypeParameters(); |
| 199 } |
| 200 |
| 201 /** |
| 202 * Define the parameters for the given function in the scope that encloses |
| 203 * this function. |
| 204 */ |
| 205 void defineParameters() { |
| 206 if (_parametersDefined) { |
| 207 return; |
| 208 } |
| 209 _parametersDefined = true; |
| 210 Scope parameterScope = enclosingScope; |
| 211 List<ParameterElement> parameters = _functionElement.parameters; |
| 212 int length = parameters.length; |
| 213 for (int i = 0; i < length; i++) { |
| 214 ParameterElement parameter = parameters[i]; |
| 215 if (!parameter.isInitializingFormal) { |
| 216 parameterScope.define(parameter); |
| 217 } |
| 218 } |
| 219 } |
| 220 |
| 221 /** |
| 222 * Define the type parameters for the function. |
| 223 */ |
| 224 void _defineTypeParameters() { |
| 225 Scope typeParameterScope = enclosingScope.enclosingScope; |
| 226 List<TypeParameterElement> typeParameters = _functionElement.typeParameters; |
| 227 int length = typeParameters.length; |
| 228 for (int i = 0; i < length; i++) { |
| 229 TypeParameterElement typeParameter = typeParameters[i]; |
| 230 typeParameterScope.define(typeParameter); |
| 231 } |
| 232 } |
| 233 } |
| 234 |
| 235 /** |
| 236 * The scope defined by a function type alias. |
| 237 */ |
| 238 class FunctionTypeScope extends EnclosedScope { |
| 239 final FunctionTypeAliasElement _typeElement; |
| 240 |
| 241 bool _parametersDefined = false; |
| 242 |
| 243 /** |
| 244 * Initialize a newly created scope, enclosed within the [enclosingScope], |
| 245 * that represents the given [_typeElement]. |
| 246 */ |
| 247 FunctionTypeScope(Scope enclosingScope, this._typeElement) |
| 248 : super(new EnclosedScope(enclosingScope)) { |
| 249 _defineTypeParameters(); |
| 250 } |
| 251 |
| 252 /** |
| 253 * Define the parameters for the function type alias. |
| 254 */ |
| 255 void defineParameters() { |
| 256 if (_parametersDefined) { |
| 257 return; |
| 258 } |
| 259 _parametersDefined = true; |
| 260 for (ParameterElement parameter in _typeElement.parameters) { |
| 261 define(parameter); |
| 262 } |
| 263 } |
| 264 |
| 265 /** |
| 266 * Define the type parameters for the function type alias. |
| 267 */ |
| 268 void _defineTypeParameters() { |
| 269 Scope typeParameterScope = enclosingScope; |
| 270 for (TypeParameterElement typeParameter in _typeElement.typeParameters) { |
| 271 typeParameterScope.define(typeParameter); |
| 272 } |
| 273 } |
| 274 } |
| 275 |
| 276 /** |
| 277 * The scope statements that can be the target of unlabeled `break` and |
| 278 * `continue` statements. |
| 279 */ |
| 280 class ImplicitLabelScope { |
| 281 /** |
| 282 * The implicit label scope associated with the top level of a function. |
| 283 */ |
| 284 static const ImplicitLabelScope ROOT = const ImplicitLabelScope._(null, null); |
| 285 |
| 286 /** |
| 287 * The implicit label scope enclosing this implicit label scope. |
| 288 */ |
| 289 final ImplicitLabelScope outerScope; |
| 290 |
| 291 /** |
| 292 * The statement that acts as a target for break and/or continue statements |
| 293 * at this scoping level. |
| 294 */ |
| 295 final Statement statement; |
| 296 |
| 297 /** |
| 298 * Initialize a newly created scope, enclosed within the [outerScope], |
| 299 * representing the given [statement]. |
| 300 */ |
| 301 const ImplicitLabelScope._(this.outerScope, this.statement); |
| 302 |
| 303 /** |
| 304 * Return the statement which should be the target of an unlabeled `break` or |
| 305 * `continue` statement, or `null` if there is no appropriate target. |
| 306 */ |
| 307 Statement getTarget(bool isContinue) { |
| 308 if (outerScope == null) { |
| 309 // This scope represents the toplevel of a function body, so it doesn't |
| 310 // match either break or continue. |
| 311 return null; |
| 312 } |
| 313 if (isContinue && statement is SwitchStatement) { |
| 314 return outerScope.getTarget(isContinue); |
| 315 } |
| 316 return statement; |
| 317 } |
| 318 |
| 319 /** |
| 320 * Initialize a newly created scope to represent a switch statement or loop |
| 321 * nested within the current scope. [statement] is the statement associated |
| 322 * with the newly created scope. |
| 323 */ |
| 324 ImplicitLabelScope nest(Statement statement) => |
| 325 new ImplicitLabelScope._(this, statement); |
| 326 } |
| 327 |
| 328 /** |
| 329 * A scope in which a single label is defined. |
| 330 */ |
| 331 class LabelScope { |
| 332 /** |
| 333 * The label scope enclosing this label scope. |
| 334 */ |
| 335 final LabelScope _outerScope; |
| 336 |
| 337 /** |
| 338 * The label defined in this scope. |
| 339 */ |
| 340 final String _label; |
| 341 |
| 342 /** |
| 343 * The element to which the label resolves. |
| 344 */ |
| 345 final LabelElement element; |
| 346 |
| 347 /** |
| 348 * The AST node to which the label resolves. |
| 349 */ |
| 350 final AstNode node; |
| 351 |
| 352 /** |
| 353 * Initialize a newly created scope, enclosed within the [_outerScope], |
| 354 * representing the label [_label]. The [node] is the AST node the label |
| 355 * resolves to. The [element] is the element the label resolves to. |
| 356 */ |
| 357 LabelScope(this._outerScope, this._label, this.node, this.element); |
| 358 |
| 359 /** |
| 360 * Return the LabelScope which defines [targetLabel], or `null` if it is not |
| 361 * defined in this scope. |
| 362 */ |
| 363 LabelScope lookup(String targetLabel) { |
| 364 if (_label == targetLabel) { |
| 365 return this; |
| 366 } |
| 367 return _outerScope?.lookup(targetLabel); |
| 368 } |
| 369 } |
| 370 |
| 371 /** |
| 372 * The scope containing all of the names available from imported libraries. |
| 373 */ |
| 374 class LibraryImportScope extends Scope { |
| 375 /** |
| 376 * The name of the property containing a list of the elements from the SDK |
| 377 * that conflict with the single name imported from non-SDK libraries. The |
| 378 * value of the property is always of type `List<Element>`. |
| 379 */ |
| 380 static const String conflictingSdkElements = 'conflictingSdkElements'; |
| 381 |
| 382 /** |
| 383 * The element representing the library in which this scope is enclosed. |
| 384 */ |
| 385 final LibraryElement _definingLibrary; |
| 386 |
| 387 /** |
| 388 * A list of the namespaces representing the names that are available in this
scope from imported |
| 389 * libraries. |
| 390 */ |
| 391 List<Namespace> _importedNamespaces; |
| 392 |
| 393 /** |
| 394 * A table mapping prefixes that have been referenced to a map from the names |
| 395 * that have been referenced to the element associated with the prefixed name. |
| 396 */ |
| 397 Map<String, Map<String, Element>> _definedPrefixedNames; |
| 398 |
| 399 /** |
| 400 * Initialize a newly created scope representing the names imported into the |
| 401 * [_definingLibrary]. |
| 402 */ |
| 403 LibraryImportScope(this._definingLibrary) { |
| 404 _createImportedNamespaces(); |
| 405 } |
| 406 |
| 407 @override |
| 408 void define(Element element) { |
| 409 if (!Scope.isPrivateName(element.displayName)) { |
| 410 super.define(element); |
| 411 } |
| 412 } |
| 413 |
| 414 @override |
| 415 Source getSource(AstNode node) { |
| 416 Source source = super.getSource(node); |
| 417 if (source == null) { |
| 418 source = _definingLibrary.definingCompilationUnit.source; |
| 419 } |
| 420 return source; |
| 421 } |
| 422 |
| 423 @override |
| 424 Element internalLookup( |
| 425 Identifier identifier, String name, LibraryElement referencingLibrary) { |
| 426 Element element = localLookup(name, referencingLibrary); |
| 427 if (element != null) { |
| 428 return element; |
| 429 } |
| 430 element = _lookupInImportedNamespaces( |
| 431 identifier, (Namespace namespace) => namespace.get(name)); |
| 432 if (element != null) { |
| 433 defineNameWithoutChecking(name, element); |
| 434 } |
| 435 return element; |
| 436 } |
| 437 |
| 438 @override |
| 439 bool shouldIgnoreUndefined(Identifier node) { |
| 440 Iterable<NamespaceCombinator> getShowCombinators( |
| 441 ImportElement importElement) => |
| 442 importElement.combinators.where((NamespaceCombinator combinator) => |
| 443 combinator is ShowElementCombinator); |
| 444 if (node is PrefixedIdentifier) { |
| 445 String prefix = node.prefix.name; |
| 446 String name = node.identifier.name; |
| 447 List<ImportElement> imports = _definingLibrary.imports; |
| 448 int count = imports.length; |
| 449 for (int i = 0; i < count; i++) { |
| 450 ImportElement importElement = imports[i]; |
| 451 if (importElement.prefix?.name == prefix && |
| 452 importElement.importedLibrary?.isSynthetic != false) { |
| 453 Iterable<NamespaceCombinator> showCombinators = |
| 454 getShowCombinators(importElement); |
| 455 if (showCombinators.isEmpty) { |
| 456 return true; |
| 457 } |
| 458 for (ShowElementCombinator combinator in showCombinators) { |
| 459 if (combinator.shownNames.contains(name)) { |
| 460 return true; |
| 461 } |
| 462 } |
| 463 } |
| 464 } |
| 465 } else if (node is SimpleIdentifier) { |
| 466 String name = node.name; |
| 467 List<ImportElement> imports = _definingLibrary.imports; |
| 468 int count = imports.length; |
| 469 for (int i = 0; i < count; i++) { |
| 470 ImportElement importElement = imports[i]; |
| 471 if (importElement.prefix == null && |
| 472 importElement.importedLibrary?.isSynthetic != false) { |
| 473 for (ShowElementCombinator combinator |
| 474 in getShowCombinators(importElement)) { |
| 475 if (combinator.shownNames.contains(name)) { |
| 476 return true; |
| 477 } |
| 478 } |
| 479 } |
| 480 } |
| 481 } |
| 482 return false; |
| 483 } |
| 484 |
| 485 /** |
| 486 * Create all of the namespaces associated with the libraries imported into |
| 487 * this library. The names are not added to this scope, but are stored for |
| 488 * later reference. |
| 489 */ |
| 490 void _createImportedNamespaces() { |
| 491 NamespaceBuilder builder = new NamespaceBuilder(); |
| 492 List<ImportElement> imports = _definingLibrary.imports; |
| 493 int count = imports.length; |
| 494 _importedNamespaces = new List<Namespace>(count); |
| 495 for (int i = 0; i < count; i++) { |
| 496 _importedNamespaces[i] = |
| 497 builder.createImportNamespaceForDirective(imports[i]); |
| 498 } |
| 499 } |
| 500 |
| 501 /** |
| 502 * Add the given [element] to this scope without checking for duplication or |
| 503 * hiding. |
| 504 */ |
| 505 void _definePrefixedNameWithoutChecking( |
| 506 String prefix, String name, Element element) { |
| 507 _definedPrefixedNames ??= new HashMap<String, Map<String, Element>>(); |
| 508 Map<String, Element> unprefixedNames = _definedPrefixedNames.putIfAbsent( |
| 509 prefix, () => new HashMap<String, Element>()); |
| 510 unprefixedNames[name] = element; |
| 511 } |
| 512 |
| 513 @override |
| 514 Element _internalLookupPrefixed(PrefixedIdentifier identifier, String prefix, |
| 515 String name, LibraryElement referencingLibrary) { |
| 516 Element element = _localPrefixedLookup(prefix, name); |
| 517 if (element != null) { |
| 518 return element; |
| 519 } |
| 520 element = _lookupInImportedNamespaces(identifier.identifier, |
| 521 (Namespace namespace) => namespace.getPrefixed(prefix, name)); |
| 522 if (element != null) { |
| 523 _definePrefixedNameWithoutChecking(prefix, name, element); |
| 524 } |
| 525 return element; |
| 526 } |
| 527 |
| 528 /** |
| 529 * Return the element with which the given [prefix] and [name] are associated, |
| 530 * or `null` if the name is not defined within this scope. |
| 531 */ |
| 532 Element _localPrefixedLookup(String prefix, String name) { |
| 533 if (_definedPrefixedNames != null) { |
| 534 Map<String, Element> unprefixedNames = _definedPrefixedNames[prefix]; |
| 535 if (unprefixedNames != null) { |
| 536 return unprefixedNames[name]; |
| 537 } |
| 538 } |
| 539 return null; |
| 540 } |
| 541 |
| 542 Element _lookupInImportedNamespaces( |
| 543 Identifier identifier, Element lookup(Namespace namespace)) { |
| 544 Set<Element> sdkElements = new HashSet<Element>(); |
| 545 Set<Element> nonSdkElements = new HashSet<Element>(); |
| 546 for (int i = 0; i < _importedNamespaces.length; i++) { |
| 547 Element element = lookup(_importedNamespaces[i]); |
| 548 if (element != null) { |
| 549 if (element.library.isInSdk) { |
| 550 sdkElements.add(element); |
| 551 } else { |
| 552 nonSdkElements.add(element); |
| 553 } |
| 554 } |
| 555 } |
| 556 int nonSdkCount = nonSdkElements.length; |
| 557 int sdkCount = sdkElements.length; |
| 558 if (nonSdkCount == 0) { |
| 559 if (sdkCount == 0) { |
| 560 return null; |
| 561 } else if (sdkCount == 1) { |
| 562 return sdkElements.first; |
| 563 } |
| 564 } |
| 565 if (nonSdkCount == 1) { |
| 566 if (sdkCount > 0) { |
| 567 identifier.setProperty( |
| 568 conflictingSdkElements, sdkElements.toList(growable: false)); |
| 569 } |
| 570 return nonSdkElements.first; |
| 571 } |
| 572 return new MultiplyDefinedElementImpl( |
| 573 _definingLibrary.context, |
| 574 sdkElements.toList(growable: false), |
| 575 nonSdkElements.toList(growable: false)); |
| 576 } |
| 577 } |
| 578 |
| 579 /** |
| 580 * A scope containing all of the names defined in a given library. |
| 581 */ |
| 582 class LibraryScope extends EnclosedScope { |
| 583 /** |
| 584 * Initialize a newly created scope representing the names defined in the |
| 585 * [definingLibrary]. |
| 586 */ |
| 587 LibraryScope(LibraryElement definingLibrary) |
| 588 : super(new LibraryImportScope(definingLibrary)) { |
| 589 _defineTopLevelNames(definingLibrary); |
| 590 } |
| 591 |
| 592 @override |
| 593 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { |
| 594 if (existing is PrefixElement) { |
| 595 // TODO(scheglov) consider providing actual 'nameOffset' from the |
| 596 // synthetic accessor |
| 597 int offset = duplicate.nameOffset; |
| 598 if (duplicate is PropertyAccessorElement) { |
| 599 PropertyAccessorElement accessor = duplicate; |
| 600 if (accessor.isSynthetic) { |
| 601 offset = accessor.variable.nameOffset; |
| 602 } |
| 603 } |
| 604 return new AnalysisError( |
| 605 duplicate.source, |
| 606 offset, |
| 607 duplicate.nameLength, |
| 608 CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER, |
| 609 [existing.displayName]); |
| 610 } |
| 611 return super.getErrorForDuplicate(existing, duplicate); |
| 612 } |
| 613 |
| 614 /** |
| 615 * Add to this scope all of the public top-level names that are defined in the |
| 616 * given [compilationUnit]. |
| 617 */ |
| 618 void _defineLocalNames(CompilationUnitElement compilationUnit) { |
| 619 for (PropertyAccessorElement element in compilationUnit.accessors) { |
| 620 define(element); |
| 621 } |
| 622 for (ClassElement element in compilationUnit.enums) { |
| 623 define(element); |
| 624 } |
| 625 for (FunctionElement element in compilationUnit.functions) { |
| 626 define(element); |
| 627 } |
| 628 for (FunctionTypeAliasElement element |
| 629 in compilationUnit.functionTypeAliases) { |
| 630 define(element); |
| 631 } |
| 632 for (ClassElement element in compilationUnit.types) { |
| 633 define(element); |
| 634 } |
| 635 } |
| 636 |
| 637 /** |
| 638 * Add to this scope all of the names that are explicitly defined in the |
| 639 * [definingLibrary]. |
| 640 */ |
| 641 void _defineTopLevelNames(LibraryElement definingLibrary) { |
| 642 for (PrefixElement prefix in definingLibrary.prefixes) { |
| 643 define(prefix); |
| 644 } |
| 645 _defineLocalNames(definingLibrary.definingCompilationUnit); |
| 646 for (CompilationUnitElement compilationUnit in definingLibrary.parts) { |
| 647 _defineLocalNames(compilationUnit); |
| 648 } |
| 649 } |
| 650 } |
| 651 |
| 652 /** |
| 653 * A mapping of identifiers to the elements represented by those identifiers. |
| 654 * Namespaces are the building blocks for scopes. |
| 655 */ |
| 656 class Namespace { |
| 657 /** |
| 658 * An empty namespace. |
| 659 */ |
| 660 static Namespace EMPTY = new Namespace(new HashMap<String, Element>()); |
| 661 |
| 662 /** |
| 663 * A table mapping names that are defined in this namespace to the element |
| 664 * representing the thing declared with that name. |
| 665 */ |
| 666 final HashMap<String, Element> _definedNames; |
| 667 |
| 668 /** |
| 669 * Initialize a newly created namespace to have the [_definedNames]. |
| 670 */ |
| 671 Namespace(this._definedNames); |
| 672 |
| 673 /** |
| 674 * Return a table containing the same mappings as those defined by this |
| 675 * namespace. |
| 676 */ |
| 677 Map<String, Element> get definedNames => _definedNames; |
| 678 |
| 679 /** |
| 680 * Return the element in this namespace that is available to the containing |
| 681 * scope using the given name, or `null` if there is no such element. |
| 682 */ |
| 683 Element get(String name) => _definedNames[name]; |
| 684 |
| 685 /** |
| 686 * Return the element in this namespace whose name is the result of combining |
| 687 * the [prefix] and the [name], separated by a period, or `null` if there is |
| 688 * no such element. |
| 689 */ |
| 690 Element getPrefixed(String prefix, String name) => null; |
| 691 } |
| 692 |
| 693 /** |
| 694 * The builder used to build a namespace. Namespace builders are thread-safe and |
| 695 * re-usable. |
| 696 */ |
| 697 class NamespaceBuilder { |
| 698 /** |
| 699 * Create a namespace representing the export namespace of the given [element]
. |
| 700 */ |
| 701 Namespace createExportNamespaceForDirective(ExportElement element) { |
| 702 LibraryElement exportedLibrary = element.exportedLibrary; |
| 703 if (exportedLibrary == null) { |
| 704 // |
| 705 // The exported library will be null if the URI does not reference a valid |
| 706 // library. |
| 707 // |
| 708 return Namespace.EMPTY; |
| 709 } |
| 710 HashMap<String, Element> exportedNames = _getExportMapping(exportedLibrary); |
| 711 exportedNames = _applyCombinators(exportedNames, element.combinators); |
| 712 return new Namespace(exportedNames); |
| 713 } |
| 714 |
| 715 /** |
| 716 * Create a namespace representing the export namespace of the given [library]
. |
| 717 */ |
| 718 Namespace createExportNamespaceForLibrary(LibraryElement library) { |
| 719 HashMap<String, Element> exportedNames = _getExportMapping(library); |
| 720 return new Namespace(exportedNames); |
| 721 } |
| 722 |
| 723 /** |
| 724 * Create a namespace representing the import namespace of the given [element]
. |
| 725 */ |
| 726 Namespace createImportNamespaceForDirective(ImportElement element) { |
| 727 LibraryElement importedLibrary = element.importedLibrary; |
| 728 if (importedLibrary == null) { |
| 729 // |
| 730 // The imported library will be null if the URI does not reference a valid |
| 731 // library. |
| 732 // |
| 733 return Namespace.EMPTY; |
| 734 } |
| 735 HashMap<String, Element> exportedNames = _getExportMapping(importedLibrary); |
| 736 exportedNames = _applyCombinators(exportedNames, element.combinators); |
| 737 PrefixElement prefix = element.prefix; |
| 738 if (prefix != null) { |
| 739 return new PrefixedNamespace(prefix.name, exportedNames); |
| 740 } |
| 741 return new Namespace(exportedNames); |
| 742 } |
| 743 |
| 744 /** |
| 745 * Create a namespace representing the public namespace of the given |
| 746 * [library]. |
| 747 */ |
| 748 Namespace createPublicNamespaceForLibrary(LibraryElement library) { |
| 749 HashMap<String, Element> definedNames = new HashMap<String, Element>(); |
| 750 _addPublicNames(definedNames, library.definingCompilationUnit); |
| 751 for (CompilationUnitElement compilationUnit in library.parts) { |
| 752 _addPublicNames(definedNames, compilationUnit); |
| 753 } |
| 754 return new Namespace(definedNames); |
| 755 } |
| 756 |
| 757 /** |
| 758 * Add all of the names in the given [namespace] to the table of |
| 759 * [definedNames]. |
| 760 */ |
| 761 void _addAllFromNamespace( |
| 762 Map<String, Element> definedNames, Namespace namespace) { |
| 763 if (namespace != null) { |
| 764 definedNames.addAll(namespace.definedNames); |
| 765 } |
| 766 } |
| 767 |
| 768 /** |
| 769 * Add the given [element] to the table of [definedNames] if it has a |
| 770 * publicly visible name. |
| 771 */ |
| 772 void _addIfPublic(Map<String, Element> definedNames, Element element) { |
| 773 String name = element.name; |
| 774 if (name != null && !Scope.isPrivateName(name)) { |
| 775 definedNames[name] = element; |
| 776 } |
| 777 } |
| 778 |
| 779 /** |
| 780 * Add to the table of [definedNames] all of the public top-level names that |
| 781 * are defined in the given [compilationUnit]. |
| 782 * namespace |
| 783 */ |
| 784 void _addPublicNames(Map<String, Element> definedNames, |
| 785 CompilationUnitElement compilationUnit) { |
| 786 for (PropertyAccessorElement element in compilationUnit.accessors) { |
| 787 _addIfPublic(definedNames, element); |
| 788 } |
| 789 for (ClassElement element in compilationUnit.enums) { |
| 790 _addIfPublic(definedNames, element); |
| 791 } |
| 792 for (FunctionElement element in compilationUnit.functions) { |
| 793 _addIfPublic(definedNames, element); |
| 794 } |
| 795 for (FunctionTypeAliasElement element |
| 796 in compilationUnit.functionTypeAliases) { |
| 797 _addIfPublic(definedNames, element); |
| 798 } |
| 799 for (ClassElement element in compilationUnit.types) { |
| 800 _addIfPublic(definedNames, element); |
| 801 } |
| 802 } |
| 803 |
| 804 /** |
| 805 * Apply the given [combinators] to all of the names in the given table of |
| 806 * [definedNames]. |
| 807 */ |
| 808 HashMap<String, Element> _applyCombinators( |
| 809 HashMap<String, Element> definedNames, |
| 810 List<NamespaceCombinator> combinators) { |
| 811 for (NamespaceCombinator combinator in combinators) { |
| 812 if (combinator is HideElementCombinator) { |
| 813 definedNames = _hide(definedNames, combinator.hiddenNames); |
| 814 } else if (combinator is ShowElementCombinator) { |
| 815 definedNames = _show(definedNames, combinator.shownNames); |
| 816 } else { |
| 817 // Internal error. |
| 818 AnalysisEngine.instance.logger |
| 819 .logError("Unknown type of combinator: ${combinator.runtimeType}"); |
| 820 } |
| 821 } |
| 822 return definedNames; |
| 823 } |
| 824 |
| 825 /** |
| 826 * Create a mapping table representing the export namespace of the given |
| 827 * [library]. The set of [visitedElements] contains the libraries that do not |
| 828 * need to be visited when processing the export directives of the given |
| 829 * library because all of the names defined by them will be added by another |
| 830 * library. |
| 831 */ |
| 832 HashMap<String, Element> _computeExportMapping( |
| 833 LibraryElement library, HashSet<LibraryElement> visitedElements) { |
| 834 visitedElements.add(library); |
| 835 try { |
| 836 HashMap<String, Element> definedNames = new HashMap<String, Element>(); |
| 837 for (ExportElement element in library.exports) { |
| 838 LibraryElement exportedLibrary = element.exportedLibrary; |
| 839 if (exportedLibrary != null && |
| 840 !visitedElements.contains(exportedLibrary)) { |
| 841 // |
| 842 // The exported library will be null if the URI does not reference a |
| 843 // valid library. |
| 844 // |
| 845 HashMap<String, Element> exportedNames = |
| 846 _computeExportMapping(exportedLibrary, visitedElements); |
| 847 exportedNames = _applyCombinators(exportedNames, element.combinators); |
| 848 definedNames.addAll(exportedNames); |
| 849 } |
| 850 } |
| 851 _addAllFromNamespace( |
| 852 definedNames, |
| 853 (library.context as InternalAnalysisContext) |
| 854 .getPublicNamespace(library)); |
| 855 return definedNames; |
| 856 } finally { |
| 857 visitedElements.remove(library); |
| 858 } |
| 859 } |
| 860 |
| 861 HashMap<String, Element> _getExportMapping(LibraryElement library) { |
| 862 if (library is LibraryElementImpl) { |
| 863 if (library.exportNamespace != null) { |
| 864 return library.exportNamespace.definedNames; |
| 865 } else { |
| 866 HashMap<String, Element> exportMapping = |
| 867 _computeExportMapping(library, new HashSet<LibraryElement>()); |
| 868 library.exportNamespace = new Namespace(exportMapping); |
| 869 return exportMapping; |
| 870 } |
| 871 } |
| 872 return _computeExportMapping(library, new HashSet<LibraryElement>()); |
| 873 } |
| 874 |
| 875 /** |
| 876 * Return a new map of names which has all the names from [definedNames] |
| 877 * with exception of [hiddenNames]. |
| 878 */ |
| 879 Map<String, Element> _hide( |
| 880 HashMap<String, Element> definedNames, List<String> hiddenNames) { |
| 881 HashMap<String, Element> newNames = |
| 882 new HashMap<String, Element>.from(definedNames); |
| 883 for (String name in hiddenNames) { |
| 884 newNames.remove(name); |
| 885 newNames.remove("$name="); |
| 886 } |
| 887 return newNames; |
| 888 } |
| 889 |
| 890 /** |
| 891 * Return a new map of names which has only [shownNames] from [definedNames]. |
| 892 */ |
| 893 HashMap<String, Element> _show( |
| 894 HashMap<String, Element> definedNames, List<String> shownNames) { |
| 895 HashMap<String, Element> newNames = new HashMap<String, Element>(); |
| 896 for (String name in shownNames) { |
| 897 Element element = definedNames[name]; |
| 898 if (element != null) { |
| 899 newNames[name] = element; |
| 900 } |
| 901 String setterName = "$name="; |
| 902 element = definedNames[setterName]; |
| 903 if (element != null) { |
| 904 newNames[setterName] = element; |
| 905 } |
| 906 } |
| 907 return newNames; |
| 908 } |
| 909 } |
| 910 |
| 911 /** |
| 912 * A mapping of identifiers to the elements represented by those identifiers. |
| 913 * Namespaces are the building blocks for scopes. |
| 914 */ |
| 915 class PrefixedNamespace implements Namespace { |
| 916 /** |
| 917 * The prefix that is prepended to each of the defined names. |
| 918 */ |
| 919 final String _prefix; |
| 920 |
| 921 /** |
| 922 * The length of the prefix. |
| 923 */ |
| 924 final int _length; |
| 925 |
| 926 /** |
| 927 * A table mapping names that are defined in this namespace to the element |
| 928 * representing the thing declared with that name. |
| 929 */ |
| 930 final HashMap<String, Element> _definedNames; |
| 931 |
| 932 /** |
| 933 * Initialize a newly created namespace to have the names resulting from |
| 934 * prefixing each of the [_definedNames] with the given [_prefix] (and a |
| 935 * period). |
| 936 */ |
| 937 PrefixedNamespace(String prefix, this._definedNames) |
| 938 : _prefix = prefix, |
| 939 _length = prefix.length; |
| 940 |
| 941 @override |
| 942 Map<String, Element> get definedNames { |
| 943 Map<String, Element> definedNames = <String, Element>{}; |
| 944 _definedNames.forEach((String name, Element element) { |
| 945 definedNames["$_prefix.$name"] = element; |
| 946 }); |
| 947 return definedNames; |
| 948 } |
| 949 |
| 950 @override |
| 951 Element get(String name) { |
| 952 if (name.length > _length && name.startsWith(_prefix)) { |
| 953 if (name.codeUnitAt(_length) == '.'.codeUnitAt(0)) { |
| 954 return _definedNames[name.substring(_length + 1)]; |
| 955 } |
| 956 } |
| 957 return null; |
| 958 } |
| 959 |
| 960 @override |
| 961 Element getPrefixed(String prefix, String name) { |
| 962 if (prefix == _prefix) { |
| 963 return _definedNames[name]; |
| 964 } |
| 965 return null; |
| 966 } |
| 967 } |
| 968 |
| 969 /** |
| 970 * A name scope used by the resolver to determine which names are visible at any |
| 971 * given point in the code. |
| 972 */ |
| 973 abstract class Scope { |
| 974 /** |
| 975 * The prefix used to mark an identifier as being private to its library. |
| 976 */ |
| 977 static int PRIVATE_NAME_PREFIX = 0x5F; |
| 978 |
| 979 /** |
| 980 * The suffix added to the declared name of a setter when looking up the |
| 981 * setter. Used to disambiguate between a getter and a setter that have the |
| 982 * same name. |
| 983 */ |
| 984 static String SETTER_SUFFIX = "="; |
| 985 |
| 986 /** |
| 987 * The name used to look up the method used to implement the unary minus |
| 988 * operator. Used to disambiguate between the unary and binary operators. |
| 989 */ |
| 990 static String UNARY_MINUS = "unary-"; |
| 991 |
| 992 /** |
| 993 * A table mapping names that are defined in this scope to the element |
| 994 * representing the thing declared with that name. |
| 995 */ |
| 996 HashMap<String, Element> _definedNames = null; |
| 997 |
| 998 /** |
| 999 * Return the scope in which this scope is lexically enclosed. |
| 1000 */ |
| 1001 Scope get enclosingScope => null; |
| 1002 |
| 1003 /** |
| 1004 * Add the given [element] to this scope. If there is already an element with |
| 1005 * the given name defined in this scope, then the original element will |
| 1006 * continue to be mapped to the name. |
| 1007 */ |
| 1008 void define(Element element) { |
| 1009 String name = _getName(element); |
| 1010 if (name != null && !name.isEmpty) { |
| 1011 _definedNames ??= new HashMap<String, Element>(); |
| 1012 _definedNames.putIfAbsent(name, () => element); |
| 1013 } |
| 1014 } |
| 1015 |
| 1016 /** |
| 1017 * Add the given [element] to this scope without checking for duplication or |
| 1018 * hiding. |
| 1019 */ |
| 1020 void defineNameWithoutChecking(String name, Element element) { |
| 1021 _definedNames ??= new HashMap<String, Element>(); |
| 1022 _definedNames[name] = element; |
| 1023 } |
| 1024 |
| 1025 /** |
| 1026 * Add the given [element] to this scope without checking for duplication or |
| 1027 * hiding. |
| 1028 */ |
| 1029 void defineWithoutChecking(Element element) { |
| 1030 _definedNames ??= new HashMap<String, Element>(); |
| 1031 _definedNames[_getName(element)] = element; |
| 1032 } |
| 1033 |
| 1034 /** |
| 1035 * Return the error code to be used when reporting that a name being defined |
| 1036 * locally conflicts with another element of the same name in the local scope. |
| 1037 * [existing] is the first element to be declared with the conflicting name, |
| 1038 * while [duplicate] another element declared with the conflicting name. |
| 1039 */ |
| 1040 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { |
| 1041 // TODO(brianwilkerson) Customize the error message based on the types of |
| 1042 // elements that share the same name. |
| 1043 // TODO(jwren) There are 4 error codes for duplicate, but only 1 is being |
| 1044 // generated. |
| 1045 Source source = duplicate.source; |
| 1046 return new AnalysisError(source, duplicate.nameOffset, duplicate.nameLength, |
| 1047 CompileTimeErrorCode.DUPLICATE_DEFINITION, [existing.displayName]); |
| 1048 } |
| 1049 |
| 1050 /** |
| 1051 * Return the source that contains the given [identifier], or the source |
| 1052 * associated with this scope if the source containing the identifier could |
| 1053 * not be determined. |
| 1054 */ |
| 1055 Source getSource(AstNode identifier) { |
| 1056 CompilationUnit unit = |
| 1057 identifier.getAncestor((node) => node is CompilationUnit); |
| 1058 if (unit != null) { |
| 1059 CompilationUnitElement unitElement = unit.element; |
| 1060 if (unitElement != null) { |
| 1061 return unitElement.source; |
| 1062 } |
| 1063 } |
| 1064 return null; |
| 1065 } |
| 1066 |
| 1067 /** |
| 1068 * Return the element with which the given [name] is associated, or `null` if |
| 1069 * the name is not defined within this scope. The [identifier] is the |
| 1070 * identifier node to lookup element for, used to report correct kind of a |
| 1071 * problem and associate problem with. The [referencingLibrary] is the library |
| 1072 * that contains the reference to the name, used to implement library-level |
| 1073 * privacy. |
| 1074 */ |
| 1075 Element internalLookup( |
| 1076 Identifier identifier, String name, LibraryElement referencingLibrary); |
| 1077 |
| 1078 /** |
| 1079 * Return the element with which the given [name] is associated, or `null` if |
| 1080 * the name is not defined within this scope. This method only returns |
| 1081 * elements that are directly defined within this scope, not elements that are |
| 1082 * defined in an enclosing scope. The [referencingLibrary] is the library that |
| 1083 * contains the reference to the name, used to implement library-level privacy
. |
| 1084 */ |
| 1085 Element localLookup(String name, LibraryElement referencingLibrary) { |
| 1086 if (_definedNames != null) { |
| 1087 return _definedNames[name]; |
| 1088 } |
| 1089 return null; |
| 1090 } |
| 1091 |
| 1092 /** |
| 1093 * Return the element with which the given [identifier] is associated, or |
| 1094 * `null` if the name is not defined within this scope. The |
| 1095 * [referencingLibrary] is the library that contains the reference to the |
| 1096 * name, used to implement library-level privacy. |
| 1097 */ |
| 1098 Element lookup(Identifier identifier, LibraryElement referencingLibrary) { |
| 1099 if (identifier is PrefixedIdentifier) { |
| 1100 return _internalLookupPrefixed(identifier, identifier.prefix.name, |
| 1101 identifier.identifier.name, referencingLibrary); |
| 1102 } |
| 1103 return internalLookup(identifier, identifier.name, referencingLibrary); |
| 1104 } |
| 1105 |
| 1106 /** |
| 1107 * Return `true` if the fact that the given [node] is not defined should be |
| 1108 * ignored (from the perspective of error reporting). This will be the case if |
| 1109 * there is at least one import that defines the node's prefix, and if that |
| 1110 * import either has no show combinators or has a show combinator that |
| 1111 * explicitly lists the node's name. |
| 1112 */ |
| 1113 bool shouldIgnoreUndefined(Identifier node) { |
| 1114 if (enclosingScope != null) { |
| 1115 return enclosingScope.shouldIgnoreUndefined(node); |
| 1116 } |
| 1117 return false; |
| 1118 } |
| 1119 |
| 1120 /** |
| 1121 * Return the name that will be used to look up the given [element]. |
| 1122 */ |
| 1123 String _getName(Element element) { |
| 1124 if (element is MethodElement) { |
| 1125 MethodElement method = element; |
| 1126 if (method.name == "-" && method.parameters.length == 0) { |
| 1127 return UNARY_MINUS; |
| 1128 } |
| 1129 } |
| 1130 return element.name; |
| 1131 } |
| 1132 |
| 1133 /** |
| 1134 * Return the element with which the given [prefix] and [name] are associated, |
| 1135 * or `null` if the name is not defined within this scope. The [identifier] is |
| 1136 * the identifier node to lookup element for, used to report correct kind of a |
| 1137 * problem and associate problem with. The [referencingLibrary] is the library |
| 1138 * that contains the reference to the name, used to implement library-level |
| 1139 * privacy. |
| 1140 */ |
| 1141 Element _internalLookupPrefixed(PrefixedIdentifier identifier, String prefix, |
| 1142 String name, LibraryElement referencingLibrary); |
| 1143 |
| 1144 /** |
| 1145 * Return `true` if the given [name] is a library-private name. |
| 1146 */ |
| 1147 static bool isPrivateName(String name) => |
| 1148 name != null && StringUtilities.startsWithChar(name, PRIVATE_NAME_PREFIX); |
| 1149 } |
| 1150 |
| 1151 /** |
| 1152 * The scope defined by the type parameters in a class. |
| 1153 */ |
| 1154 class TypeParameterScope extends EnclosedScope { |
| 1155 /** |
| 1156 * Initialize a newly created scope, enclosed within the [enclosingScope], |
| 1157 * that defined the type parameters from the given [classElement]. |
| 1158 */ |
| 1159 TypeParameterScope(Scope enclosingScope, ClassElement classElement) |
| 1160 : super(enclosingScope) { |
| 1161 if (classElement == null) { |
| 1162 throw new ArgumentError("class element cannot be null"); |
| 1163 } |
| 1164 _defineTypeParameters(classElement); |
| 1165 } |
| 1166 |
| 1167 /** |
| 1168 * Define the type parameters declared by the [classElement]. |
| 1169 */ |
| 1170 void _defineTypeParameters(ClassElement classElement) { |
| 1171 for (TypeParameterElement typeParameter in classElement.typeParameters) { |
| 1172 define(typeParameter); |
| 1173 } |
| 1174 } |
| 1175 } |
| OLD | NEW |