OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library engine.resolver; | |
6 | |
7 import 'dart:collection'; | |
8 | |
9 import 'ast.dart'; | |
10 import 'constant.dart'; | |
11 import 'element.dart'; | |
12 import 'element_resolver.dart'; | |
13 import 'engine.dart'; | |
14 import 'error.dart'; | |
15 import 'error_verifier.dart'; | |
16 import 'html.dart' as ht; | |
17 import 'java_core.dart'; | |
18 import 'java_engine.dart'; | |
19 import 'scanner.dart' as sc; | |
20 import 'sdk.dart' show DartSdk, SdkLibrary; | |
21 import 'source.dart'; | |
22 import 'static_type_analyzer.dart'; | |
23 import 'utilities_dart.dart'; | |
24 | |
25 /** | |
26 * Callback signature used by ImplicitConstructorBuilder to register | |
27 * computations to be performed, and their dependencies. A call to this | |
28 * callback indicates that [computation] may be used to compute implicit | |
29 * constructors for [classElement], but that the computation may not be invoked | |
30 * until after implicit constructors have been built for [superclassElement]. | |
31 */ | |
32 typedef void ImplicitConstructorBuilderCallback(ClassElement classElement, | |
33 ClassElement superclassElement, void computation()); | |
34 | |
35 typedef LibraryResolver LibraryResolverFactory(AnalysisContext context); | |
36 | |
37 typedef ResolverVisitor ResolverVisitorFactory( | |
38 Library library, Source source, TypeProvider typeProvider); | |
39 | |
40 typedef StaticTypeAnalyzer StaticTypeAnalyzerFactory(ResolverVisitor visitor); | |
41 | |
42 typedef TypeResolverVisitor TypeResolverVisitorFactory( | |
43 Library library, Source source, TypeProvider typeProvider); | |
44 | |
45 typedef void VoidFunction(); | |
46 | |
47 /** | |
48 * Instances of the class `BestPracticesVerifier` traverse an AST structure look
ing for | |
49 * violations of Dart best practices. | |
50 */ | |
51 class BestPracticesVerifier extends RecursiveAstVisitor<Object> { | |
52 // static String _HASHCODE_GETTER_NAME = "hashCode"; | |
53 | |
54 static String _NULL_TYPE_NAME = "Null"; | |
55 | |
56 static String _TO_INT_METHOD_NAME = "toInt"; | |
57 | |
58 /** | |
59 * The class containing the AST nodes being visited, or `null` if we are not i
n the scope of | |
60 * a class. | |
61 */ | |
62 ClassElement _enclosingClass; | |
63 | |
64 /** | |
65 * The error reporter by which errors will be reported. | |
66 */ | |
67 final ErrorReporter _errorReporter; | |
68 | |
69 /** | |
70 * The type Future<Null>, which is needed for determining whether it is safe | |
71 * to have a bare "return;" in an async method. | |
72 */ | |
73 final InterfaceType _futureNullType; | |
74 | |
75 /** | |
76 * Create a new instance of the [BestPracticesVerifier]. | |
77 * | |
78 * @param errorReporter the error reporter | |
79 */ | |
80 BestPracticesVerifier(this._errorReporter, TypeProvider typeProvider) | |
81 : _futureNullType = typeProvider.futureNullType; | |
82 | |
83 @override | |
84 Object visitArgumentList(ArgumentList node) { | |
85 _checkForArgumentTypesNotAssignableInList(node); | |
86 return super.visitArgumentList(node); | |
87 } | |
88 | |
89 @override | |
90 Object visitAsExpression(AsExpression node) { | |
91 _checkForUnnecessaryCast(node); | |
92 return super.visitAsExpression(node); | |
93 } | |
94 | |
95 @override | |
96 Object visitAssignmentExpression(AssignmentExpression node) { | |
97 sc.TokenType operatorType = node.operator.type; | |
98 if (operatorType == sc.TokenType.EQ) { | |
99 _checkForUseOfVoidResult(node.rightHandSide); | |
100 _checkForInvalidAssignment(node.leftHandSide, node.rightHandSide); | |
101 } else { | |
102 _checkForDeprecatedMemberUse(node.bestElement, node); | |
103 } | |
104 return super.visitAssignmentExpression(node); | |
105 } | |
106 | |
107 @override | |
108 Object visitBinaryExpression(BinaryExpression node) { | |
109 _checkForDivisionOptimizationHint(node); | |
110 _checkForDeprecatedMemberUse(node.bestElement, node); | |
111 return super.visitBinaryExpression(node); | |
112 } | |
113 | |
114 @override | |
115 Object visitClassDeclaration(ClassDeclaration node) { | |
116 ClassElement outerClass = _enclosingClass; | |
117 try { | |
118 _enclosingClass = node.element; | |
119 // Commented out until we decide that we want this hint in the analyzer | |
120 // checkForOverrideEqualsButNotHashCode(node); | |
121 return super.visitClassDeclaration(node); | |
122 } finally { | |
123 _enclosingClass = outerClass; | |
124 } | |
125 } | |
126 | |
127 @override | |
128 Object visitExportDirective(ExportDirective node) { | |
129 _checkForDeprecatedMemberUse(node.uriElement, node); | |
130 return super.visitExportDirective(node); | |
131 } | |
132 | |
133 @override | |
134 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
135 _checkForMissingReturn(node.returnType, node.functionExpression.body); | |
136 return super.visitFunctionDeclaration(node); | |
137 } | |
138 | |
139 @override | |
140 Object visitImportDirective(ImportDirective node) { | |
141 _checkForDeprecatedMemberUse(node.uriElement, node); | |
142 ImportElement importElement = node.element; | |
143 if (importElement != null) { | |
144 if (importElement.isDeferred) { | |
145 _checkForLoadLibraryFunction(node, importElement); | |
146 } | |
147 } | |
148 return super.visitImportDirective(node); | |
149 } | |
150 | |
151 @override | |
152 Object visitIndexExpression(IndexExpression node) { | |
153 _checkForDeprecatedMemberUse(node.bestElement, node); | |
154 return super.visitIndexExpression(node); | |
155 } | |
156 | |
157 @override | |
158 Object visitInstanceCreationExpression(InstanceCreationExpression node) { | |
159 _checkForDeprecatedMemberUse(node.staticElement, node); | |
160 return super.visitInstanceCreationExpression(node); | |
161 } | |
162 | |
163 @override | |
164 Object visitIsExpression(IsExpression node) { | |
165 _checkAllTypeChecks(node); | |
166 return super.visitIsExpression(node); | |
167 } | |
168 | |
169 @override | |
170 Object visitMethodDeclaration(MethodDeclaration node) { | |
171 // This was determined to not be a good hint, see: dartbug.com/16029 | |
172 //checkForOverridingPrivateMember(node); | |
173 _checkForMissingReturn(node.returnType, node.body); | |
174 return super.visitMethodDeclaration(node); | |
175 } | |
176 | |
177 @override | |
178 Object visitPostfixExpression(PostfixExpression node) { | |
179 _checkForDeprecatedMemberUse(node.bestElement, node); | |
180 return super.visitPostfixExpression(node); | |
181 } | |
182 | |
183 @override | |
184 Object visitPrefixExpression(PrefixExpression node) { | |
185 _checkForDeprecatedMemberUse(node.bestElement, node); | |
186 return super.visitPrefixExpression(node); | |
187 } | |
188 | |
189 @override | |
190 Object visitRedirectingConstructorInvocation( | |
191 RedirectingConstructorInvocation node) { | |
192 _checkForDeprecatedMemberUse(node.staticElement, node); | |
193 return super.visitRedirectingConstructorInvocation(node); | |
194 } | |
195 | |
196 @override | |
197 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
198 _checkForDeprecatedMemberUseAtIdentifier(node); | |
199 return super.visitSimpleIdentifier(node); | |
200 } | |
201 | |
202 @override | |
203 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
204 _checkForDeprecatedMemberUse(node.staticElement, node); | |
205 return super.visitSuperConstructorInvocation(node); | |
206 } | |
207 | |
208 @override | |
209 Object visitVariableDeclaration(VariableDeclaration node) { | |
210 _checkForUseOfVoidResult(node.initializer); | |
211 _checkForInvalidAssignment(node.name, node.initializer); | |
212 return super.visitVariableDeclaration(node); | |
213 } | |
214 | |
215 /** | |
216 * Check for the passed is expression for the unnecessary type check hint code
s as well as null | |
217 * checks expressed using an is expression. | |
218 * | |
219 * @param node the is expression to check | |
220 * @return `true` if and only if a hint code is generated on the passed node | |
221 * See [HintCode.TYPE_CHECK_IS_NOT_NULL], [HintCode.TYPE_CHECK_IS_NULL], | |
222 * [HintCode.UNNECESSARY_TYPE_CHECK_TRUE], and | |
223 * [HintCode.UNNECESSARY_TYPE_CHECK_FALSE]. | |
224 */ | |
225 bool _checkAllTypeChecks(IsExpression node) { | |
226 Expression expression = node.expression; | |
227 TypeName typeName = node.type; | |
228 DartType lhsType = expression.staticType; | |
229 DartType rhsType = typeName.type; | |
230 if (lhsType == null || rhsType == null) { | |
231 return false; | |
232 } | |
233 String rhsNameStr = typeName.name.name; | |
234 // if x is dynamic | |
235 if (rhsType.isDynamic && rhsNameStr == sc.Keyword.DYNAMIC.syntax) { | |
236 if (node.notOperator == null) { | |
237 // the is case | |
238 _errorReporter.reportErrorForNode( | |
239 HintCode.UNNECESSARY_TYPE_CHECK_TRUE, node); | |
240 } else { | |
241 // the is not case | |
242 _errorReporter.reportErrorForNode( | |
243 HintCode.UNNECESSARY_TYPE_CHECK_FALSE, node); | |
244 } | |
245 return true; | |
246 } | |
247 Element rhsElement = rhsType.element; | |
248 LibraryElement libraryElement = | |
249 rhsElement != null ? rhsElement.library : null; | |
250 if (libraryElement != null && libraryElement.isDartCore) { | |
251 // if x is Object or null is Null | |
252 if (rhsType.isObject || | |
253 (expression is NullLiteral && rhsNameStr == _NULL_TYPE_NAME)) { | |
254 if (node.notOperator == null) { | |
255 // the is case | |
256 _errorReporter.reportErrorForNode( | |
257 HintCode.UNNECESSARY_TYPE_CHECK_TRUE, node); | |
258 } else { | |
259 // the is not case | |
260 _errorReporter.reportErrorForNode( | |
261 HintCode.UNNECESSARY_TYPE_CHECK_FALSE, node); | |
262 } | |
263 return true; | |
264 } else if (rhsNameStr == _NULL_TYPE_NAME) { | |
265 if (node.notOperator == null) { | |
266 // the is case | |
267 _errorReporter.reportErrorForNode(HintCode.TYPE_CHECK_IS_NULL, node); | |
268 } else { | |
269 // the is not case | |
270 _errorReporter.reportErrorForNode( | |
271 HintCode.TYPE_CHECK_IS_NOT_NULL, node); | |
272 } | |
273 return true; | |
274 } | |
275 } | |
276 return false; | |
277 } | |
278 | |
279 /** | |
280 * This verifies that the passed expression can be assigned to its correspondi
ng parameters. | |
281 * | |
282 * This method corresponds to ErrorVerifier.checkForArgumentTypeNotAssignable. | |
283 * | |
284 * TODO (jwren) In the ErrorVerifier there are other warnings that we could ha
ve a corresponding | |
285 * hint for: see other callers of ErrorVerifier.checkForArgumentTypeNotAssigna
ble(..). | |
286 * | |
287 * @param expression the expression to evaluate | |
288 * @param expectedStaticType the expected static type of the parameter | |
289 * @param actualStaticType the actual static type of the argument | |
290 * @param expectedPropagatedType the expected propagated type of the parameter
, may be | |
291 * `null` | |
292 * @param actualPropagatedType the expected propagated type of the parameter,
may be `null` | |
293 * @return `true` if and only if an hint code is generated on the passed node | |
294 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. | |
295 */ | |
296 bool _checkForArgumentTypeNotAssignable(Expression expression, | |
297 DartType expectedStaticType, DartType actualStaticType, | |
298 DartType expectedPropagatedType, DartType actualPropagatedType, | |
299 ErrorCode hintCode) { | |
300 // | |
301 // Warning case: test static type information | |
302 // | |
303 if (actualStaticType != null && expectedStaticType != null) { | |
304 if (!actualStaticType.isAssignableTo(expectedStaticType)) { | |
305 // A warning was created in the ErrorVerifier, return false, don't | |
306 // create a hint when a warning has already been created. | |
307 return false; | |
308 } | |
309 } | |
310 // | |
311 // Hint case: test propagated type information | |
312 // | |
313 // Compute the best types to use. | |
314 DartType expectedBestType = expectedPropagatedType != null | |
315 ? expectedPropagatedType | |
316 : expectedStaticType; | |
317 DartType actualBestType = | |
318 actualPropagatedType != null ? actualPropagatedType : actualStaticType; | |
319 if (actualBestType != null && expectedBestType != null) { | |
320 if (!actualBestType.isAssignableTo(expectedBestType)) { | |
321 _errorReporter.reportTypeErrorForNode( | |
322 hintCode, expression, [actualBestType, expectedBestType]); | |
323 return true; | |
324 } | |
325 } | |
326 return false; | |
327 } | |
328 | |
329 /** | |
330 * This verifies that the passed argument can be assigned to its corresponding
parameter. | |
331 * | |
332 * This method corresponds to ErrorCode.checkForArgumentTypeNotAssignableForAr
gument. | |
333 * | |
334 * @param argument the argument to evaluate | |
335 * @return `true` if and only if an hint code is generated on the passed node | |
336 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. | |
337 */ | |
338 bool _checkForArgumentTypeNotAssignableForArgument(Expression argument) { | |
339 if (argument == null) { | |
340 return false; | |
341 } | |
342 ParameterElement staticParameterElement = argument.staticParameterElement; | |
343 DartType staticParameterType = | |
344 staticParameterElement == null ? null : staticParameterElement.type; | |
345 ParameterElement propagatedParameterElement = | |
346 argument.propagatedParameterElement; | |
347 DartType propagatedParameterType = propagatedParameterElement == null | |
348 ? null | |
349 : propagatedParameterElement.type; | |
350 return _checkForArgumentTypeNotAssignableWithExpectedTypes(argument, | |
351 staticParameterType, propagatedParameterType, | |
352 HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); | |
353 } | |
354 | |
355 /** | |
356 * This verifies that the passed expression can be assigned to its correspondi
ng parameters. | |
357 * | |
358 * This method corresponds to ErrorCode.checkForArgumentTypeNotAssignableWithE
xpectedTypes. | |
359 * | |
360 * @param expression the expression to evaluate | |
361 * @param expectedStaticType the expected static type | |
362 * @param expectedPropagatedType the expected propagated type, may be `null` | |
363 * @return `true` if and only if an hint code is generated on the passed node | |
364 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. | |
365 */ | |
366 bool _checkForArgumentTypeNotAssignableWithExpectedTypes( | |
367 Expression expression, DartType expectedStaticType, | |
368 DartType expectedPropagatedType, ErrorCode errorCode) => | |
369 _checkForArgumentTypeNotAssignable(expression, expectedStaticType, | |
370 expression.staticType, expectedPropagatedType, | |
371 expression.propagatedType, errorCode); | |
372 | |
373 /** | |
374 * This verifies that the passed arguments can be assigned to their correspond
ing parameters. | |
375 * | |
376 * This method corresponds to ErrorCode.checkForArgumentTypesNotAssignableInLi
st. | |
377 * | |
378 * @param node the arguments to evaluate | |
379 * @return `true` if and only if an hint code is generated on the passed node | |
380 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. | |
381 */ | |
382 bool _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) { | |
383 if (argumentList == null) { | |
384 return false; | |
385 } | |
386 bool problemReported = false; | |
387 for (Expression argument in argumentList.arguments) { | |
388 if (_checkForArgumentTypeNotAssignableForArgument(argument)) { | |
389 problemReported = true; | |
390 } | |
391 } | |
392 return problemReported; | |
393 } | |
394 | |
395 /** | |
396 * Given some [Element], look at the associated metadata and report the use of
the member if | |
397 * it is declared as deprecated. | |
398 * | |
399 * @param element some element to check for deprecated use of | |
400 * @param node the node use for the location of the error | |
401 * @return `true` if and only if a hint code is generated on the passed node | |
402 * See [HintCode.DEPRECATED_MEMBER_USE]. | |
403 */ | |
404 bool _checkForDeprecatedMemberUse(Element element, AstNode node) { | |
405 if (element != null && element.isDeprecated) { | |
406 String displayName = element.displayName; | |
407 if (element is ConstructorElement) { | |
408 // TODO(jwren) We should modify ConstructorElement.getDisplayName(), | |
409 // or have the logic centralized elsewhere, instead of doing this logic | |
410 // here. | |
411 ConstructorElement constructorElement = element; | |
412 displayName = constructorElement.enclosingElement.displayName; | |
413 if (!constructorElement.displayName.isEmpty) { | |
414 displayName = "$displayName.${constructorElement.displayName}"; | |
415 } | |
416 } | |
417 _errorReporter.reportErrorForNode( | |
418 HintCode.DEPRECATED_MEMBER_USE, node, [displayName]); | |
419 return true; | |
420 } | |
421 return false; | |
422 } | |
423 | |
424 /** | |
425 * For [SimpleIdentifier]s, only call [checkForDeprecatedMemberUse] | |
426 * if the node is not in a declaration context. | |
427 * | |
428 * Also, if the identifier is a constructor name in a constructor invocation,
then calls to the | |
429 * deprecated constructor will be caught by | |
430 * [visitInstanceCreationExpression] and | |
431 * [visitSuperConstructorInvocation], and can be ignored by | |
432 * this visit method. | |
433 * | |
434 * @param identifier some simple identifier to check for deprecated use of | |
435 * @return `true` if and only if a hint code is generated on the passed node | |
436 * See [HintCode.DEPRECATED_MEMBER_USE]. | |
437 */ | |
438 bool _checkForDeprecatedMemberUseAtIdentifier(SimpleIdentifier identifier) { | |
439 if (identifier.inDeclarationContext()) { | |
440 return false; | |
441 } | |
442 AstNode parent = identifier.parent; | |
443 if ((parent is ConstructorName && identical(identifier, parent.name)) || | |
444 (parent is SuperConstructorInvocation && | |
445 identical(identifier, parent.constructorName)) || | |
446 parent is HideCombinator) { | |
447 return false; | |
448 } | |
449 return _checkForDeprecatedMemberUse(identifier.bestElement, identifier); | |
450 } | |
451 | |
452 /** | |
453 * Check for the passed binary expression for the [HintCode.DIVISION_OPTIMIZAT
ION]. | |
454 * | |
455 * @param node the binary expression to check | |
456 * @return `true` if and only if a hint code is generated on the passed node | |
457 * See [HintCode.DIVISION_OPTIMIZATION]. | |
458 */ | |
459 bool _checkForDivisionOptimizationHint(BinaryExpression node) { | |
460 // Return if the operator is not '/' | |
461 if (node.operator.type != sc.TokenType.SLASH) { | |
462 return false; | |
463 } | |
464 // Return if the '/' operator is not defined in core, or if we don't know | |
465 // its static or propagated type | |
466 MethodElement methodElement = node.bestElement; | |
467 if (methodElement == null) { | |
468 return false; | |
469 } | |
470 LibraryElement libraryElement = methodElement.library; | |
471 if (libraryElement != null && !libraryElement.isDartCore) { | |
472 return false; | |
473 } | |
474 // Report error if the (x/y) has toInt() invoked on it | |
475 if (node.parent is ParenthesizedExpression) { | |
476 ParenthesizedExpression parenthesizedExpression = | |
477 _wrapParenthesizedExpression(node.parent as ParenthesizedExpression); | |
478 if (parenthesizedExpression.parent is MethodInvocation) { | |
479 MethodInvocation methodInvocation = | |
480 parenthesizedExpression.parent as MethodInvocation; | |
481 if (_TO_INT_METHOD_NAME == methodInvocation.methodName.name && | |
482 methodInvocation.argumentList.arguments.isEmpty) { | |
483 _errorReporter.reportErrorForNode( | |
484 HintCode.DIVISION_OPTIMIZATION, methodInvocation); | |
485 return true; | |
486 } | |
487 } | |
488 } | |
489 return false; | |
490 } | |
491 | |
492 /** | |
493 * This verifies that the passed left hand side and right hand side represent
a valid assignment. | |
494 * | |
495 * This method corresponds to ErrorVerifier.checkForInvalidAssignment. | |
496 * | |
497 * @param lhs the left hand side expression | |
498 * @param rhs the right hand side expression | |
499 * @return `true` if and only if an error code is generated on the passed node | |
500 * See [HintCode.INVALID_ASSIGNMENT]. | |
501 */ | |
502 bool _checkForInvalidAssignment(Expression lhs, Expression rhs) { | |
503 if (lhs == null || rhs == null) { | |
504 return false; | |
505 } | |
506 VariableElement leftVariableElement = ErrorVerifier.getVariableElement(lhs); | |
507 DartType leftType = (leftVariableElement == null) | |
508 ? ErrorVerifier.getStaticType(lhs) | |
509 : leftVariableElement.type; | |
510 DartType staticRightType = ErrorVerifier.getStaticType(rhs); | |
511 if (!staticRightType.isAssignableTo(leftType)) { | |
512 // The warning was generated on this rhs | |
513 return false; | |
514 } | |
515 // Test for, and then generate the hint | |
516 DartType bestRightType = rhs.bestType; | |
517 if (leftType != null && bestRightType != null) { | |
518 if (!bestRightType.isAssignableTo(leftType)) { | |
519 _errorReporter.reportTypeErrorForNode( | |
520 HintCode.INVALID_ASSIGNMENT, rhs, [bestRightType, leftType]); | |
521 return true; | |
522 } | |
523 } | |
524 return false; | |
525 } | |
526 | |
527 /** | |
528 * Check that the imported library does not define a loadLibrary function. The
import has already | |
529 * been determined to be deferred when this is called. | |
530 * | |
531 * @param node the import directive to evaluate | |
532 * @param importElement the [ImportElement] retrieved from the node | |
533 * @return `true` if and only if an error code is generated on the passed node | |
534 * See [CompileTimeErrorCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION]. | |
535 */ | |
536 bool _checkForLoadLibraryFunction( | |
537 ImportDirective node, ImportElement importElement) { | |
538 LibraryElement importedLibrary = importElement.importedLibrary; | |
539 if (importedLibrary == null) { | |
540 return false; | |
541 } | |
542 if (importedLibrary.hasLoadLibraryFunction) { | |
543 _errorReporter.reportErrorForNode( | |
544 HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION, node, | |
545 [importedLibrary.name]); | |
546 return true; | |
547 } | |
548 return false; | |
549 } | |
550 | |
551 /** | |
552 * Generate a hint for functions or methods that have a return type, but do no
t have a return | |
553 * statement on all branches. At the end of blocks with no return, Dart implic
itly returns | |
554 * `null`, avoiding these implicit returns is considered a best practice. | |
555 * | |
556 * Note: for async functions/methods, this hint only applies when the | |
557 * function has a return type that Future<Null> is not assignable to. | |
558 * | |
559 * @param node the binary expression to check | |
560 * @param body the function body | |
561 * @return `true` if and only if a hint code is generated on the passed node | |
562 * See [HintCode.MISSING_RETURN]. | |
563 */ | |
564 bool _checkForMissingReturn(TypeName returnType, FunctionBody body) { | |
565 // Check that the method or function has a return type, and a function body | |
566 if (returnType == null || body == null) { | |
567 return false; | |
568 } | |
569 // Check that the body is a BlockFunctionBody | |
570 if (body is! BlockFunctionBody) { | |
571 return false; | |
572 } | |
573 // Generators are never required to have a return statement. | |
574 if (body.isGenerator) { | |
575 return false; | |
576 } | |
577 // Check that the type is resolvable, and is not "void" | |
578 DartType returnTypeType = returnType.type; | |
579 if (returnTypeType == null || returnTypeType.isVoid) { | |
580 return false; | |
581 } | |
582 // For async, give no hint if Future<Null> is assignable to the return | |
583 // type. | |
584 if (body.isAsynchronous && _futureNullType.isAssignableTo(returnTypeType)) { | |
585 return false; | |
586 } | |
587 // Check the block for a return statement, if not, create the hint | |
588 BlockFunctionBody blockFunctionBody = body as BlockFunctionBody; | |
589 if (!ExitDetector.exits(blockFunctionBody)) { | |
590 _errorReporter.reportErrorForNode( | |
591 HintCode.MISSING_RETURN, returnType, [returnTypeType.displayName]); | |
592 return true; | |
593 } | |
594 return false; | |
595 } | |
596 | |
597 /** | |
598 * Check for the passed class declaration for the | |
599 * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code. | |
600 * | |
601 * @param node the class declaration to check | |
602 * @return `true` if and only if a hint code is generated on the passed node | |
603 * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE]. | |
604 */ | |
605 // bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) { | |
606 // ClassElement classElement = node.element; | |
607 // if (classElement == null) { | |
608 // return false; | |
609 // } | |
610 // MethodElement equalsOperatorMethodElement = | |
611 // classElement.getMethod(sc.TokenType.EQ_EQ.lexeme); | |
612 // if (equalsOperatorMethodElement != null) { | |
613 // PropertyAccessorElement hashCodeElement = | |
614 // classElement.getGetter(_HASHCODE_GETTER_NAME); | |
615 // if (hashCodeElement == null) { | |
616 // _errorReporter.reportErrorForNode( | |
617 // HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE, | |
618 // node.name, | |
619 // [classElement.displayName]); | |
620 // return true; | |
621 // } | |
622 // } | |
623 // return false; | |
624 // } | |
625 | |
626 /** | |
627 * Check for the passed as expression for the [HintCode.UNNECESSARY_CAST] hint
code. | |
628 * | |
629 * @param node the as expression to check | |
630 * @return `true` if and only if a hint code is generated on the passed node | |
631 * See [HintCode.UNNECESSARY_CAST]. | |
632 */ | |
633 bool _checkForUnnecessaryCast(AsExpression node) { | |
634 // TODO(jwren) After dartbug.com/13732, revisit this, we should be able to | |
635 // remove the (x is! TypeParameterType) checks. | |
636 AstNode parent = node.parent; | |
637 if (parent is ConditionalExpression && | |
638 (node == parent.thenExpression || node == parent.elseExpression)) { | |
639 Expression thenExpression = parent.thenExpression; | |
640 DartType thenType; | |
641 if (thenExpression is AsExpression) { | |
642 thenType = thenExpression.expression.staticType; | |
643 } else { | |
644 thenType = thenExpression.staticType; | |
645 } | |
646 Expression elseExpression = parent.elseExpression; | |
647 DartType elseType; | |
648 if (elseExpression is AsExpression) { | |
649 elseType = elseExpression.expression.staticType; | |
650 } else { | |
651 elseType = elseExpression.staticType; | |
652 } | |
653 if (thenType != null && | |
654 elseType != null && | |
655 !thenType.isDynamic && | |
656 !elseType.isDynamic && | |
657 !thenType.isMoreSpecificThan(elseType) && | |
658 !elseType.isMoreSpecificThan(thenType)) { | |
659 return false; | |
660 } | |
661 } | |
662 DartType lhsType = node.expression.staticType; | |
663 DartType rhsType = node.type.type; | |
664 if (lhsType != null && | |
665 rhsType != null && | |
666 !lhsType.isDynamic && | |
667 !rhsType.isDynamic && | |
668 lhsType.isMoreSpecificThan(rhsType)) { | |
669 _errorReporter.reportErrorForNode(HintCode.UNNECESSARY_CAST, node); | |
670 return true; | |
671 } | |
672 return false; | |
673 } | |
674 | |
675 /** | |
676 * Check for situations where the result of a method or function is used, when
it returns 'void'. | |
677 * | |
678 * TODO(jwren) Many other situations of use could be covered. We currently cov
er the cases var x = | |
679 * m() and x = m(), but we could also cover cases such as m().x, m()[k], a + m
(), f(m()), return | |
680 * m(). | |
681 * | |
682 * @param node expression on the RHS of some assignment | |
683 * @return `true` if and only if a hint code is generated on the passed node | |
684 * See [HintCode.USE_OF_VOID_RESULT]. | |
685 */ | |
686 bool _checkForUseOfVoidResult(Expression expression) { | |
687 if (expression == null || expression is! MethodInvocation) { | |
688 return false; | |
689 } | |
690 MethodInvocation methodInvocation = expression as MethodInvocation; | |
691 if (identical(methodInvocation.staticType, VoidTypeImpl.instance)) { | |
692 SimpleIdentifier methodName = methodInvocation.methodName; | |
693 _errorReporter.reportErrorForNode( | |
694 HintCode.USE_OF_VOID_RESULT, methodName, [methodName.name]); | |
695 return true; | |
696 } | |
697 return false; | |
698 } | |
699 | |
700 /** | |
701 * Given a parenthesized expression, this returns the parent (or recursively g
rand-parent) of the | |
702 * expression that is a parenthesized expression, but whose parent is not a pa
renthesized | |
703 * expression. | |
704 * | |
705 * For example given the code `(((e)))`: `(e) -> (((e)))`. | |
706 * | |
707 * @param parenthesizedExpression some expression whose parent is a parenthesi
zed expression | |
708 * @return the first parent or grand-parent that is a parenthesized expression
, that does not have | |
709 * a parenthesized expression parent | |
710 */ | |
711 static ParenthesizedExpression _wrapParenthesizedExpression( | |
712 ParenthesizedExpression parenthesizedExpression) { | |
713 if (parenthesizedExpression.parent is ParenthesizedExpression) { | |
714 return _wrapParenthesizedExpression( | |
715 parenthesizedExpression.parent as ParenthesizedExpression); | |
716 } | |
717 return parenthesizedExpression; | |
718 } | |
719 } | |
720 | |
721 /** | |
722 * Instances of the class `ClassScope` implement the scope defined by a class. | |
723 */ | |
724 class ClassScope extends EnclosedScope { | |
725 /** | |
726 * Initialize a newly created scope enclosed within another scope. | |
727 * | |
728 * @param enclosingScope the scope in which this scope is lexically enclosed | |
729 * @param typeElement the element representing the type represented by this sc
ope | |
730 */ | |
731 ClassScope(Scope enclosingScope, ClassElement typeElement) | |
732 : super(enclosingScope) { | |
733 if (typeElement == null) { | |
734 throw new IllegalArgumentException("class element cannot be null"); | |
735 } | |
736 _defineMembers(typeElement); | |
737 } | |
738 | |
739 @override | |
740 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { | |
741 if (existing is PropertyAccessorElement && duplicate is MethodElement) { | |
742 if (existing.nameOffset < duplicate.nameOffset) { | |
743 return new AnalysisError(duplicate.source, duplicate.nameOffset, | |
744 duplicate.displayName.length, | |
745 CompileTimeErrorCode.METHOD_AND_GETTER_WITH_SAME_NAME, | |
746 [existing.displayName]); | |
747 } else { | |
748 return new AnalysisError(existing.source, existing.nameOffset, | |
749 existing.displayName.length, | |
750 CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME, | |
751 [existing.displayName]); | |
752 } | |
753 } | |
754 return super.getErrorForDuplicate(existing, duplicate); | |
755 } | |
756 | |
757 /** | |
758 * Define the instance members defined by the class. | |
759 * | |
760 * @param typeElement the element representing the type represented by this sc
ope | |
761 */ | |
762 void _defineMembers(ClassElement typeElement) { | |
763 for (PropertyAccessorElement accessor in typeElement.accessors) { | |
764 define(accessor); | |
765 } | |
766 for (MethodElement method in typeElement.methods) { | |
767 define(method); | |
768 } | |
769 } | |
770 } | |
771 | |
772 /** | |
773 * A `CompilationUnitBuilder` builds an element model for a single compilation | |
774 * unit. | |
775 */ | |
776 class CompilationUnitBuilder { | |
777 /** | |
778 * Build the compilation unit element for the given [source] based on the | |
779 * compilation [unit] associated with the source. Throw an AnalysisException | |
780 * if the element could not be built. [librarySource] is the source for the | |
781 * containing library. | |
782 */ | |
783 CompilationUnitElementImpl buildCompilationUnit( | |
784 Source source, CompilationUnit unit, Source librarySource) { | |
785 return PerformanceStatistics.resolve.makeCurrentWhile(() { | |
786 if (unit == null) { | |
787 return null; | |
788 } | |
789 ElementHolder holder = new ElementHolder(); | |
790 ElementBuilder builder = new ElementBuilder(holder); | |
791 unit.accept(builder); | |
792 CompilationUnitElementImpl element = | |
793 new CompilationUnitElementImpl(source.shortName); | |
794 element.accessors = holder.accessors; | |
795 element.enums = holder.enums; | |
796 element.functions = holder.functions; | |
797 element.source = source; | |
798 element.librarySource = librarySource; | |
799 element.typeAliases = holder.typeAliases; | |
800 element.types = holder.types; | |
801 element.topLevelVariables = holder.topLevelVariables; | |
802 unit.element = element; | |
803 holder.validate(); | |
804 return element; | |
805 }); | |
806 } | |
807 } | |
808 | |
809 /** | |
810 * Instances of the class `ConstantVerifier` traverse an AST structure looking f
or additional | |
811 * errors and warnings not covered by the parser and resolver. In particular, it
looks for errors | |
812 * and warnings related to constant expressions. | |
813 */ | |
814 class ConstantVerifier extends RecursiveAstVisitor<Object> { | |
815 /** | |
816 * The error reporter by which errors will be reported. | |
817 */ | |
818 final ErrorReporter _errorReporter; | |
819 | |
820 /** | |
821 * The type provider used to access the known types. | |
822 */ | |
823 final TypeProvider _typeProvider; | |
824 | |
825 /** | |
826 * The set of variables declared using '-D' on the command line. | |
827 */ | |
828 final DeclaredVariables declaredVariables; | |
829 | |
830 /** | |
831 * The type representing the type 'bool'. | |
832 */ | |
833 InterfaceType _boolType; | |
834 | |
835 /** | |
836 * The type representing the type 'int'. | |
837 */ | |
838 InterfaceType _intType; | |
839 | |
840 /** | |
841 * The type representing the type 'num'. | |
842 */ | |
843 InterfaceType _numType; | |
844 | |
845 /** | |
846 * The type representing the type 'string'. | |
847 */ | |
848 InterfaceType _stringType; | |
849 | |
850 /** | |
851 * The current library that is being analyzed. | |
852 */ | |
853 final LibraryElement _currentLibrary; | |
854 | |
855 /** | |
856 * Initialize a newly created constant verifier. | |
857 * | |
858 * @param errorReporter the error reporter by which errors will be reported | |
859 */ | |
860 ConstantVerifier(this._errorReporter, this._currentLibrary, | |
861 this._typeProvider, this.declaredVariables) { | |
862 this._boolType = _typeProvider.boolType; | |
863 this._intType = _typeProvider.intType; | |
864 this._numType = _typeProvider.numType; | |
865 this._stringType = _typeProvider.stringType; | |
866 } | |
867 | |
868 @override | |
869 Object visitAnnotation(Annotation node) { | |
870 super.visitAnnotation(node); | |
871 // check annotation creation | |
872 Element element = node.element; | |
873 if (element is ConstructorElement) { | |
874 ConstructorElement constructorElement = element; | |
875 // should 'const' constructor | |
876 if (!constructorElement.isConst) { | |
877 _errorReporter.reportErrorForNode( | |
878 CompileTimeErrorCode.NON_CONSTANT_ANNOTATION_CONSTRUCTOR, node); | |
879 return null; | |
880 } | |
881 // should have arguments | |
882 ArgumentList argumentList = node.arguments; | |
883 if (argumentList == null) { | |
884 _errorReporter.reportErrorForNode( | |
885 CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS, node); | |
886 return null; | |
887 } | |
888 // arguments should be constants | |
889 _validateConstantArguments(argumentList); | |
890 } | |
891 return null; | |
892 } | |
893 | |
894 @override | |
895 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
896 if (node.constKeyword != null) { | |
897 _validateConstructorInitializers(node); | |
898 _validateFieldInitializers(node.parent as ClassDeclaration, node); | |
899 } | |
900 _validateDefaultValues(node.parameters); | |
901 return super.visitConstructorDeclaration(node); | |
902 } | |
903 | |
904 @override | |
905 Object visitFunctionExpression(FunctionExpression node) { | |
906 super.visitFunctionExpression(node); | |
907 _validateDefaultValues(node.parameters); | |
908 return null; | |
909 } | |
910 | |
911 @override | |
912 Object visitInstanceCreationExpression(InstanceCreationExpression node) { | |
913 if (node.isConst) { | |
914 // We need to evaluate the constant to see if any errors occur during its | |
915 // evaluation. | |
916 ConstructorElement constructor = node.staticElement; | |
917 if (constructor != null) { | |
918 ConstantEvaluationEngine evaluationEngine = | |
919 new ConstantEvaluationEngine(_typeProvider, declaredVariables); | |
920 ConstantVisitor constantVisitor = | |
921 new ConstantVisitor(evaluationEngine, _errorReporter); | |
922 evaluationEngine.evaluateConstructorCall(node, | |
923 node.argumentList.arguments, constructor, constantVisitor, | |
924 _errorReporter); | |
925 } | |
926 } | |
927 _validateInstanceCreationArguments(node); | |
928 return super.visitInstanceCreationExpression(node); | |
929 } | |
930 | |
931 @override | |
932 Object visitListLiteral(ListLiteral node) { | |
933 super.visitListLiteral(node); | |
934 if (node.constKeyword != null) { | |
935 DartObjectImpl result; | |
936 for (Expression element in node.elements) { | |
937 result = | |
938 _validate(element, CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT); | |
939 if (result != null) { | |
940 _reportErrorIfFromDeferredLibrary(element, | |
941 CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRA
RY); | |
942 } | |
943 } | |
944 } | |
945 return null; | |
946 } | |
947 | |
948 @override | |
949 Object visitMapLiteral(MapLiteral node) { | |
950 super.visitMapLiteral(node); | |
951 bool isConst = node.constKeyword != null; | |
952 bool reportEqualKeys = true; | |
953 HashSet<DartObject> keys = new HashSet<DartObject>(); | |
954 List<Expression> invalidKeys = new List<Expression>(); | |
955 for (MapLiteralEntry entry in node.entries) { | |
956 Expression key = entry.key; | |
957 if (isConst) { | |
958 DartObjectImpl keyResult = | |
959 _validate(key, CompileTimeErrorCode.NON_CONSTANT_MAP_KEY); | |
960 Expression valueExpression = entry.value; | |
961 DartObjectImpl valueResult = _validate( | |
962 valueExpression, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE); | |
963 if (valueResult != null) { | |
964 _reportErrorIfFromDeferredLibrary(valueExpression, | |
965 CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY)
; | |
966 } | |
967 if (keyResult != null) { | |
968 _reportErrorIfFromDeferredLibrary(key, | |
969 CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY); | |
970 if (keys.contains(keyResult)) { | |
971 invalidKeys.add(key); | |
972 } else { | |
973 keys.add(keyResult); | |
974 } | |
975 DartType type = keyResult.type; | |
976 if (_implementsEqualsWhenNotAllowed(type)) { | |
977 _errorReporter.reportErrorForNode( | |
978 CompileTimeErrorCode.CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQ
UALS, | |
979 key, [type.displayName]); | |
980 } | |
981 } | |
982 } else { | |
983 // Note: we throw the errors away because this isn't actually a const. | |
984 AnalysisErrorListener errorListener = | |
985 AnalysisErrorListener.NULL_LISTENER; | |
986 ErrorReporter subErrorReporter = | |
987 new ErrorReporter(errorListener, _errorReporter.source); | |
988 DartObjectImpl result = key.accept(new ConstantVisitor( | |
989 new ConstantEvaluationEngine(_typeProvider, declaredVariables), | |
990 subErrorReporter)); | |
991 if (result != null) { | |
992 if (keys.contains(result)) { | |
993 invalidKeys.add(key); | |
994 } else { | |
995 keys.add(result); | |
996 } | |
997 } else { | |
998 reportEqualKeys = false; | |
999 } | |
1000 } | |
1001 } | |
1002 if (reportEqualKeys) { | |
1003 for (Expression key in invalidKeys) { | |
1004 _errorReporter.reportErrorForNode( | |
1005 StaticWarningCode.EQUAL_KEYS_IN_MAP, key); | |
1006 } | |
1007 } | |
1008 return null; | |
1009 } | |
1010 | |
1011 @override | |
1012 Object visitMethodDeclaration(MethodDeclaration node) { | |
1013 super.visitMethodDeclaration(node); | |
1014 _validateDefaultValues(node.parameters); | |
1015 return null; | |
1016 } | |
1017 | |
1018 @override | |
1019 Object visitSwitchStatement(SwitchStatement node) { | |
1020 // TODO(paulberry): to minimize error messages, it would be nice to | |
1021 // compare all types with the most popular type rather than the first | |
1022 // type. | |
1023 NodeList<SwitchMember> switchMembers = node.members; | |
1024 bool foundError = false; | |
1025 DartType firstType = null; | |
1026 for (SwitchMember switchMember in switchMembers) { | |
1027 if (switchMember is SwitchCase) { | |
1028 SwitchCase switchCase = switchMember; | |
1029 Expression expression = switchCase.expression; | |
1030 DartObjectImpl caseResult = _validate( | |
1031 expression, CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION); | |
1032 if (caseResult != null) { | |
1033 _reportErrorIfFromDeferredLibrary(expression, | |
1034 CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LI
BRARY); | |
1035 DartObject value = caseResult; | |
1036 if (firstType == null) { | |
1037 firstType = value.type; | |
1038 } else { | |
1039 DartType nType = value.type; | |
1040 if (firstType != nType) { | |
1041 _errorReporter.reportErrorForNode( | |
1042 CompileTimeErrorCode.INCONSISTENT_CASE_EXPRESSION_TYPES, | |
1043 expression, [expression.toSource(), firstType.displayName]); | |
1044 foundError = true; | |
1045 } | |
1046 } | |
1047 } | |
1048 } | |
1049 } | |
1050 if (!foundError) { | |
1051 _checkForCaseExpressionTypeImplementsEquals(node, firstType); | |
1052 } | |
1053 return super.visitSwitchStatement(node); | |
1054 } | |
1055 | |
1056 @override | |
1057 Object visitVariableDeclaration(VariableDeclaration node) { | |
1058 super.visitVariableDeclaration(node); | |
1059 Expression initializer = node.initializer; | |
1060 if (initializer != null && (node.isConst || node.isFinal)) { | |
1061 VariableElementImpl element = node.element as VariableElementImpl; | |
1062 EvaluationResultImpl result = element.evaluationResult; | |
1063 if (result == null) { | |
1064 // Variables marked "const" should have had their values computed by | |
1065 // ConstantValueComputer. Other variables will only have had their | |
1066 // values computed if the value was needed (e.g. final variables in a | |
1067 // class containing const constructors). | |
1068 assert(!node.isConst); | |
1069 return null; | |
1070 } | |
1071 _reportErrors(result.errors, | |
1072 CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE); | |
1073 _reportErrorIfFromDeferredLibrary(initializer, | |
1074 CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DE
FERRED_LIBRARY); | |
1075 } | |
1076 return null; | |
1077 } | |
1078 | |
1079 /** | |
1080 * This verifies that the passed switch statement does not have a case express
ion with the | |
1081 * operator '==' overridden. | |
1082 * | |
1083 * @param node the switch statement to evaluate | |
1084 * @param type the common type of all 'case' expressions | |
1085 * @return `true` if and only if an error code is generated on the passed node | |
1086 * See [CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS]. | |
1087 */ | |
1088 bool _checkForCaseExpressionTypeImplementsEquals( | |
1089 SwitchStatement node, DartType type) { | |
1090 if (!_implementsEqualsWhenNotAllowed(type)) { | |
1091 return false; | |
1092 } | |
1093 // report error | |
1094 _errorReporter.reportErrorForToken( | |
1095 CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, | |
1096 node.switchKeyword, [type.displayName]); | |
1097 return true; | |
1098 } | |
1099 | |
1100 /** | |
1101 * @return `true` if given [Type] implements operator <i>==</i>, and it is not | |
1102 * <i>int</i> or <i>String</i>. | |
1103 */ | |
1104 bool _implementsEqualsWhenNotAllowed(DartType type) { | |
1105 // ignore int or String | |
1106 if (type == null || type == _intType || type == _typeProvider.stringType) { | |
1107 return false; | |
1108 } else if (type == _typeProvider.doubleType) { | |
1109 return true; | |
1110 } | |
1111 // prepare ClassElement | |
1112 Element element = type.element; | |
1113 if (element is! ClassElement) { | |
1114 return false; | |
1115 } | |
1116 ClassElement classElement = element as ClassElement; | |
1117 // lookup for == | |
1118 MethodElement method = | |
1119 classElement.lookUpConcreteMethod("==", _currentLibrary); | |
1120 if (method == null || method.enclosingElement.type.isObject) { | |
1121 return false; | |
1122 } | |
1123 // there is == that we don't like | |
1124 return true; | |
1125 } | |
1126 | |
1127 /** | |
1128 * Given some computed [Expression], this method generates the passed [ErrorCo
de] on | |
1129 * the node if its' value consists of information from a deferred library. | |
1130 * | |
1131 * @param expression the expression to be tested for a deferred library refere
nce | |
1132 * @param errorCode the error code to be used if the expression is or consists
of a reference to a | |
1133 * deferred library | |
1134 */ | |
1135 void _reportErrorIfFromDeferredLibrary( | |
1136 Expression expression, ErrorCode errorCode) { | |
1137 DeferredLibraryReferenceDetector referenceDetector = | |
1138 new DeferredLibraryReferenceDetector(); | |
1139 expression.accept(referenceDetector); | |
1140 if (referenceDetector.result) { | |
1141 _errorReporter.reportErrorForNode(errorCode, expression); | |
1142 } | |
1143 } | |
1144 | |
1145 /** | |
1146 * Report any errors in the given list. Except for special cases, use the give
n error code rather | |
1147 * than the one reported in the error. | |
1148 * | |
1149 * @param errors the errors that need to be reported | |
1150 * @param errorCode the error code to be used | |
1151 */ | |
1152 void _reportErrors(List<AnalysisError> errors, ErrorCode errorCode) { | |
1153 for (AnalysisError data in errors) { | |
1154 ErrorCode dataErrorCode = data.errorCode; | |
1155 if (identical(dataErrorCode, | |
1156 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) || | |
1157 identical( | |
1158 dataErrorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE) || | |
1159 identical(dataErrorCode, | |
1160 CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING) || | |
1161 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL) || | |
1162 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_INT) || | |
1163 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM) || | |
1164 identical(dataErrorCode, | |
1165 CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT) || | |
1166 identical(dataErrorCode, | |
1167 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMA
TCH) || | |
1168 identical(dataErrorCode, | |
1169 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMA
TCH) || | |
1170 identical(dataErrorCode, | |
1171 CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH)) { | |
1172 _errorReporter.reportError(data); | |
1173 } else if (errorCode != null) { | |
1174 _errorReporter.reportError(new AnalysisError( | |
1175 data.source, data.offset, data.length, errorCode)); | |
1176 } | |
1177 } | |
1178 } | |
1179 | |
1180 /** | |
1181 * Validate that the given expression is a compile time constant. Return the v
alue of the compile | |
1182 * time constant, or `null` if the expression is not a compile time constant. | |
1183 * | |
1184 * @param expression the expression to be validated | |
1185 * @param errorCode the error code to be used if the expression is not a compi
le time constant | |
1186 * @return the value of the compile time constant | |
1187 */ | |
1188 DartObjectImpl _validate(Expression expression, ErrorCode errorCode) { | |
1189 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
1190 ErrorReporter subErrorReporter = | |
1191 new ErrorReporter(errorListener, _errorReporter.source); | |
1192 DartObjectImpl result = expression.accept(new ConstantVisitor( | |
1193 new ConstantEvaluationEngine(_typeProvider, declaredVariables), | |
1194 subErrorReporter)); | |
1195 _reportErrors(errorListener.errors, errorCode); | |
1196 return result; | |
1197 } | |
1198 | |
1199 /** | |
1200 * Validate that if the passed arguments are constant expressions. | |
1201 * | |
1202 * @param argumentList the argument list to evaluate | |
1203 */ | |
1204 void _validateConstantArguments(ArgumentList argumentList) { | |
1205 for (Expression argument in argumentList.arguments) { | |
1206 if (argument is NamedExpression) { | |
1207 argument = (argument as NamedExpression).expression; | |
1208 } | |
1209 _validate( | |
1210 argument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT); | |
1211 } | |
1212 } | |
1213 | |
1214 /** | |
1215 * Validates that the expressions of the given initializers (of a constant con
structor) are all | |
1216 * compile time constants. | |
1217 * | |
1218 * @param constructor the constant constructor declaration to validate | |
1219 */ | |
1220 void _validateConstructorInitializers(ConstructorDeclaration constructor) { | |
1221 List<ParameterElement> parameterElements = | |
1222 constructor.parameters.parameterElements; | |
1223 NodeList<ConstructorInitializer> initializers = constructor.initializers; | |
1224 for (ConstructorInitializer initializer in initializers) { | |
1225 if (initializer is ConstructorFieldInitializer) { | |
1226 ConstructorFieldInitializer fieldInitializer = initializer; | |
1227 _validateInitializerExpression( | |
1228 parameterElements, fieldInitializer.expression); | |
1229 } | |
1230 if (initializer is RedirectingConstructorInvocation) { | |
1231 RedirectingConstructorInvocation invocation = initializer; | |
1232 _validateInitializerInvocationArguments( | |
1233 parameterElements, invocation.argumentList); | |
1234 } | |
1235 if (initializer is SuperConstructorInvocation) { | |
1236 SuperConstructorInvocation invocation = initializer; | |
1237 _validateInitializerInvocationArguments( | |
1238 parameterElements, invocation.argumentList); | |
1239 } | |
1240 } | |
1241 } | |
1242 | |
1243 /** | |
1244 * Validate that the default value associated with each of the parameters in t
he given list is a | |
1245 * compile time constant. | |
1246 * | |
1247 * @param parameters the list of parameters to be validated | |
1248 */ | |
1249 void _validateDefaultValues(FormalParameterList parameters) { | |
1250 if (parameters == null) { | |
1251 return; | |
1252 } | |
1253 for (FormalParameter parameter in parameters.parameters) { | |
1254 if (parameter is DefaultFormalParameter) { | |
1255 DefaultFormalParameter defaultParameter = parameter; | |
1256 Expression defaultValue = defaultParameter.defaultValue; | |
1257 DartObjectImpl result; | |
1258 if (defaultValue == null) { | |
1259 result = | |
1260 new DartObjectImpl(_typeProvider.nullType, NullState.NULL_STATE); | |
1261 } else { | |
1262 result = _validate( | |
1263 defaultValue, CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE); | |
1264 if (result != null) { | |
1265 _reportErrorIfFromDeferredLibrary(defaultValue, | |
1266 CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LI
BRARY); | |
1267 } | |
1268 } | |
1269 VariableElementImpl element = parameter.element as VariableElementImpl; | |
1270 element.evaluationResult = new EvaluationResultImpl(result); | |
1271 } | |
1272 } | |
1273 } | |
1274 | |
1275 /** | |
1276 * Validates that the expressions of any field initializers in the class decla
ration are all | |
1277 * compile time constants. Since this is only required if the class has a cons
tant constructor, | |
1278 * the error is reported at the constructor site. | |
1279 * | |
1280 * @param classDeclaration the class which should be validated | |
1281 * @param errorSite the site at which errors should be reported. | |
1282 */ | |
1283 void _validateFieldInitializers( | |
1284 ClassDeclaration classDeclaration, ConstructorDeclaration errorSite) { | |
1285 NodeList<ClassMember> members = classDeclaration.members; | |
1286 for (ClassMember member in members) { | |
1287 if (member is FieldDeclaration) { | |
1288 FieldDeclaration fieldDeclaration = member; | |
1289 if (!fieldDeclaration.isStatic) { | |
1290 for (VariableDeclaration variableDeclaration | |
1291 in fieldDeclaration.fields.variables) { | |
1292 Expression initializer = variableDeclaration.initializer; | |
1293 if (initializer != null) { | |
1294 // Ignore any errors produced during validation--if the constant | |
1295 // can't be eavluated we'll just report a single error. | |
1296 AnalysisErrorListener errorListener = | |
1297 AnalysisErrorListener.NULL_LISTENER; | |
1298 ErrorReporter subErrorReporter = | |
1299 new ErrorReporter(errorListener, _errorReporter.source); | |
1300 DartObjectImpl result = initializer.accept(new ConstantVisitor( | |
1301 new ConstantEvaluationEngine( | |
1302 _typeProvider, declaredVariables), subErrorReporter)); | |
1303 if (result == null) { | |
1304 _errorReporter.reportErrorForNode( | |
1305 CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZE
D_BY_NON_CONST, | |
1306 errorSite, [variableDeclaration.name.name]); | |
1307 } | |
1308 } | |
1309 } | |
1310 } | |
1311 } | |
1312 } | |
1313 } | |
1314 | |
1315 /** | |
1316 * Validates that the given expression is a compile time constant. | |
1317 * | |
1318 * @param parameterElements the elements of parameters of constant constructor
, they are | |
1319 * considered as a valid potentially constant expressions | |
1320 * @param expression the expression to validate | |
1321 */ | |
1322 void _validateInitializerExpression( | |
1323 List<ParameterElement> parameterElements, Expression expression) { | |
1324 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
1325 ErrorReporter subErrorReporter = | |
1326 new ErrorReporter(errorListener, _errorReporter.source); | |
1327 DartObjectImpl result = expression.accept( | |
1328 new _ConstantVerifier_validateInitializerExpression(_typeProvider, | |
1329 subErrorReporter, this, parameterElements, declaredVariables)); | |
1330 _reportErrors(errorListener.errors, | |
1331 CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER); | |
1332 if (result != null) { | |
1333 _reportErrorIfFromDeferredLibrary(expression, | |
1334 CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER_FROM_DEFERRED_L
IBRARY); | |
1335 } | |
1336 } | |
1337 | |
1338 /** | |
1339 * Validates that all of the arguments of a constructor initializer are compil
e time constants. | |
1340 * | |
1341 * @param parameterElements the elements of parameters of constant constructor
, they are | |
1342 * considered as a valid potentially constant expressions | |
1343 * @param argumentList the argument list to validate | |
1344 */ | |
1345 void _validateInitializerInvocationArguments( | |
1346 List<ParameterElement> parameterElements, ArgumentList argumentList) { | |
1347 if (argumentList == null) { | |
1348 return; | |
1349 } | |
1350 for (Expression argument in argumentList.arguments) { | |
1351 _validateInitializerExpression(parameterElements, argument); | |
1352 } | |
1353 } | |
1354 | |
1355 /** | |
1356 * Validate that if the passed instance creation is 'const' then all its argum
ents are constant | |
1357 * expressions. | |
1358 * | |
1359 * @param node the instance creation evaluate | |
1360 */ | |
1361 void _validateInstanceCreationArguments(InstanceCreationExpression node) { | |
1362 if (!node.isConst) { | |
1363 return; | |
1364 } | |
1365 ArgumentList argumentList = node.argumentList; | |
1366 if (argumentList == null) { | |
1367 return; | |
1368 } | |
1369 _validateConstantArguments(argumentList); | |
1370 } | |
1371 } | |
1372 | |
1373 /** | |
1374 * Instances of the class `Dart2JSVerifier` traverse an AST structure looking fo
r hints for | |
1375 * code that will be compiled to JS, such as [HintCode.IS_DOUBLE]. | |
1376 */ | |
1377 class Dart2JSVerifier extends RecursiveAstVisitor<Object> { | |
1378 /** | |
1379 * The name of the `double` type. | |
1380 */ | |
1381 static String _DOUBLE_TYPE_NAME = "double"; | |
1382 | |
1383 /** | |
1384 * The error reporter by which errors will be reported. | |
1385 */ | |
1386 final ErrorReporter _errorReporter; | |
1387 | |
1388 /** | |
1389 * Create a new instance of the [Dart2JSVerifier]. | |
1390 * | |
1391 * @param errorReporter the error reporter | |
1392 */ | |
1393 Dart2JSVerifier(this._errorReporter); | |
1394 | |
1395 @override | |
1396 Object visitIsExpression(IsExpression node) { | |
1397 _checkForIsDoubleHints(node); | |
1398 return super.visitIsExpression(node); | |
1399 } | |
1400 | |
1401 /** | |
1402 * Check for instances of `x is double`, `x is int`, `x is! double` and | |
1403 * `x is! int`. | |
1404 * | |
1405 * @param node the is expression to check | |
1406 * @return `true` if and only if a hint code is generated on the passed node | |
1407 * See [HintCode.IS_DOUBLE], | |
1408 * [HintCode.IS_INT], | |
1409 * [HintCode.IS_NOT_DOUBLE], and | |
1410 * [HintCode.IS_NOT_INT]. | |
1411 */ | |
1412 bool _checkForIsDoubleHints(IsExpression node) { | |
1413 TypeName typeName = node.type; | |
1414 DartType type = typeName.type; | |
1415 if (type != null && type.element != null) { | |
1416 Element element = type.element; | |
1417 String typeNameStr = element.name; | |
1418 LibraryElement libraryElement = element.library; | |
1419 // if (typeNameStr.equals(INT_TYPE_NAME) && libraryElement != null | |
1420 // && libraryElement.isDartCore()) { | |
1421 // if (node.getNotOperator() == null) { | |
1422 // errorReporter.reportError(HintCode.IS_INT, node); | |
1423 // } else { | |
1424 // errorReporter.reportError(HintCode.IS_NOT_INT, node); | |
1425 // } | |
1426 // return true; | |
1427 // } else | |
1428 if (typeNameStr == _DOUBLE_TYPE_NAME && | |
1429 libraryElement != null && | |
1430 libraryElement.isDartCore) { | |
1431 if (node.notOperator == null) { | |
1432 _errorReporter.reportErrorForNode(HintCode.IS_DOUBLE, node); | |
1433 } else { | |
1434 _errorReporter.reportErrorForNode(HintCode.IS_NOT_DOUBLE, node); | |
1435 } | |
1436 return true; | |
1437 } | |
1438 } | |
1439 return false; | |
1440 } | |
1441 } | |
1442 | |
1443 /** | |
1444 * Instances of the class `DeadCodeVerifier` traverse an AST structure looking f
or cases of | |
1445 * [HintCode.DEAD_CODE]. | |
1446 */ | |
1447 class DeadCodeVerifier extends RecursiveAstVisitor<Object> { | |
1448 /** | |
1449 * The error reporter by which errors will be reported. | |
1450 */ | |
1451 final ErrorReporter _errorReporter; | |
1452 | |
1453 /** | |
1454 * Create a new instance of the [DeadCodeVerifier]. | |
1455 * | |
1456 * @param errorReporter the error reporter | |
1457 */ | |
1458 DeadCodeVerifier(this._errorReporter); | |
1459 | |
1460 @override | |
1461 Object visitBinaryExpression(BinaryExpression node) { | |
1462 sc.Token operator = node.operator; | |
1463 bool isAmpAmp = operator.type == sc.TokenType.AMPERSAND_AMPERSAND; | |
1464 bool isBarBar = operator.type == sc.TokenType.BAR_BAR; | |
1465 if (isAmpAmp || isBarBar) { | |
1466 Expression lhsCondition = node.leftOperand; | |
1467 if (!_isDebugConstant(lhsCondition)) { | |
1468 EvaluationResultImpl lhsResult = _getConstantBooleanValue(lhsCondition); | |
1469 if (lhsResult != null) { | |
1470 if (lhsResult.value.isTrue && isBarBar) { | |
1471 // report error on else block: true || !e! | |
1472 _errorReporter.reportErrorForNode( | |
1473 HintCode.DEAD_CODE, node.rightOperand); | |
1474 // only visit the LHS: | |
1475 _safelyVisit(lhsCondition); | |
1476 return null; | |
1477 } else if (lhsResult.value.isFalse && isAmpAmp) { | |
1478 // report error on if block: false && !e! | |
1479 _errorReporter.reportErrorForNode( | |
1480 HintCode.DEAD_CODE, node.rightOperand); | |
1481 // only visit the LHS: | |
1482 _safelyVisit(lhsCondition); | |
1483 return null; | |
1484 } | |
1485 } | |
1486 } | |
1487 // How do we want to handle the RHS? It isn't dead code, but "pointless" | |
1488 // or "obscure"... | |
1489 // Expression rhsCondition = node.getRightOperand(); | |
1490 // ValidResult rhsResult = getConstantBooleanValue(rhsCondition); | |
1491 // if (rhsResult != null) { | |
1492 // if (rhsResult == ValidResult.RESULT_TRUE && isBarBar) { | |
1493 // // report error on else block: !e! || true | |
1494 // errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOpe
rand()); | |
1495 // // only visit the RHS: | |
1496 // safelyVisit(rhsCondition); | |
1497 // return null; | |
1498 // } else if (rhsResult == ValidResult.RESULT_FALSE && isAmpAmp) { | |
1499 // // report error on if block: !e! && false | |
1500 // errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOpe
rand()); | |
1501 // // only visit the RHS: | |
1502 // safelyVisit(rhsCondition); | |
1503 // return null; | |
1504 // } | |
1505 // } | |
1506 } | |
1507 return super.visitBinaryExpression(node); | |
1508 } | |
1509 | |
1510 /** | |
1511 * For each [Block], this method reports and error on all statements between t
he end of the | |
1512 * block and the first return statement (assuming there it is not at the end o
f the block.) | |
1513 * | |
1514 * @param node the block to evaluate | |
1515 */ | |
1516 @override | |
1517 Object visitBlock(Block node) { | |
1518 NodeList<Statement> statements = node.statements; | |
1519 _checkForDeadStatementsInNodeList(statements); | |
1520 return null; | |
1521 } | |
1522 | |
1523 @override | |
1524 Object visitConditionalExpression(ConditionalExpression node) { | |
1525 Expression conditionExpression = node.condition; | |
1526 _safelyVisit(conditionExpression); | |
1527 if (!_isDebugConstant(conditionExpression)) { | |
1528 EvaluationResultImpl result = | |
1529 _getConstantBooleanValue(conditionExpression); | |
1530 if (result != null) { | |
1531 if (result.value.isTrue) { | |
1532 // report error on else block: true ? 1 : !2! | |
1533 _errorReporter.reportErrorForNode( | |
1534 HintCode.DEAD_CODE, node.elseExpression); | |
1535 _safelyVisit(node.thenExpression); | |
1536 return null; | |
1537 } else { | |
1538 // report error on if block: false ? !1! : 2 | |
1539 _errorReporter.reportErrorForNode( | |
1540 HintCode.DEAD_CODE, node.thenExpression); | |
1541 _safelyVisit(node.elseExpression); | |
1542 return null; | |
1543 } | |
1544 } | |
1545 } | |
1546 return super.visitConditionalExpression(node); | |
1547 } | |
1548 | |
1549 @override | |
1550 Object visitIfStatement(IfStatement node) { | |
1551 Expression conditionExpression = node.condition; | |
1552 _safelyVisit(conditionExpression); | |
1553 if (!_isDebugConstant(conditionExpression)) { | |
1554 EvaluationResultImpl result = | |
1555 _getConstantBooleanValue(conditionExpression); | |
1556 if (result != null) { | |
1557 if (result.value.isTrue) { | |
1558 // report error on else block: if(true) {} else {!} | |
1559 Statement elseStatement = node.elseStatement; | |
1560 if (elseStatement != null) { | |
1561 _errorReporter.reportErrorForNode( | |
1562 HintCode.DEAD_CODE, elseStatement); | |
1563 _safelyVisit(node.thenStatement); | |
1564 return null; | |
1565 } | |
1566 } else { | |
1567 // report error on if block: if (false) {!} else {} | |
1568 _errorReporter.reportErrorForNode( | |
1569 HintCode.DEAD_CODE, node.thenStatement); | |
1570 _safelyVisit(node.elseStatement); | |
1571 return null; | |
1572 } | |
1573 } | |
1574 } | |
1575 return super.visitIfStatement(node); | |
1576 } | |
1577 | |
1578 @override | |
1579 Object visitSwitchCase(SwitchCase node) { | |
1580 _checkForDeadStatementsInNodeList(node.statements); | |
1581 return super.visitSwitchCase(node); | |
1582 } | |
1583 | |
1584 @override | |
1585 Object visitSwitchDefault(SwitchDefault node) { | |
1586 _checkForDeadStatementsInNodeList(node.statements); | |
1587 return super.visitSwitchDefault(node); | |
1588 } | |
1589 | |
1590 @override | |
1591 Object visitTryStatement(TryStatement node) { | |
1592 _safelyVisit(node.body); | |
1593 _safelyVisit(node.finallyBlock); | |
1594 NodeList<CatchClause> catchClauses = node.catchClauses; | |
1595 int numOfCatchClauses = catchClauses.length; | |
1596 List<DartType> visitedTypes = new List<DartType>(); | |
1597 for (int i = 0; i < numOfCatchClauses; i++) { | |
1598 CatchClause catchClause = catchClauses[i]; | |
1599 if (catchClause.onKeyword != null) { | |
1600 // on-catch clause found, verify that the exception type is not a | |
1601 // subtype of a previous on-catch exception type | |
1602 TypeName typeName = catchClause.exceptionType; | |
1603 if (typeName != null && typeName.type != null) { | |
1604 DartType currentType = typeName.type; | |
1605 if (currentType.isObject) { | |
1606 // Found catch clause clause that has Object as an exception type, | |
1607 // this is equivalent to having a catch clause that doesn't have an | |
1608 // exception type, visit the block, but generate an error on any | |
1609 // following catch clauses (and don't visit them). | |
1610 _safelyVisit(catchClause); | |
1611 if (i + 1 != numOfCatchClauses) { | |
1612 // this catch clause is not the last in the try statement | |
1613 CatchClause nextCatchClause = catchClauses[i + 1]; | |
1614 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; | |
1615 int offset = nextCatchClause.offset; | |
1616 int length = lastCatchClause.end - offset; | |
1617 _errorReporter.reportErrorForOffset( | |
1618 HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH, offset, length); | |
1619 return null; | |
1620 } | |
1621 } | |
1622 for (DartType type in visitedTypes) { | |
1623 if (currentType.isSubtypeOf(type)) { | |
1624 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; | |
1625 int offset = catchClause.offset; | |
1626 int length = lastCatchClause.end - offset; | |
1627 _errorReporter.reportErrorForOffset( | |
1628 HintCode.DEAD_CODE_ON_CATCH_SUBTYPE, offset, length, [ | |
1629 currentType.displayName, | |
1630 type.displayName | |
1631 ]); | |
1632 return null; | |
1633 } | |
1634 } | |
1635 visitedTypes.add(currentType); | |
1636 } | |
1637 _safelyVisit(catchClause); | |
1638 } else { | |
1639 // Found catch clause clause that doesn't have an exception type, | |
1640 // visit the block, but generate an error on any following catch clauses | |
1641 // (and don't visit them). | |
1642 _safelyVisit(catchClause); | |
1643 if (i + 1 != numOfCatchClauses) { | |
1644 // this catch clause is not the last in the try statement | |
1645 CatchClause nextCatchClause = catchClauses[i + 1]; | |
1646 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; | |
1647 int offset = nextCatchClause.offset; | |
1648 int length = lastCatchClause.end - offset; | |
1649 _errorReporter.reportErrorForOffset( | |
1650 HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH, offset, length); | |
1651 return null; | |
1652 } | |
1653 } | |
1654 } | |
1655 return null; | |
1656 } | |
1657 | |
1658 @override | |
1659 Object visitWhileStatement(WhileStatement node) { | |
1660 Expression conditionExpression = node.condition; | |
1661 _safelyVisit(conditionExpression); | |
1662 if (!_isDebugConstant(conditionExpression)) { | |
1663 EvaluationResultImpl result = | |
1664 _getConstantBooleanValue(conditionExpression); | |
1665 if (result != null) { | |
1666 if (result.value.isFalse) { | |
1667 // report error on if block: while (false) {!} | |
1668 _errorReporter.reportErrorForNode(HintCode.DEAD_CODE, node.body); | |
1669 return null; | |
1670 } | |
1671 } | |
1672 } | |
1673 _safelyVisit(node.body); | |
1674 return null; | |
1675 } | |
1676 | |
1677 /** | |
1678 * Given some [NodeList] of [Statement]s, from either a [Block] or | |
1679 * [SwitchMember], this loops through the list in reverse order searching for
statements | |
1680 * after a return, unlabeled break or unlabeled continue statement to mark the
m as dead code. | |
1681 * | |
1682 * @param statements some ordered list of statements in a [Block] or [SwitchMe
mber] | |
1683 */ | |
1684 void _checkForDeadStatementsInNodeList(NodeList<Statement> statements) { | |
1685 int size = statements.length; | |
1686 for (int i = 0; i < size; i++) { | |
1687 Statement currentStatement = statements[i]; | |
1688 _safelyVisit(currentStatement); | |
1689 bool returnOrBreakingStatement = currentStatement is ReturnStatement || | |
1690 (currentStatement is BreakStatement && | |
1691 currentStatement.label == null) || | |
1692 (currentStatement is ContinueStatement && | |
1693 currentStatement.label == null); | |
1694 if (returnOrBreakingStatement && i != size - 1) { | |
1695 Statement nextStatement = statements[i + 1]; | |
1696 Statement lastStatement = statements[size - 1]; | |
1697 int offset = nextStatement.offset; | |
1698 int length = lastStatement.end - offset; | |
1699 _errorReporter.reportErrorForOffset(HintCode.DEAD_CODE, offset, length); | |
1700 return; | |
1701 } | |
1702 } | |
1703 } | |
1704 | |
1705 /** | |
1706 * Given some [Expression], this method returns [ValidResult.RESULT_TRUE] if i
t is | |
1707 * `true`, [ValidResult.RESULT_FALSE] if it is `false`, or `null` if the | |
1708 * expression is not a constant boolean value. | |
1709 * | |
1710 * @param expression the expression to evaluate | |
1711 * @return [ValidResult.RESULT_TRUE] if it is `true`, [ValidResult.RESULT_FALS
E] | |
1712 * if it is `false`, or `null` if the expression is not a constant boo
lean | |
1713 * value | |
1714 */ | |
1715 EvaluationResultImpl _getConstantBooleanValue(Expression expression) { | |
1716 if (expression is BooleanLiteral) { | |
1717 if (expression.value) { | |
1718 return new EvaluationResultImpl( | |
1719 new DartObjectImpl(null, BoolState.from(true))); | |
1720 } else { | |
1721 return new EvaluationResultImpl( | |
1722 new DartObjectImpl(null, BoolState.from(false))); | |
1723 } | |
1724 } | |
1725 // Don't consider situations where we could evaluate to a constant boolean | |
1726 // expression with the ConstantVisitor | |
1727 // else { | |
1728 // EvaluationResultImpl result = expression.accept(new ConstantVisitor()); | |
1729 // if (result == ValidResult.RESULT_TRUE) { | |
1730 // return ValidResult.RESULT_TRUE; | |
1731 // } else if (result == ValidResult.RESULT_FALSE) { | |
1732 // return ValidResult.RESULT_FALSE; | |
1733 // } | |
1734 // return null; | |
1735 // } | |
1736 return null; | |
1737 } | |
1738 | |
1739 /** | |
1740 * Return `true` if and only if the passed expression is resolved to a constan
t variable. | |
1741 * | |
1742 * @param expression some conditional expression | |
1743 * @return `true` if and only if the passed expression is resolved to a consta
nt variable | |
1744 */ | |
1745 bool _isDebugConstant(Expression expression) { | |
1746 Element element = null; | |
1747 if (expression is Identifier) { | |
1748 Identifier identifier = expression; | |
1749 element = identifier.staticElement; | |
1750 } else if (expression is PropertyAccess) { | |
1751 PropertyAccess propertyAccess = expression; | |
1752 element = propertyAccess.propertyName.staticElement; | |
1753 } | |
1754 if (element is PropertyAccessorElement) { | |
1755 PropertyInducingElement variable = element.variable; | |
1756 return variable != null && variable.isConst; | |
1757 } | |
1758 return false; | |
1759 } | |
1760 | |
1761 /** | |
1762 * If the given node is not `null`, visit this instance of the dead code verif
ier. | |
1763 * | |
1764 * @param node the node to be visited | |
1765 */ | |
1766 void _safelyVisit(AstNode node) { | |
1767 if (node != null) { | |
1768 node.accept(this); | |
1769 } | |
1770 } | |
1771 } | |
1772 | |
1773 /** | |
1774 * Instances of the class `DeclarationResolver` are used to resolve declarations
in an AST | |
1775 * structure to already built elements. | |
1776 */ | |
1777 class DeclarationResolver extends RecursiveAstVisitor<Object> { | |
1778 /** | |
1779 * The compilation unit containing the AST nodes being visited. | |
1780 */ | |
1781 CompilationUnitElement _enclosingUnit; | |
1782 | |
1783 /** | |
1784 * The function type alias containing the AST nodes being visited, or `null` i
f we are not | |
1785 * in the scope of a function type alias. | |
1786 */ | |
1787 FunctionTypeAliasElement _enclosingAlias; | |
1788 | |
1789 /** | |
1790 * The class containing the AST nodes being visited, or `null` if we are not i
n the scope of | |
1791 * a class. | |
1792 */ | |
1793 ClassElement _enclosingClass; | |
1794 | |
1795 /** | |
1796 * The method or function containing the AST nodes being visited, or `null` if
we are not in | |
1797 * the scope of a method or function. | |
1798 */ | |
1799 ExecutableElement _enclosingExecutable; | |
1800 | |
1801 /** | |
1802 * The parameter containing the AST nodes being visited, or `null` if we are n
ot in the | |
1803 * scope of a parameter. | |
1804 */ | |
1805 ParameterElement _enclosingParameter; | |
1806 | |
1807 /** | |
1808 * Resolve the declarations within the given compilation unit to the elements
rooted at the given | |
1809 * element. | |
1810 * | |
1811 * @param unit the compilation unit to be resolved | |
1812 * @param element the root of the element model used to resolve the AST nodes | |
1813 */ | |
1814 void resolve(CompilationUnit unit, CompilationUnitElement element) { | |
1815 _enclosingUnit = element; | |
1816 unit.element = element; | |
1817 unit.accept(this); | |
1818 } | |
1819 | |
1820 @override | |
1821 Object visitCatchClause(CatchClause node) { | |
1822 SimpleIdentifier exceptionParameter = node.exceptionParameter; | |
1823 if (exceptionParameter != null) { | |
1824 List<LocalVariableElement> localVariables = | |
1825 _enclosingExecutable.localVariables; | |
1826 _findIdentifier(localVariables, exceptionParameter); | |
1827 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; | |
1828 if (stackTraceParameter != null) { | |
1829 _findIdentifier(localVariables, stackTraceParameter); | |
1830 } | |
1831 } | |
1832 return super.visitCatchClause(node); | |
1833 } | |
1834 | |
1835 @override | |
1836 Object visitClassDeclaration(ClassDeclaration node) { | |
1837 ClassElement outerClass = _enclosingClass; | |
1838 try { | |
1839 SimpleIdentifier className = node.name; | |
1840 _enclosingClass = _findIdentifier(_enclosingUnit.types, className); | |
1841 return super.visitClassDeclaration(node); | |
1842 } finally { | |
1843 _enclosingClass = outerClass; | |
1844 } | |
1845 } | |
1846 | |
1847 @override | |
1848 Object visitClassTypeAlias(ClassTypeAlias node) { | |
1849 ClassElement outerClass = _enclosingClass; | |
1850 try { | |
1851 SimpleIdentifier className = node.name; | |
1852 _enclosingClass = _findIdentifier(_enclosingUnit.types, className); | |
1853 return super.visitClassTypeAlias(node); | |
1854 } finally { | |
1855 _enclosingClass = outerClass; | |
1856 } | |
1857 } | |
1858 | |
1859 @override | |
1860 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
1861 ExecutableElement outerExecutable = _enclosingExecutable; | |
1862 try { | |
1863 SimpleIdentifier constructorName = node.name; | |
1864 if (constructorName == null) { | |
1865 _enclosingExecutable = _enclosingClass.unnamedConstructor; | |
1866 } else { | |
1867 _enclosingExecutable = | |
1868 _enclosingClass.getNamedConstructor(constructorName.name); | |
1869 constructorName.staticElement = _enclosingExecutable; | |
1870 } | |
1871 node.element = _enclosingExecutable as ConstructorElement; | |
1872 return super.visitConstructorDeclaration(node); | |
1873 } finally { | |
1874 _enclosingExecutable = outerExecutable; | |
1875 } | |
1876 } | |
1877 | |
1878 @override | |
1879 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
1880 SimpleIdentifier variableName = node.identifier; | |
1881 _findIdentifier(_enclosingExecutable.localVariables, variableName); | |
1882 return super.visitDeclaredIdentifier(node); | |
1883 } | |
1884 | |
1885 @override | |
1886 Object visitDefaultFormalParameter(DefaultFormalParameter node) { | |
1887 SimpleIdentifier parameterName = node.parameter.identifier; | |
1888 ParameterElement element = _getElementForParameter(node, parameterName); | |
1889 Expression defaultValue = node.defaultValue; | |
1890 if (defaultValue != null) { | |
1891 ExecutableElement outerExecutable = _enclosingExecutable; | |
1892 try { | |
1893 if (element == null) { | |
1894 // TODO(brianwilkerson) Report this internal error. | |
1895 } else { | |
1896 _enclosingExecutable = element.initializer; | |
1897 } | |
1898 defaultValue.accept(this); | |
1899 } finally { | |
1900 _enclosingExecutable = outerExecutable; | |
1901 } | |
1902 } | |
1903 ParameterElement outerParameter = _enclosingParameter; | |
1904 try { | |
1905 _enclosingParameter = element; | |
1906 return super.visitDefaultFormalParameter(node); | |
1907 } finally { | |
1908 _enclosingParameter = outerParameter; | |
1909 } | |
1910 } | |
1911 | |
1912 @override | |
1913 Object visitEnumDeclaration(EnumDeclaration node) { | |
1914 ClassElement enclosingEnum = | |
1915 _findIdentifier(_enclosingUnit.enums, node.name); | |
1916 List<FieldElement> constants = enclosingEnum.fields; | |
1917 for (EnumConstantDeclaration constant in node.constants) { | |
1918 _findIdentifier(constants, constant.name); | |
1919 } | |
1920 return super.visitEnumDeclaration(node); | |
1921 } | |
1922 | |
1923 @override | |
1924 Object visitExportDirective(ExportDirective node) { | |
1925 String uri = _getStringValue(node.uri); | |
1926 if (uri != null) { | |
1927 LibraryElement library = _enclosingUnit.library; | |
1928 ExportElement exportElement = _findExport(library.exports, | |
1929 _enclosingUnit.context.sourceFactory.resolveUri( | |
1930 _enclosingUnit.source, uri)); | |
1931 node.element = exportElement; | |
1932 } | |
1933 return super.visitExportDirective(node); | |
1934 } | |
1935 | |
1936 @override | |
1937 Object visitFieldFormalParameter(FieldFormalParameter node) { | |
1938 if (node.parent is! DefaultFormalParameter) { | |
1939 SimpleIdentifier parameterName = node.identifier; | |
1940 ParameterElement element = _getElementForParameter(node, parameterName); | |
1941 ParameterElement outerParameter = _enclosingParameter; | |
1942 try { | |
1943 _enclosingParameter = element; | |
1944 return super.visitFieldFormalParameter(node); | |
1945 } finally { | |
1946 _enclosingParameter = outerParameter; | |
1947 } | |
1948 } else { | |
1949 return super.visitFieldFormalParameter(node); | |
1950 } | |
1951 } | |
1952 | |
1953 @override | |
1954 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
1955 ExecutableElement outerExecutable = _enclosingExecutable; | |
1956 try { | |
1957 SimpleIdentifier functionName = node.name; | |
1958 sc.Token property = node.propertyKeyword; | |
1959 if (property == null) { | |
1960 if (_enclosingExecutable != null) { | |
1961 _enclosingExecutable = | |
1962 _findIdentifier(_enclosingExecutable.functions, functionName); | |
1963 } else { | |
1964 _enclosingExecutable = | |
1965 _findIdentifier(_enclosingUnit.functions, functionName); | |
1966 } | |
1967 } else { | |
1968 PropertyAccessorElement accessor = | |
1969 _findIdentifier(_enclosingUnit.accessors, functionName); | |
1970 if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) { | |
1971 accessor = accessor.variable.setter; | |
1972 functionName.staticElement = accessor; | |
1973 } | |
1974 _enclosingExecutable = accessor; | |
1975 } | |
1976 node.functionExpression.element = _enclosingExecutable; | |
1977 return super.visitFunctionDeclaration(node); | |
1978 } finally { | |
1979 _enclosingExecutable = outerExecutable; | |
1980 } | |
1981 } | |
1982 | |
1983 @override | |
1984 Object visitFunctionExpression(FunctionExpression node) { | |
1985 if (node.parent is! FunctionDeclaration) { | |
1986 FunctionElement element = | |
1987 _findAtOffset(_enclosingExecutable.functions, node.beginToken.offset); | |
1988 node.element = element; | |
1989 } | |
1990 ExecutableElement outerExecutable = _enclosingExecutable; | |
1991 try { | |
1992 _enclosingExecutable = node.element; | |
1993 return super.visitFunctionExpression(node); | |
1994 } finally { | |
1995 _enclosingExecutable = outerExecutable; | |
1996 } | |
1997 } | |
1998 | |
1999 @override | |
2000 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
2001 FunctionTypeAliasElement outerAlias = _enclosingAlias; | |
2002 try { | |
2003 SimpleIdentifier aliasName = node.name; | |
2004 _enclosingAlias = | |
2005 _findIdentifier(_enclosingUnit.functionTypeAliases, aliasName); | |
2006 return super.visitFunctionTypeAlias(node); | |
2007 } finally { | |
2008 _enclosingAlias = outerAlias; | |
2009 } | |
2010 } | |
2011 | |
2012 @override | |
2013 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
2014 if (node.parent is! DefaultFormalParameter) { | |
2015 SimpleIdentifier parameterName = node.identifier; | |
2016 ParameterElement element = _getElementForParameter(node, parameterName); | |
2017 ParameterElement outerParameter = _enclosingParameter; | |
2018 try { | |
2019 _enclosingParameter = element; | |
2020 return super.visitFunctionTypedFormalParameter(node); | |
2021 } finally { | |
2022 _enclosingParameter = outerParameter; | |
2023 } | |
2024 } else { | |
2025 return super.visitFunctionTypedFormalParameter(node); | |
2026 } | |
2027 } | |
2028 | |
2029 @override | |
2030 Object visitImportDirective(ImportDirective node) { | |
2031 String uri = _getStringValue(node.uri); | |
2032 if (uri != null) { | |
2033 LibraryElement library = _enclosingUnit.library; | |
2034 ImportElement importElement = _findImport(library.imports, | |
2035 _enclosingUnit.context.sourceFactory.resolveUri( | |
2036 _enclosingUnit.source, uri), node.prefix); | |
2037 node.element = importElement; | |
2038 } | |
2039 return super.visitImportDirective(node); | |
2040 } | |
2041 | |
2042 @override | |
2043 Object visitLabeledStatement(LabeledStatement node) { | |
2044 for (Label label in node.labels) { | |
2045 SimpleIdentifier labelName = label.label; | |
2046 _findIdentifier(_enclosingExecutable.labels, labelName); | |
2047 } | |
2048 return super.visitLabeledStatement(node); | |
2049 } | |
2050 | |
2051 @override | |
2052 Object visitLibraryDirective(LibraryDirective node) { | |
2053 node.element = _enclosingUnit.library; | |
2054 return super.visitLibraryDirective(node); | |
2055 } | |
2056 | |
2057 @override | |
2058 Object visitMethodDeclaration(MethodDeclaration node) { | |
2059 ExecutableElement outerExecutable = _enclosingExecutable; | |
2060 try { | |
2061 sc.Token property = node.propertyKeyword; | |
2062 SimpleIdentifier methodName = node.name; | |
2063 String nameOfMethod = methodName.name; | |
2064 if (property == null) { | |
2065 _enclosingExecutable = _findWithNameAndOffset( | |
2066 _enclosingClass.methods, nameOfMethod, methodName.offset); | |
2067 methodName.staticElement = _enclosingExecutable; | |
2068 } else { | |
2069 PropertyAccessorElement accessor = | |
2070 _findIdentifier(_enclosingClass.accessors, methodName); | |
2071 if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) { | |
2072 accessor = accessor.variable.setter; | |
2073 methodName.staticElement = accessor; | |
2074 } | |
2075 _enclosingExecutable = accessor; | |
2076 } | |
2077 return super.visitMethodDeclaration(node); | |
2078 } finally { | |
2079 _enclosingExecutable = outerExecutable; | |
2080 } | |
2081 } | |
2082 | |
2083 @override | |
2084 Object visitPartDirective(PartDirective node) { | |
2085 String uri = _getStringValue(node.uri); | |
2086 if (uri != null) { | |
2087 Source partSource = _enclosingUnit.context.sourceFactory.resolveUri( | |
2088 _enclosingUnit.source, uri); | |
2089 node.element = _findPart(_enclosingUnit.library.parts, partSource); | |
2090 } | |
2091 return super.visitPartDirective(node); | |
2092 } | |
2093 | |
2094 @override | |
2095 Object visitPartOfDirective(PartOfDirective node) { | |
2096 node.element = _enclosingUnit.library; | |
2097 return super.visitPartOfDirective(node); | |
2098 } | |
2099 | |
2100 @override | |
2101 Object visitSimpleFormalParameter(SimpleFormalParameter node) { | |
2102 if (node.parent is! DefaultFormalParameter) { | |
2103 SimpleIdentifier parameterName = node.identifier; | |
2104 ParameterElement element = _getElementForParameter(node, parameterName); | |
2105 ParameterElement outerParameter = _enclosingParameter; | |
2106 try { | |
2107 _enclosingParameter = element; | |
2108 return super.visitSimpleFormalParameter(node); | |
2109 } finally { | |
2110 _enclosingParameter = outerParameter; | |
2111 } | |
2112 } else {} | |
2113 return super.visitSimpleFormalParameter(node); | |
2114 } | |
2115 | |
2116 @override | |
2117 Object visitSwitchCase(SwitchCase node) { | |
2118 for (Label label in node.labels) { | |
2119 SimpleIdentifier labelName = label.label; | |
2120 _findIdentifier(_enclosingExecutable.labels, labelName); | |
2121 } | |
2122 return super.visitSwitchCase(node); | |
2123 } | |
2124 | |
2125 @override | |
2126 Object visitSwitchDefault(SwitchDefault node) { | |
2127 for (Label label in node.labels) { | |
2128 SimpleIdentifier labelName = label.label; | |
2129 _findIdentifier(_enclosingExecutable.labels, labelName); | |
2130 } | |
2131 return super.visitSwitchDefault(node); | |
2132 } | |
2133 | |
2134 @override | |
2135 Object visitTypeParameter(TypeParameter node) { | |
2136 SimpleIdentifier parameterName = node.name; | |
2137 if (_enclosingClass != null) { | |
2138 _findIdentifier(_enclosingClass.typeParameters, parameterName); | |
2139 } else if (_enclosingAlias != null) { | |
2140 _findIdentifier(_enclosingAlias.typeParameters, parameterName); | |
2141 } | |
2142 return super.visitTypeParameter(node); | |
2143 } | |
2144 | |
2145 @override | |
2146 Object visitVariableDeclaration(VariableDeclaration node) { | |
2147 VariableElement element = null; | |
2148 SimpleIdentifier variableName = node.name; | |
2149 if (_enclosingExecutable != null) { | |
2150 element = | |
2151 _findIdentifier(_enclosingExecutable.localVariables, variableName); | |
2152 } | |
2153 if (element == null && _enclosingClass != null) { | |
2154 element = _findIdentifier(_enclosingClass.fields, variableName); | |
2155 } | |
2156 if (element == null && _enclosingUnit != null) { | |
2157 element = _findIdentifier(_enclosingUnit.topLevelVariables, variableName); | |
2158 } | |
2159 Expression initializer = node.initializer; | |
2160 if (initializer != null) { | |
2161 ExecutableElement outerExecutable = _enclosingExecutable; | |
2162 try { | |
2163 if (element == null) { | |
2164 // TODO(brianwilkerson) Report this internal error. | |
2165 } else { | |
2166 _enclosingExecutable = element.initializer; | |
2167 } | |
2168 return super.visitVariableDeclaration(node); | |
2169 } finally { | |
2170 _enclosingExecutable = outerExecutable; | |
2171 } | |
2172 } | |
2173 return super.visitVariableDeclaration(node); | |
2174 } | |
2175 | |
2176 /** | |
2177 * Return the element in the given array of elements that was created for the
declaration at the | |
2178 * given offset. This method should only be used when there is no name | |
2179 * | |
2180 * @param elements the elements of the appropriate kind that exist in the curr
ent context | |
2181 * @param offset the offset of the name of the element to be returned | |
2182 * @return the element at the given offset | |
2183 */ | |
2184 Element _findAtOffset(List<Element> elements, int offset) => | |
2185 _findWithNameAndOffset(elements, "", offset); | |
2186 | |
2187 /** | |
2188 * Return the export element from the given array whose library has the given
source, or | |
2189 * `null` if there is no such export. | |
2190 * | |
2191 * @param exports the export elements being searched | |
2192 * @param source the source of the library associated with the export element
to being searched | |
2193 * for | |
2194 * @return the export element whose library has the given source | |
2195 */ | |
2196 ExportElement _findExport(List<ExportElement> exports, Source source) { | |
2197 for (ExportElement export in exports) { | |
2198 if (export.exportedLibrary.source == source) { | |
2199 return export; | |
2200 } | |
2201 } | |
2202 return null; | |
2203 } | |
2204 | |
2205 /** | |
2206 * Return the element in the given array of elements that was created for the
declaration with the | |
2207 * given name. | |
2208 * | |
2209 * @param elements the elements of the appropriate kind that exist in the curr
ent context | |
2210 * @param identifier the name node in the declaration of the element to be ret
urned | |
2211 * @return the element created for the declaration with the given name | |
2212 */ | |
2213 Element _findIdentifier(List<Element> elements, SimpleIdentifier identifier) { | |
2214 Element element = | |
2215 _findWithNameAndOffset(elements, identifier.name, identifier.offset); | |
2216 identifier.staticElement = element; | |
2217 return element; | |
2218 } | |
2219 | |
2220 /** | |
2221 * Return the import element from the given array whose library has the given
source and that has | |
2222 * the given prefix, or `null` if there is no such import. | |
2223 * | |
2224 * @param imports the import elements being searched | |
2225 * @param source the source of the library associated with the import element
to being searched | |
2226 * for | |
2227 * @param prefix the prefix with which the library was imported | |
2228 * @return the import element whose library has the given source and prefix | |
2229 */ | |
2230 ImportElement _findImport( | |
2231 List<ImportElement> imports, Source source, SimpleIdentifier prefix) { | |
2232 for (ImportElement element in imports) { | |
2233 if (element.importedLibrary.source == source) { | |
2234 PrefixElement prefixElement = element.prefix; | |
2235 if (prefix == null) { | |
2236 if (prefixElement == null) { | |
2237 return element; | |
2238 } | |
2239 } else { | |
2240 if (prefixElement != null && | |
2241 prefix.name == prefixElement.displayName) { | |
2242 return element; | |
2243 } | |
2244 } | |
2245 } | |
2246 } | |
2247 return null; | |
2248 } | |
2249 | |
2250 /** | |
2251 * Return the element for the part with the given source, or `null` if there i
s no element | |
2252 * for the given source. | |
2253 * | |
2254 * @param parts the elements for the parts | |
2255 * @param partSource the source for the part whose element is to be returned | |
2256 * @return the element for the part with the given source | |
2257 */ | |
2258 CompilationUnitElement _findPart( | |
2259 List<CompilationUnitElement> parts, Source partSource) { | |
2260 for (CompilationUnitElement part in parts) { | |
2261 if (part.source == partSource) { | |
2262 return part; | |
2263 } | |
2264 } | |
2265 return null; | |
2266 } | |
2267 | |
2268 /** | |
2269 * Return the element in the given array of elements that was created for the
declaration with the | |
2270 * given name at the given offset. | |
2271 * | |
2272 * @param elements the elements of the appropriate kind that exist in the curr
ent context | |
2273 * @param name the name of the element to be returned | |
2274 * @param offset the offset of the name of the element to be returned | |
2275 * @return the element with the given name and offset | |
2276 */ | |
2277 Element _findWithNameAndOffset( | |
2278 List<Element> elements, String name, int offset) { | |
2279 for (Element element in elements) { | |
2280 if (element.nameOffset == offset && element.displayName == name) { | |
2281 return element; | |
2282 } | |
2283 } | |
2284 return null; | |
2285 } | |
2286 | |
2287 /** | |
2288 * Search the most closely enclosing list of parameters for a parameter with t
he given name. | |
2289 * | |
2290 * @param node the node defining the parameter with the given name | |
2291 * @param parameterName the name of the parameter being searched for | |
2292 * @return the element representing the parameter with that name | |
2293 */ | |
2294 ParameterElement _getElementForParameter( | |
2295 FormalParameter node, SimpleIdentifier parameterName) { | |
2296 List<ParameterElement> parameters = null; | |
2297 if (_enclosingParameter != null) { | |
2298 parameters = _enclosingParameter.parameters; | |
2299 } | |
2300 if (parameters == null && _enclosingExecutable != null) { | |
2301 parameters = _enclosingExecutable.parameters; | |
2302 } | |
2303 if (parameters == null && _enclosingAlias != null) { | |
2304 parameters = _enclosingAlias.parameters; | |
2305 } | |
2306 ParameterElement element = | |
2307 parameters == null ? null : _findIdentifier(parameters, parameterName); | |
2308 if (element == null) { | |
2309 StringBuffer buffer = new StringBuffer(); | |
2310 buffer.writeln("Invalid state found in the Analysis Engine:"); | |
2311 buffer.writeln( | |
2312 "DeclarationResolver.getElementForParameter() is visiting a parameter
that does not appear to be in a method or function."); | |
2313 buffer.writeln("Ancestors:"); | |
2314 AstNode parent = node.parent; | |
2315 while (parent != null) { | |
2316 buffer.writeln(parent.runtimeType.toString()); | |
2317 buffer.writeln("---------"); | |
2318 parent = parent.parent; | |
2319 } | |
2320 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
2321 new CaughtException(new AnalysisException(), null)); | |
2322 } | |
2323 return element; | |
2324 } | |
2325 | |
2326 /** | |
2327 * Return the value of the given string literal, or `null` if the string is no
t a constant | |
2328 * string without any string interpolation. | |
2329 * | |
2330 * @param literal the string literal whose value is to be returned | |
2331 * @return the value of the given string literal | |
2332 */ | |
2333 String _getStringValue(StringLiteral literal) { | |
2334 if (literal is StringInterpolation) { | |
2335 return null; | |
2336 } | |
2337 return literal.stringValue; | |
2338 } | |
2339 } | |
2340 | |
2341 /** | |
2342 * Instances of the class `ElementBuilder` traverse an AST structure and build t
he element | |
2343 * model representing the AST structure. | |
2344 */ | |
2345 class ElementBuilder extends RecursiveAstVisitor<Object> { | |
2346 /** | |
2347 * The element holder associated with the element that is currently being buil
t. | |
2348 */ | |
2349 ElementHolder _currentHolder; | |
2350 | |
2351 /** | |
2352 * A flag indicating whether a variable declaration is in the context of a fie
ld declaration. | |
2353 */ | |
2354 bool _inFieldContext = false; | |
2355 | |
2356 /** | |
2357 * A flag indicating whether a variable declaration is within the body of a me
thod or function. | |
2358 */ | |
2359 bool _inFunction = false; | |
2360 | |
2361 /** | |
2362 * A flag indicating whether the class currently being visited can be used as
a mixin. | |
2363 */ | |
2364 bool _isValidMixin = false; | |
2365 | |
2366 /** | |
2367 * A collection holding the function types defined in a class that need to hav
e their type | |
2368 * arguments set to the types of the type parameters for the class, or `null`
if we are not | |
2369 * currently processing nodes within a class. | |
2370 */ | |
2371 List<FunctionTypeImpl> _functionTypesToFix = null; | |
2372 | |
2373 /** | |
2374 * A table mapping field names to field elements for the fields defined in the
current class, or | |
2375 * `null` if we are not in the scope of a class. | |
2376 */ | |
2377 HashMap<String, FieldElement> _fieldMap; | |
2378 | |
2379 /** | |
2380 * Initialize a newly created element builder to build the elements for a comp
ilation unit. | |
2381 * | |
2382 * @param initialHolder the element holder associated with the compilation uni
t being built | |
2383 */ | |
2384 ElementBuilder(ElementHolder initialHolder) { | |
2385 _currentHolder = initialHolder; | |
2386 } | |
2387 | |
2388 @override | |
2389 Object visitBlock(Block node) { | |
2390 bool wasInField = _inFieldContext; | |
2391 _inFieldContext = false; | |
2392 try { | |
2393 node.visitChildren(this); | |
2394 } finally { | |
2395 _inFieldContext = wasInField; | |
2396 } | |
2397 return null; | |
2398 } | |
2399 | |
2400 @override | |
2401 Object visitCatchClause(CatchClause node) { | |
2402 SimpleIdentifier exceptionParameter = node.exceptionParameter; | |
2403 if (exceptionParameter != null) { | |
2404 // exception | |
2405 LocalVariableElementImpl exception = | |
2406 new LocalVariableElementImpl.forNode(exceptionParameter); | |
2407 _currentHolder.addLocalVariable(exception); | |
2408 exceptionParameter.staticElement = exception; | |
2409 // stack trace | |
2410 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; | |
2411 if (stackTraceParameter != null) { | |
2412 LocalVariableElementImpl stackTrace = | |
2413 new LocalVariableElementImpl.forNode(stackTraceParameter); | |
2414 _currentHolder.addLocalVariable(stackTrace); | |
2415 stackTraceParameter.staticElement = stackTrace; | |
2416 } | |
2417 } | |
2418 return super.visitCatchClause(node); | |
2419 } | |
2420 | |
2421 @override | |
2422 Object visitClassDeclaration(ClassDeclaration node) { | |
2423 ElementHolder holder = new ElementHolder(); | |
2424 _isValidMixin = true; | |
2425 _functionTypesToFix = new List<FunctionTypeImpl>(); | |
2426 // | |
2427 // Process field declarations before constructors and methods so that field | |
2428 // formal parameters can be correctly resolved to their fields. | |
2429 // | |
2430 ElementHolder previousHolder = _currentHolder; | |
2431 _currentHolder = holder; | |
2432 try { | |
2433 List<ClassMember> nonFields = new List<ClassMember>(); | |
2434 node.visitChildren( | |
2435 new _ElementBuilder_visitClassDeclaration(this, nonFields)); | |
2436 _buildFieldMap(holder.fieldsWithoutFlushing); | |
2437 int count = nonFields.length; | |
2438 for (int i = 0; i < count; i++) { | |
2439 nonFields[i].accept(this); | |
2440 } | |
2441 } finally { | |
2442 _currentHolder = previousHolder; | |
2443 } | |
2444 SimpleIdentifier className = node.name; | |
2445 ClassElementImpl element = new ClassElementImpl.forNode(className); | |
2446 List<TypeParameterElement> typeParameters = holder.typeParameters; | |
2447 List<DartType> typeArguments = _createTypeParameterTypes(typeParameters); | |
2448 InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element); | |
2449 interfaceType.typeArguments = typeArguments; | |
2450 element.type = interfaceType; | |
2451 List<ConstructorElement> constructors = holder.constructors; | |
2452 if (constructors.length == 0) { | |
2453 // | |
2454 // Create the default constructor. | |
2455 // | |
2456 constructors = _createDefaultConstructors(interfaceType); | |
2457 } | |
2458 element.abstract = node.isAbstract; | |
2459 element.accessors = holder.accessors; | |
2460 element.constructors = constructors; | |
2461 element.fields = holder.fields; | |
2462 element.methods = holder.methods; | |
2463 element.typeParameters = typeParameters; | |
2464 element.validMixin = _isValidMixin; | |
2465 int functionTypeCount = _functionTypesToFix.length; | |
2466 for (int i = 0; i < functionTypeCount; i++) { | |
2467 _functionTypesToFix[i].typeArguments = typeArguments; | |
2468 } | |
2469 _functionTypesToFix = null; | |
2470 _currentHolder.addType(element); | |
2471 className.staticElement = element; | |
2472 _fieldMap = null; | |
2473 holder.validate(); | |
2474 return null; | |
2475 } | |
2476 | |
2477 /** | |
2478 * Implementation of this method should be synchronized with | |
2479 * [visitClassDeclaration]. | |
2480 */ | |
2481 void visitClassDeclarationIncrementally(ClassDeclaration node) { | |
2482 // | |
2483 // Process field declarations before constructors and methods so that field | |
2484 // formal parameters can be correctly resolved to their fields. | |
2485 // | |
2486 ClassElement classElement = node.element; | |
2487 _buildFieldMap(classElement.fields); | |
2488 } | |
2489 | |
2490 @override | |
2491 Object visitClassTypeAlias(ClassTypeAlias node) { | |
2492 ElementHolder holder = new ElementHolder(); | |
2493 _functionTypesToFix = new List<FunctionTypeImpl>(); | |
2494 _visitChildren(holder, node); | |
2495 SimpleIdentifier className = node.name; | |
2496 ClassElementImpl element = new ClassElementImpl.forNode(className); | |
2497 element.abstract = node.abstractKeyword != null; | |
2498 element.mixinApplication = true; | |
2499 List<TypeParameterElement> typeParameters = holder.typeParameters; | |
2500 element.typeParameters = typeParameters; | |
2501 List<DartType> typeArguments = _createTypeParameterTypes(typeParameters); | |
2502 InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element); | |
2503 interfaceType.typeArguments = typeArguments; | |
2504 element.type = interfaceType; | |
2505 // set default constructor | |
2506 for (FunctionTypeImpl functionType in _functionTypesToFix) { | |
2507 functionType.typeArguments = typeArguments; | |
2508 } | |
2509 _functionTypesToFix = null; | |
2510 _currentHolder.addType(element); | |
2511 className.staticElement = element; | |
2512 holder.validate(); | |
2513 return null; | |
2514 } | |
2515 | |
2516 @override | |
2517 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
2518 _isValidMixin = false; | |
2519 ElementHolder holder = new ElementHolder(); | |
2520 bool wasInFunction = _inFunction; | |
2521 _inFunction = true; | |
2522 try { | |
2523 _visitChildren(holder, node); | |
2524 } finally { | |
2525 _inFunction = wasInFunction; | |
2526 } | |
2527 FunctionBody body = node.body; | |
2528 SimpleIdentifier constructorName = node.name; | |
2529 ConstructorElementImpl element = | |
2530 new ConstructorElementImpl.forNode(constructorName); | |
2531 if (node.externalKeyword != null) { | |
2532 element.external = true; | |
2533 } | |
2534 if (node.factoryKeyword != null) { | |
2535 element.factory = true; | |
2536 } | |
2537 element.functions = holder.functions; | |
2538 element.labels = holder.labels; | |
2539 element.localVariables = holder.localVariables; | |
2540 element.parameters = holder.parameters; | |
2541 element.const2 = node.constKeyword != null; | |
2542 if (body.isAsynchronous) { | |
2543 element.asynchronous = true; | |
2544 } | |
2545 if (body.isGenerator) { | |
2546 element.generator = true; | |
2547 } | |
2548 _currentHolder.addConstructor(element); | |
2549 node.element = element; | |
2550 if (constructorName == null) { | |
2551 Identifier returnType = node.returnType; | |
2552 if (returnType != null) { | |
2553 element.nameOffset = returnType.offset; | |
2554 element.nameEnd = returnType.end; | |
2555 } | |
2556 } else { | |
2557 constructorName.staticElement = element; | |
2558 element.periodOffset = node.period.offset; | |
2559 element.nameEnd = constructorName.end; | |
2560 } | |
2561 holder.validate(); | |
2562 return null; | |
2563 } | |
2564 | |
2565 @override | |
2566 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
2567 SimpleIdentifier variableName = node.identifier; | |
2568 LocalVariableElementImpl element = | |
2569 new LocalVariableElementImpl.forNode(variableName); | |
2570 ForEachStatement statement = node.parent as ForEachStatement; | |
2571 int declarationEnd = node.offset + node.length; | |
2572 int statementEnd = statement.offset + statement.length; | |
2573 element.setVisibleRange(declarationEnd, statementEnd - declarationEnd - 1); | |
2574 element.const3 = node.isConst; | |
2575 element.final2 = node.isFinal; | |
2576 _currentHolder.addLocalVariable(element); | |
2577 variableName.staticElement = element; | |
2578 return super.visitDeclaredIdentifier(node); | |
2579 } | |
2580 | |
2581 @override | |
2582 Object visitDefaultFormalParameter(DefaultFormalParameter node) { | |
2583 ElementHolder holder = new ElementHolder(); | |
2584 NormalFormalParameter normalParameter = node.parameter; | |
2585 SimpleIdentifier parameterName = normalParameter.identifier; | |
2586 ParameterElementImpl parameter; | |
2587 if (normalParameter is FieldFormalParameter) { | |
2588 parameter = new DefaultFieldFormalParameterElementImpl(parameterName); | |
2589 FieldElement field = | |
2590 _fieldMap == null ? null : _fieldMap[parameterName.name]; | |
2591 if (field != null) { | |
2592 (parameter as DefaultFieldFormalParameterElementImpl).field = field; | |
2593 } | |
2594 } else { | |
2595 parameter = new DefaultParameterElementImpl(parameterName); | |
2596 } | |
2597 parameter.const3 = node.isConst; | |
2598 parameter.final2 = node.isFinal; | |
2599 parameter.parameterKind = node.kind; | |
2600 // set initializer, default value range | |
2601 Expression defaultValue = node.defaultValue; | |
2602 if (defaultValue != null) { | |
2603 _visit(holder, defaultValue); | |
2604 FunctionElementImpl initializer = | |
2605 new FunctionElementImpl.forOffset(defaultValue.beginToken.offset); | |
2606 initializer.functions = holder.functions; | |
2607 initializer.labels = holder.labels; | |
2608 initializer.localVariables = holder.localVariables; | |
2609 initializer.parameters = holder.parameters; | |
2610 initializer.synthetic = true; | |
2611 parameter.initializer = initializer; | |
2612 parameter.defaultValueCode = defaultValue.toSource(); | |
2613 } | |
2614 // visible range | |
2615 _setParameterVisibleRange(node, parameter); | |
2616 _currentHolder.addParameter(parameter); | |
2617 parameterName.staticElement = parameter; | |
2618 normalParameter.accept(this); | |
2619 holder.validate(); | |
2620 return null; | |
2621 } | |
2622 | |
2623 @override | |
2624 Object visitEnumDeclaration(EnumDeclaration node) { | |
2625 SimpleIdentifier enumName = node.name; | |
2626 ClassElementImpl enumElement = new ClassElementImpl.forNode(enumName); | |
2627 enumElement.enum2 = true; | |
2628 InterfaceTypeImpl enumType = new InterfaceTypeImpl(enumElement); | |
2629 enumElement.type = enumType; | |
2630 // The equivalent code for enums in the spec shows a single constructor, | |
2631 // but that constructor is not callable (since it is a compile-time error | |
2632 // to subclass, mix-in, implement, or explicitly instantiate an enum). So | |
2633 // we represent this as having no constructors. | |
2634 enumElement.constructors = ConstructorElement.EMPTY_LIST; | |
2635 _currentHolder.addEnum(enumElement); | |
2636 enumName.staticElement = enumElement; | |
2637 return super.visitEnumDeclaration(node); | |
2638 } | |
2639 | |
2640 @override | |
2641 Object visitFieldDeclaration(FieldDeclaration node) { | |
2642 bool wasInField = _inFieldContext; | |
2643 _inFieldContext = true; | |
2644 try { | |
2645 node.visitChildren(this); | |
2646 } finally { | |
2647 _inFieldContext = wasInField; | |
2648 } | |
2649 return null; | |
2650 } | |
2651 | |
2652 @override | |
2653 Object visitFieldFormalParameter(FieldFormalParameter node) { | |
2654 if (node.parent is! DefaultFormalParameter) { | |
2655 SimpleIdentifier parameterName = node.identifier; | |
2656 FieldElement field = | |
2657 _fieldMap == null ? null : _fieldMap[parameterName.name]; | |
2658 FieldFormalParameterElementImpl parameter = | |
2659 new FieldFormalParameterElementImpl(parameterName); | |
2660 parameter.const3 = node.isConst; | |
2661 parameter.final2 = node.isFinal; | |
2662 parameter.parameterKind = node.kind; | |
2663 if (field != null) { | |
2664 parameter.field = field; | |
2665 } | |
2666 _currentHolder.addParameter(parameter); | |
2667 parameterName.staticElement = parameter; | |
2668 } | |
2669 // | |
2670 // The children of this parameter include any parameters defined on the type | |
2671 // of this parameter. | |
2672 // | |
2673 ElementHolder holder = new ElementHolder(); | |
2674 _visitChildren(holder, node); | |
2675 ParameterElementImpl element = node.element; | |
2676 element.parameters = holder.parameters; | |
2677 element.typeParameters = holder.typeParameters; | |
2678 holder.validate(); | |
2679 return null; | |
2680 } | |
2681 | |
2682 @override | |
2683 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
2684 FunctionExpression expression = node.functionExpression; | |
2685 if (expression != null) { | |
2686 ElementHolder holder = new ElementHolder(); | |
2687 bool wasInFunction = _inFunction; | |
2688 _inFunction = true; | |
2689 try { | |
2690 _visitChildren(holder, node); | |
2691 } finally { | |
2692 _inFunction = wasInFunction; | |
2693 } | |
2694 FunctionBody body = expression.body; | |
2695 sc.Token property = node.propertyKeyword; | |
2696 if (property == null || _inFunction) { | |
2697 SimpleIdentifier functionName = node.name; | |
2698 FunctionElementImpl element = | |
2699 new FunctionElementImpl.forNode(functionName); | |
2700 if (node.externalKeyword != null) { | |
2701 element.external = true; | |
2702 } | |
2703 element.functions = holder.functions; | |
2704 element.labels = holder.labels; | |
2705 element.localVariables = holder.localVariables; | |
2706 element.parameters = holder.parameters; | |
2707 element.typeParameters = holder.typeParameters; | |
2708 if (body.isAsynchronous) { | |
2709 element.asynchronous = true; | |
2710 } | |
2711 if (body.isGenerator) { | |
2712 element.generator = true; | |
2713 } | |
2714 if (_inFunction) { | |
2715 Block enclosingBlock = node.getAncestor((node) => node is Block); | |
2716 if (enclosingBlock != null) { | |
2717 int functionEnd = node.offset + node.length; | |
2718 int blockEnd = enclosingBlock.offset + enclosingBlock.length; | |
2719 element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); | |
2720 } | |
2721 } | |
2722 _currentHolder.addFunction(element); | |
2723 expression.element = element; | |
2724 functionName.staticElement = element; | |
2725 } else { | |
2726 SimpleIdentifier propertyNameNode = node.name; | |
2727 if (propertyNameNode == null) { | |
2728 // TODO(brianwilkerson) Report this internal error. | |
2729 return null; | |
2730 } | |
2731 String propertyName = propertyNameNode.name; | |
2732 TopLevelVariableElementImpl variable = _currentHolder | |
2733 .getTopLevelVariable(propertyName) as TopLevelVariableElementImpl; | |
2734 if (variable == null) { | |
2735 variable = new TopLevelVariableElementImpl(node.name.name, -1); | |
2736 variable.final2 = true; | |
2737 variable.synthetic = true; | |
2738 _currentHolder.addTopLevelVariable(variable); | |
2739 } | |
2740 if (node.isGetter) { | |
2741 PropertyAccessorElementImpl getter = | |
2742 new PropertyAccessorElementImpl.forNode(propertyNameNode); | |
2743 if (node.externalKeyword != null) { | |
2744 getter.external = true; | |
2745 } | |
2746 getter.functions = holder.functions; | |
2747 getter.labels = holder.labels; | |
2748 getter.localVariables = holder.localVariables; | |
2749 if (body.isAsynchronous) { | |
2750 getter.asynchronous = true; | |
2751 } | |
2752 if (body.isGenerator) { | |
2753 getter.generator = true; | |
2754 } | |
2755 getter.variable = variable; | |
2756 getter.getter = true; | |
2757 getter.static = true; | |
2758 variable.getter = getter; | |
2759 _currentHolder.addAccessor(getter); | |
2760 expression.element = getter; | |
2761 propertyNameNode.staticElement = getter; | |
2762 } else { | |
2763 PropertyAccessorElementImpl setter = | |
2764 new PropertyAccessorElementImpl.forNode(propertyNameNode); | |
2765 if (node.externalKeyword != null) { | |
2766 setter.external = true; | |
2767 } | |
2768 setter.functions = holder.functions; | |
2769 setter.labels = holder.labels; | |
2770 setter.localVariables = holder.localVariables; | |
2771 setter.parameters = holder.parameters; | |
2772 if (body.isAsynchronous) { | |
2773 setter.asynchronous = true; | |
2774 } | |
2775 if (body.isGenerator) { | |
2776 setter.generator = true; | |
2777 } | |
2778 setter.variable = variable; | |
2779 setter.setter = true; | |
2780 setter.static = true; | |
2781 variable.setter = setter; | |
2782 variable.final2 = false; | |
2783 _currentHolder.addAccessor(setter); | |
2784 expression.element = setter; | |
2785 propertyNameNode.staticElement = setter; | |
2786 } | |
2787 } | |
2788 holder.validate(); | |
2789 } | |
2790 return null; | |
2791 } | |
2792 | |
2793 @override | |
2794 Object visitFunctionExpression(FunctionExpression node) { | |
2795 if (node.parent is FunctionDeclaration) { | |
2796 // visitFunctionDeclaration has already created the element for the | |
2797 // declaration. We just need to visit children. | |
2798 return super.visitFunctionExpression(node); | |
2799 } | |
2800 ElementHolder holder = new ElementHolder(); | |
2801 bool wasInFunction = _inFunction; | |
2802 _inFunction = true; | |
2803 try { | |
2804 _visitChildren(holder, node); | |
2805 } finally { | |
2806 _inFunction = wasInFunction; | |
2807 } | |
2808 FunctionBody body = node.body; | |
2809 FunctionElementImpl element = | |
2810 new FunctionElementImpl.forOffset(node.beginToken.offset); | |
2811 element.functions = holder.functions; | |
2812 element.labels = holder.labels; | |
2813 element.localVariables = holder.localVariables; | |
2814 element.parameters = holder.parameters; | |
2815 element.typeParameters = holder.typeParameters; | |
2816 if (body.isAsynchronous) { | |
2817 element.asynchronous = true; | |
2818 } | |
2819 if (body.isGenerator) { | |
2820 element.generator = true; | |
2821 } | |
2822 if (_inFunction) { | |
2823 Block enclosingBlock = node.getAncestor((node) => node is Block); | |
2824 if (enclosingBlock != null) { | |
2825 int functionEnd = node.offset + node.length; | |
2826 int blockEnd = enclosingBlock.offset + enclosingBlock.length; | |
2827 element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); | |
2828 } | |
2829 } | |
2830 FunctionTypeImpl type = new FunctionTypeImpl(element); | |
2831 if (_functionTypesToFix != null) { | |
2832 _functionTypesToFix.add(type); | |
2833 } | |
2834 element.type = type; | |
2835 _currentHolder.addFunction(element); | |
2836 node.element = element; | |
2837 holder.validate(); | |
2838 return null; | |
2839 } | |
2840 | |
2841 @override | |
2842 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
2843 ElementHolder holder = new ElementHolder(); | |
2844 _visitChildren(holder, node); | |
2845 SimpleIdentifier aliasName = node.name; | |
2846 List<ParameterElement> parameters = holder.parameters; | |
2847 List<TypeParameterElement> typeParameters = holder.typeParameters; | |
2848 FunctionTypeAliasElementImpl element = | |
2849 new FunctionTypeAliasElementImpl.forNode(aliasName); | |
2850 element.parameters = parameters; | |
2851 element.typeParameters = typeParameters; | |
2852 FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(element); | |
2853 type.typeArguments = _createTypeParameterTypes(typeParameters); | |
2854 element.type = type; | |
2855 _currentHolder.addTypeAlias(element); | |
2856 aliasName.staticElement = element; | |
2857 holder.validate(); | |
2858 return null; | |
2859 } | |
2860 | |
2861 @override | |
2862 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
2863 if (node.parent is! DefaultFormalParameter) { | |
2864 SimpleIdentifier parameterName = node.identifier; | |
2865 ParameterElementImpl parameter = | |
2866 new ParameterElementImpl.forNode(parameterName); | |
2867 parameter.parameterKind = node.kind; | |
2868 _setParameterVisibleRange(node, parameter); | |
2869 _currentHolder.addParameter(parameter); | |
2870 parameterName.staticElement = parameter; | |
2871 } | |
2872 // | |
2873 // The children of this parameter include any parameters defined on the type | |
2874 //of this parameter. | |
2875 // | |
2876 ElementHolder holder = new ElementHolder(); | |
2877 _visitChildren(holder, node); | |
2878 ParameterElementImpl element = node.element; | |
2879 element.parameters = holder.parameters; | |
2880 element.typeParameters = holder.typeParameters; | |
2881 holder.validate(); | |
2882 return null; | |
2883 } | |
2884 | |
2885 @override | |
2886 Object visitLabeledStatement(LabeledStatement node) { | |
2887 bool onSwitchStatement = node.statement is SwitchStatement; | |
2888 for (Label label in node.labels) { | |
2889 SimpleIdentifier labelName = label.label; | |
2890 LabelElementImpl element = | |
2891 new LabelElementImpl(labelName, onSwitchStatement, false); | |
2892 _currentHolder.addLabel(element); | |
2893 labelName.staticElement = element; | |
2894 } | |
2895 return super.visitLabeledStatement(node); | |
2896 } | |
2897 | |
2898 @override | |
2899 Object visitMethodDeclaration(MethodDeclaration node) { | |
2900 try { | |
2901 ElementHolder holder = new ElementHolder(); | |
2902 bool wasInFunction = _inFunction; | |
2903 _inFunction = true; | |
2904 try { | |
2905 _visitChildren(holder, node); | |
2906 } finally { | |
2907 _inFunction = wasInFunction; | |
2908 } | |
2909 bool isStatic = node.isStatic; | |
2910 sc.Token property = node.propertyKeyword; | |
2911 FunctionBody body = node.body; | |
2912 if (property == null) { | |
2913 SimpleIdentifier methodName = node.name; | |
2914 String nameOfMethod = methodName.name; | |
2915 if (nameOfMethod == sc.TokenType.MINUS.lexeme && | |
2916 node.parameters.parameters.length == 0) { | |
2917 nameOfMethod = "unary-"; | |
2918 } | |
2919 MethodElementImpl element = | |
2920 new MethodElementImpl(nameOfMethod, methodName.offset); | |
2921 element.abstract = node.isAbstract; | |
2922 if (node.externalKeyword != null) { | |
2923 element.external = true; | |
2924 } | |
2925 element.functions = holder.functions; | |
2926 element.labels = holder.labels; | |
2927 element.localVariables = holder.localVariables; | |
2928 element.parameters = holder.parameters; | |
2929 element.static = isStatic; | |
2930 element.typeParameters = holder.typeParameters; | |
2931 if (body.isAsynchronous) { | |
2932 element.asynchronous = true; | |
2933 } | |
2934 if (body.isGenerator) { | |
2935 element.generator = true; | |
2936 } | |
2937 _currentHolder.addMethod(element); | |
2938 methodName.staticElement = element; | |
2939 } else { | |
2940 SimpleIdentifier propertyNameNode = node.name; | |
2941 String propertyName = propertyNameNode.name; | |
2942 FieldElementImpl field = | |
2943 _currentHolder.getField(propertyName) as FieldElementImpl; | |
2944 if (field == null) { | |
2945 field = new FieldElementImpl(node.name.name, -1); | |
2946 field.final2 = true; | |
2947 field.static = isStatic; | |
2948 field.synthetic = true; | |
2949 _currentHolder.addField(field); | |
2950 } | |
2951 if (node.isGetter) { | |
2952 PropertyAccessorElementImpl getter = | |
2953 new PropertyAccessorElementImpl.forNode(propertyNameNode); | |
2954 if (node.externalKeyword != null) { | |
2955 getter.external = true; | |
2956 } | |
2957 getter.functions = holder.functions; | |
2958 getter.labels = holder.labels; | |
2959 getter.localVariables = holder.localVariables; | |
2960 if (body.isAsynchronous) { | |
2961 getter.asynchronous = true; | |
2962 } | |
2963 if (body.isGenerator) { | |
2964 getter.generator = true; | |
2965 } | |
2966 getter.variable = field; | |
2967 getter.abstract = node.isAbstract; | |
2968 getter.getter = true; | |
2969 getter.static = isStatic; | |
2970 field.getter = getter; | |
2971 _currentHolder.addAccessor(getter); | |
2972 propertyNameNode.staticElement = getter; | |
2973 } else { | |
2974 PropertyAccessorElementImpl setter = | |
2975 new PropertyAccessorElementImpl.forNode(propertyNameNode); | |
2976 if (node.externalKeyword != null) { | |
2977 setter.external = true; | |
2978 } | |
2979 setter.functions = holder.functions; | |
2980 setter.labels = holder.labels; | |
2981 setter.localVariables = holder.localVariables; | |
2982 setter.parameters = holder.parameters; | |
2983 if (body.isAsynchronous) { | |
2984 setter.asynchronous = true; | |
2985 } | |
2986 if (body.isGenerator) { | |
2987 setter.generator = true; | |
2988 } | |
2989 setter.variable = field; | |
2990 setter.abstract = node.isAbstract; | |
2991 setter.setter = true; | |
2992 setter.static = isStatic; | |
2993 field.setter = setter; | |
2994 field.final2 = false; | |
2995 _currentHolder.addAccessor(setter); | |
2996 propertyNameNode.staticElement = setter; | |
2997 } | |
2998 } | |
2999 holder.validate(); | |
3000 } catch (exception, stackTrace) { | |
3001 if (node.name.staticElement == null) { | |
3002 ClassDeclaration classNode = | |
3003 node.getAncestor((node) => node is ClassDeclaration); | |
3004 StringBuffer buffer = new StringBuffer(); | |
3005 buffer.write("The element for the method "); | |
3006 buffer.write(node.name); | |
3007 buffer.write(" in "); | |
3008 buffer.write(classNode.name); | |
3009 buffer.write(" was not set while trying to build the element model."); | |
3010 AnalysisEngine.instance.logger.logError( | |
3011 buffer.toString(), new CaughtException(exception, stackTrace)); | |
3012 } else { | |
3013 String message = | |
3014 "Exception caught in ElementBuilder.visitMethodDeclaration()"; | |
3015 AnalysisEngine.instance.logger.logError( | |
3016 message, new CaughtException(exception, stackTrace)); | |
3017 } | |
3018 } finally { | |
3019 if (node.name.staticElement == null) { | |
3020 ClassDeclaration classNode = | |
3021 node.getAncestor((node) => node is ClassDeclaration); | |
3022 StringBuffer buffer = new StringBuffer(); | |
3023 buffer.write("The element for the method "); | |
3024 buffer.write(node.name); | |
3025 buffer.write(" in "); | |
3026 buffer.write(classNode.name); | |
3027 buffer.write(" was not set while trying to resolve types."); | |
3028 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
3029 new CaughtException( | |
3030 new AnalysisException(buffer.toString()), null)); | |
3031 } | |
3032 } | |
3033 return null; | |
3034 } | |
3035 | |
3036 @override | |
3037 Object visitSimpleFormalParameter(SimpleFormalParameter node) { | |
3038 if (node.parent is! DefaultFormalParameter) { | |
3039 SimpleIdentifier parameterName = node.identifier; | |
3040 ParameterElementImpl parameter = | |
3041 new ParameterElementImpl.forNode(parameterName); | |
3042 parameter.const3 = node.isConst; | |
3043 parameter.final2 = node.isFinal; | |
3044 parameter.parameterKind = node.kind; | |
3045 _setParameterVisibleRange(node, parameter); | |
3046 _currentHolder.addParameter(parameter); | |
3047 parameterName.staticElement = parameter; | |
3048 } | |
3049 return super.visitSimpleFormalParameter(node); | |
3050 } | |
3051 | |
3052 @override | |
3053 Object visitSuperExpression(SuperExpression node) { | |
3054 _isValidMixin = false; | |
3055 return super.visitSuperExpression(node); | |
3056 } | |
3057 | |
3058 @override | |
3059 Object visitSwitchCase(SwitchCase node) { | |
3060 for (Label label in node.labels) { | |
3061 SimpleIdentifier labelName = label.label; | |
3062 LabelElementImpl element = new LabelElementImpl(labelName, false, true); | |
3063 _currentHolder.addLabel(element); | |
3064 labelName.staticElement = element; | |
3065 } | |
3066 return super.visitSwitchCase(node); | |
3067 } | |
3068 | |
3069 @override | |
3070 Object visitSwitchDefault(SwitchDefault node) { | |
3071 for (Label label in node.labels) { | |
3072 SimpleIdentifier labelName = label.label; | |
3073 LabelElementImpl element = new LabelElementImpl(labelName, false, true); | |
3074 _currentHolder.addLabel(element); | |
3075 labelName.staticElement = element; | |
3076 } | |
3077 return super.visitSwitchDefault(node); | |
3078 } | |
3079 | |
3080 @override | |
3081 Object visitTypeParameter(TypeParameter node) { | |
3082 SimpleIdentifier parameterName = node.name; | |
3083 TypeParameterElementImpl typeParameter = | |
3084 new TypeParameterElementImpl.forNode(parameterName); | |
3085 TypeParameterTypeImpl typeParameterType = | |
3086 new TypeParameterTypeImpl(typeParameter); | |
3087 typeParameter.type = typeParameterType; | |
3088 _currentHolder.addTypeParameter(typeParameter); | |
3089 parameterName.staticElement = typeParameter; | |
3090 return super.visitTypeParameter(node); | |
3091 } | |
3092 | |
3093 @override | |
3094 Object visitVariableDeclaration(VariableDeclaration node) { | |
3095 bool isConst = node.isConst; | |
3096 bool isFinal = node.isFinal; | |
3097 bool hasInitializer = node.initializer != null; | |
3098 VariableElementImpl element; | |
3099 if (_inFieldContext) { | |
3100 SimpleIdentifier fieldName = node.name; | |
3101 FieldElementImpl field; | |
3102 if ((isConst || isFinal) && hasInitializer) { | |
3103 field = new ConstFieldElementImpl.forNode(fieldName); | |
3104 } else { | |
3105 field = new FieldElementImpl.forNode(fieldName); | |
3106 } | |
3107 element = field; | |
3108 _currentHolder.addField(field); | |
3109 fieldName.staticElement = field; | |
3110 } else if (_inFunction) { | |
3111 SimpleIdentifier variableName = node.name; | |
3112 LocalVariableElementImpl variable; | |
3113 if (isConst && hasInitializer) { | |
3114 variable = new ConstLocalVariableElementImpl.forNode(variableName); | |
3115 } else { | |
3116 variable = new LocalVariableElementImpl.forNode(variableName); | |
3117 } | |
3118 element = variable; | |
3119 Block enclosingBlock = node.getAncestor((node) => node is Block); | |
3120 // TODO(brianwilkerson) This isn't right for variables declared in a for | |
3121 // loop. | |
3122 variable.setVisibleRange(enclosingBlock.offset, enclosingBlock.length); | |
3123 _currentHolder.addLocalVariable(variable); | |
3124 variableName.staticElement = element; | |
3125 } else { | |
3126 SimpleIdentifier variableName = node.name; | |
3127 TopLevelVariableElementImpl variable; | |
3128 if (isConst && hasInitializer) { | |
3129 variable = new ConstTopLevelVariableElementImpl(variableName); | |
3130 } else { | |
3131 variable = new TopLevelVariableElementImpl.forNode(variableName); | |
3132 } | |
3133 element = variable; | |
3134 _currentHolder.addTopLevelVariable(variable); | |
3135 variableName.staticElement = element; | |
3136 } | |
3137 element.const3 = isConst; | |
3138 element.final2 = isFinal; | |
3139 if (hasInitializer) { | |
3140 ElementHolder holder = new ElementHolder(); | |
3141 bool wasInFieldContext = _inFieldContext; | |
3142 _inFieldContext = false; | |
3143 try { | |
3144 _visit(holder, node.initializer); | |
3145 } finally { | |
3146 _inFieldContext = wasInFieldContext; | |
3147 } | |
3148 FunctionElementImpl initializer = | |
3149 new FunctionElementImpl.forOffset(node.initializer.beginToken.offset); | |
3150 initializer.functions = holder.functions; | |
3151 initializer.labels = holder.labels; | |
3152 initializer.localVariables = holder.localVariables; | |
3153 initializer.synthetic = true; | |
3154 element.initializer = initializer; | |
3155 holder.validate(); | |
3156 } | |
3157 if (element is PropertyInducingElementImpl) { | |
3158 if (_inFieldContext) { | |
3159 (element as FieldElementImpl).static = | |
3160 (node.parent.parent as FieldDeclaration).isStatic; | |
3161 } | |
3162 PropertyAccessorElementImpl getter = | |
3163 new PropertyAccessorElementImpl.forVariable(element); | |
3164 getter.getter = true; | |
3165 _currentHolder.addAccessor(getter); | |
3166 element.getter = getter; | |
3167 if (!isConst && !isFinal) { | |
3168 PropertyAccessorElementImpl setter = | |
3169 new PropertyAccessorElementImpl.forVariable(element); | |
3170 setter.setter = true; | |
3171 ParameterElementImpl parameter = | |
3172 new ParameterElementImpl("_${element.name}", element.nameOffset); | |
3173 parameter.synthetic = true; | |
3174 parameter.parameterKind = ParameterKind.REQUIRED; | |
3175 setter.parameters = <ParameterElement>[parameter]; | |
3176 _currentHolder.addAccessor(setter); | |
3177 element.setter = setter; | |
3178 } | |
3179 } | |
3180 return null; | |
3181 } | |
3182 | |
3183 /** | |
3184 * Build the table mapping field names to field elements for the fields define
d in the current | |
3185 * class. | |
3186 * | |
3187 * @param fields the field elements defined in the current class | |
3188 */ | |
3189 void _buildFieldMap(List<FieldElement> fields) { | |
3190 _fieldMap = new HashMap<String, FieldElement>(); | |
3191 int count = fields.length; | |
3192 for (int i = 0; i < count; i++) { | |
3193 FieldElement field = fields[i]; | |
3194 _fieldMap[field.name] = field; | |
3195 } | |
3196 } | |
3197 | |
3198 /** | |
3199 * Creates the [ConstructorElement]s array with the single default constructor
element. | |
3200 * | |
3201 * @param interfaceType the interface type for which to create a default const
ructor | |
3202 * @return the [ConstructorElement]s array with the single default constructor
element | |
3203 */ | |
3204 List<ConstructorElement> _createDefaultConstructors( | |
3205 InterfaceTypeImpl interfaceType) { | |
3206 ConstructorElementImpl constructor = | |
3207 new ConstructorElementImpl.forNode(null); | |
3208 constructor.synthetic = true; | |
3209 constructor.returnType = interfaceType; | |
3210 FunctionTypeImpl type = new FunctionTypeImpl(constructor); | |
3211 _functionTypesToFix.add(type); | |
3212 constructor.type = type; | |
3213 return <ConstructorElement>[constructor]; | |
3214 } | |
3215 | |
3216 /** | |
3217 * Create the types associated with the given type parameters, setting the typ
e of each type | |
3218 * parameter, and return an array of types corresponding to the given paramete
rs. | |
3219 * | |
3220 * @param typeParameters the type parameters for which types are to be created | |
3221 * @return an array of types corresponding to the given parameters | |
3222 */ | |
3223 List<DartType> _createTypeParameterTypes( | |
3224 List<TypeParameterElement> typeParameters) { | |
3225 int typeParameterCount = typeParameters.length; | |
3226 List<DartType> typeArguments = new List<DartType>(typeParameterCount); | |
3227 for (int i = 0; i < typeParameterCount; i++) { | |
3228 TypeParameterElementImpl typeParameter = | |
3229 typeParameters[i] as TypeParameterElementImpl; | |
3230 TypeParameterTypeImpl typeParameterType = | |
3231 new TypeParameterTypeImpl(typeParameter); | |
3232 typeParameter.type = typeParameterType; | |
3233 typeArguments[i] = typeParameterType; | |
3234 } | |
3235 return typeArguments; | |
3236 } | |
3237 | |
3238 /** | |
3239 * Return the body of the function that contains the given parameter, or `null
` if no | |
3240 * function body could be found. | |
3241 * | |
3242 * @param node the parameter contained in the function whose body is to be ret
urned | |
3243 * @return the body of the function that contains the given parameter | |
3244 */ | |
3245 FunctionBody _getFunctionBody(FormalParameter node) { | |
3246 AstNode parent = node.parent; | |
3247 while (parent != null) { | |
3248 if (parent is ConstructorDeclaration) { | |
3249 return parent.body; | |
3250 } else if (parent is FunctionExpression) { | |
3251 return parent.body; | |
3252 } else if (parent is MethodDeclaration) { | |
3253 return parent.body; | |
3254 } | |
3255 parent = parent.parent; | |
3256 } | |
3257 return null; | |
3258 } | |
3259 | |
3260 /** | |
3261 * Sets the visible source range for formal parameter. | |
3262 */ | |
3263 void _setParameterVisibleRange( | |
3264 FormalParameter node, ParameterElementImpl element) { | |
3265 FunctionBody body = _getFunctionBody(node); | |
3266 if (body != null) { | |
3267 element.setVisibleRange(body.offset, body.length); | |
3268 } | |
3269 } | |
3270 | |
3271 /** | |
3272 * Make the given holder be the current holder while visiting the given node. | |
3273 * | |
3274 * @param holder the holder that will gather elements that are built while vis
iting the children | |
3275 * @param node the node to be visited | |
3276 */ | |
3277 void _visit(ElementHolder holder, AstNode node) { | |
3278 if (node != null) { | |
3279 ElementHolder previousHolder = _currentHolder; | |
3280 _currentHolder = holder; | |
3281 try { | |
3282 node.accept(this); | |
3283 } finally { | |
3284 _currentHolder = previousHolder; | |
3285 } | |
3286 } | |
3287 } | |
3288 | |
3289 /** | |
3290 * Make the given holder be the current holder while visiting the children of
the given node. | |
3291 * | |
3292 * @param holder the holder that will gather elements that are built while vis
iting the children | |
3293 * @param node the node whose children are to be visited | |
3294 */ | |
3295 void _visitChildren(ElementHolder holder, AstNode node) { | |
3296 if (node != null) { | |
3297 ElementHolder previousHolder = _currentHolder; | |
3298 _currentHolder = holder; | |
3299 try { | |
3300 node.visitChildren(this); | |
3301 } finally { | |
3302 _currentHolder = previousHolder; | |
3303 } | |
3304 } | |
3305 } | |
3306 } | |
3307 | |
3308 /** | |
3309 * Instances of the class `ElementHolder` hold on to elements created while trav
ersing an AST | |
3310 * structure so that they can be accessed when creating their enclosing element. | |
3311 */ | |
3312 class ElementHolder { | |
3313 List<PropertyAccessorElement> _accessors; | |
3314 | |
3315 List<ConstructorElement> _constructors; | |
3316 | |
3317 List<ClassElement> _enums; | |
3318 | |
3319 List<FieldElement> _fields; | |
3320 | |
3321 List<FunctionElement> _functions; | |
3322 | |
3323 List<LabelElement> _labels; | |
3324 | |
3325 List<LocalVariableElement> _localVariables; | |
3326 | |
3327 List<MethodElement> _methods; | |
3328 | |
3329 List<ParameterElement> _parameters; | |
3330 | |
3331 List<TopLevelVariableElement> _topLevelVariables; | |
3332 | |
3333 List<ClassElement> _types; | |
3334 | |
3335 List<FunctionTypeAliasElement> _typeAliases; | |
3336 | |
3337 List<TypeParameterElement> _typeParameters; | |
3338 | |
3339 List<PropertyAccessorElement> get accessors { | |
3340 if (_accessors == null) { | |
3341 return PropertyAccessorElement.EMPTY_LIST; | |
3342 } | |
3343 List<PropertyAccessorElement> result = _accessors; | |
3344 _accessors = null; | |
3345 return result; | |
3346 } | |
3347 | |
3348 List<ConstructorElement> get constructors { | |
3349 if (_constructors == null) { | |
3350 return ConstructorElement.EMPTY_LIST; | |
3351 } | |
3352 List<ConstructorElement> result = _constructors; | |
3353 _constructors = null; | |
3354 return result; | |
3355 } | |
3356 | |
3357 List<ClassElement> get enums { | |
3358 if (_enums == null) { | |
3359 return ClassElement.EMPTY_LIST; | |
3360 } | |
3361 List<ClassElement> result = _enums; | |
3362 _enums = null; | |
3363 return result; | |
3364 } | |
3365 | |
3366 List<FieldElement> get fields { | |
3367 if (_fields == null) { | |
3368 return FieldElement.EMPTY_LIST; | |
3369 } | |
3370 List<FieldElement> result = _fields; | |
3371 _fields = null; | |
3372 return result; | |
3373 } | |
3374 | |
3375 List<FieldElement> get fieldsWithoutFlushing { | |
3376 if (_fields == null) { | |
3377 return FieldElement.EMPTY_LIST; | |
3378 } | |
3379 List<FieldElement> result = _fields; | |
3380 return result; | |
3381 } | |
3382 | |
3383 List<FunctionElement> get functions { | |
3384 if (_functions == null) { | |
3385 return FunctionElement.EMPTY_LIST; | |
3386 } | |
3387 List<FunctionElement> result = _functions; | |
3388 _functions = null; | |
3389 return result; | |
3390 } | |
3391 | |
3392 List<LabelElement> get labels { | |
3393 if (_labels == null) { | |
3394 return LabelElement.EMPTY_LIST; | |
3395 } | |
3396 List<LabelElement> result = _labels; | |
3397 _labels = null; | |
3398 return result; | |
3399 } | |
3400 | |
3401 List<LocalVariableElement> get localVariables { | |
3402 if (_localVariables == null) { | |
3403 return LocalVariableElement.EMPTY_LIST; | |
3404 } | |
3405 List<LocalVariableElement> result = _localVariables; | |
3406 _localVariables = null; | |
3407 return result; | |
3408 } | |
3409 | |
3410 List<MethodElement> get methods { | |
3411 if (_methods == null) { | |
3412 return MethodElement.EMPTY_LIST; | |
3413 } | |
3414 List<MethodElement> result = _methods; | |
3415 _methods = null; | |
3416 return result; | |
3417 } | |
3418 | |
3419 List<ParameterElement> get parameters { | |
3420 if (_parameters == null) { | |
3421 return ParameterElement.EMPTY_LIST; | |
3422 } | |
3423 List<ParameterElement> result = _parameters; | |
3424 _parameters = null; | |
3425 return result; | |
3426 } | |
3427 | |
3428 List<TopLevelVariableElement> get topLevelVariables { | |
3429 if (_topLevelVariables == null) { | |
3430 return TopLevelVariableElement.EMPTY_LIST; | |
3431 } | |
3432 List<TopLevelVariableElement> result = _topLevelVariables; | |
3433 _topLevelVariables = null; | |
3434 return result; | |
3435 } | |
3436 | |
3437 List<FunctionTypeAliasElement> get typeAliases { | |
3438 if (_typeAliases == null) { | |
3439 return FunctionTypeAliasElement.EMPTY_LIST; | |
3440 } | |
3441 List<FunctionTypeAliasElement> result = _typeAliases; | |
3442 _typeAliases = null; | |
3443 return result; | |
3444 } | |
3445 | |
3446 List<TypeParameterElement> get typeParameters { | |
3447 if (_typeParameters == null) { | |
3448 return TypeParameterElement.EMPTY_LIST; | |
3449 } | |
3450 List<TypeParameterElement> result = _typeParameters; | |
3451 _typeParameters = null; | |
3452 return result; | |
3453 } | |
3454 | |
3455 List<ClassElement> get types { | |
3456 if (_types == null) { | |
3457 return ClassElement.EMPTY_LIST; | |
3458 } | |
3459 List<ClassElement> result = _types; | |
3460 _types = null; | |
3461 return result; | |
3462 } | |
3463 | |
3464 void addAccessor(PropertyAccessorElement element) { | |
3465 if (_accessors == null) { | |
3466 _accessors = new List<PropertyAccessorElement>(); | |
3467 } | |
3468 _accessors.add(element); | |
3469 } | |
3470 | |
3471 void addConstructor(ConstructorElement element) { | |
3472 if (_constructors == null) { | |
3473 _constructors = new List<ConstructorElement>(); | |
3474 } | |
3475 _constructors.add(element); | |
3476 } | |
3477 | |
3478 void addEnum(ClassElement element) { | |
3479 if (_enums == null) { | |
3480 _enums = new List<ClassElement>(); | |
3481 } | |
3482 _enums.add(element); | |
3483 } | |
3484 | |
3485 void addField(FieldElement element) { | |
3486 if (_fields == null) { | |
3487 _fields = new List<FieldElement>(); | |
3488 } | |
3489 _fields.add(element); | |
3490 } | |
3491 | |
3492 void addFunction(FunctionElement element) { | |
3493 if (_functions == null) { | |
3494 _functions = new List<FunctionElement>(); | |
3495 } | |
3496 _functions.add(element); | |
3497 } | |
3498 | |
3499 void addLabel(LabelElement element) { | |
3500 if (_labels == null) { | |
3501 _labels = new List<LabelElement>(); | |
3502 } | |
3503 _labels.add(element); | |
3504 } | |
3505 | |
3506 void addLocalVariable(LocalVariableElement element) { | |
3507 if (_localVariables == null) { | |
3508 _localVariables = new List<LocalVariableElement>(); | |
3509 } | |
3510 _localVariables.add(element); | |
3511 } | |
3512 | |
3513 void addMethod(MethodElement element) { | |
3514 if (_methods == null) { | |
3515 _methods = new List<MethodElement>(); | |
3516 } | |
3517 _methods.add(element); | |
3518 } | |
3519 | |
3520 void addParameter(ParameterElement element) { | |
3521 if (_parameters == null) { | |
3522 _parameters = new List<ParameterElement>(); | |
3523 } | |
3524 _parameters.add(element); | |
3525 } | |
3526 | |
3527 void addTopLevelVariable(TopLevelVariableElement element) { | |
3528 if (_topLevelVariables == null) { | |
3529 _topLevelVariables = new List<TopLevelVariableElement>(); | |
3530 } | |
3531 _topLevelVariables.add(element); | |
3532 } | |
3533 | |
3534 void addType(ClassElement element) { | |
3535 if (_types == null) { | |
3536 _types = new List<ClassElement>(); | |
3537 } | |
3538 _types.add(element); | |
3539 } | |
3540 | |
3541 void addTypeAlias(FunctionTypeAliasElement element) { | |
3542 if (_typeAliases == null) { | |
3543 _typeAliases = new List<FunctionTypeAliasElement>(); | |
3544 } | |
3545 _typeAliases.add(element); | |
3546 } | |
3547 | |
3548 void addTypeParameter(TypeParameterElement element) { | |
3549 if (_typeParameters == null) { | |
3550 _typeParameters = new List<TypeParameterElement>(); | |
3551 } | |
3552 _typeParameters.add(element); | |
3553 } | |
3554 | |
3555 FieldElement getField(String fieldName) { | |
3556 if (_fields == null) { | |
3557 return null; | |
3558 } | |
3559 for (FieldElement field in _fields) { | |
3560 if (field.name == fieldName) { | |
3561 return field; | |
3562 } | |
3563 } | |
3564 return null; | |
3565 } | |
3566 | |
3567 TopLevelVariableElement getTopLevelVariable(String variableName) { | |
3568 if (_topLevelVariables == null) { | |
3569 return null; | |
3570 } | |
3571 for (TopLevelVariableElement variable in _topLevelVariables) { | |
3572 if (variable.name == variableName) { | |
3573 return variable; | |
3574 } | |
3575 } | |
3576 return null; | |
3577 } | |
3578 | |
3579 void validate() { | |
3580 StringBuffer buffer = new StringBuffer(); | |
3581 if (_accessors != null) { | |
3582 buffer.write(_accessors.length); | |
3583 buffer.write(" accessors"); | |
3584 } | |
3585 if (_constructors != null) { | |
3586 if (buffer.length > 0) { | |
3587 buffer.write("; "); | |
3588 } | |
3589 buffer.write(_constructors.length); | |
3590 buffer.write(" constructors"); | |
3591 } | |
3592 if (_fields != null) { | |
3593 if (buffer.length > 0) { | |
3594 buffer.write("; "); | |
3595 } | |
3596 buffer.write(_fields.length); | |
3597 buffer.write(" fields"); | |
3598 } | |
3599 if (_functions != null) { | |
3600 if (buffer.length > 0) { | |
3601 buffer.write("; "); | |
3602 } | |
3603 buffer.write(_functions.length); | |
3604 buffer.write(" functions"); | |
3605 } | |
3606 if (_labels != null) { | |
3607 if (buffer.length > 0) { | |
3608 buffer.write("; "); | |
3609 } | |
3610 buffer.write(_labels.length); | |
3611 buffer.write(" labels"); | |
3612 } | |
3613 if (_localVariables != null) { | |
3614 if (buffer.length > 0) { | |
3615 buffer.write("; "); | |
3616 } | |
3617 buffer.write(_localVariables.length); | |
3618 buffer.write(" local variables"); | |
3619 } | |
3620 if (_methods != null) { | |
3621 if (buffer.length > 0) { | |
3622 buffer.write("; "); | |
3623 } | |
3624 buffer.write(_methods.length); | |
3625 buffer.write(" methods"); | |
3626 } | |
3627 if (_parameters != null) { | |
3628 if (buffer.length > 0) { | |
3629 buffer.write("; "); | |
3630 } | |
3631 buffer.write(_parameters.length); | |
3632 buffer.write(" parameters"); | |
3633 } | |
3634 if (_topLevelVariables != null) { | |
3635 if (buffer.length > 0) { | |
3636 buffer.write("; "); | |
3637 } | |
3638 buffer.write(_topLevelVariables.length); | |
3639 buffer.write(" top-level variables"); | |
3640 } | |
3641 if (_types != null) { | |
3642 if (buffer.length > 0) { | |
3643 buffer.write("; "); | |
3644 } | |
3645 buffer.write(_types.length); | |
3646 buffer.write(" types"); | |
3647 } | |
3648 if (_typeAliases != null) { | |
3649 if (buffer.length > 0) { | |
3650 buffer.write("; "); | |
3651 } | |
3652 buffer.write(_typeAliases.length); | |
3653 buffer.write(" type aliases"); | |
3654 } | |
3655 if (_typeParameters != null) { | |
3656 if (buffer.length > 0) { | |
3657 buffer.write("; "); | |
3658 } | |
3659 buffer.write(_typeParameters.length); | |
3660 buffer.write(" type parameters"); | |
3661 } | |
3662 if (buffer.length > 0) { | |
3663 AnalysisEngine.instance.logger | |
3664 .logError("Failed to capture elements: $buffer"); | |
3665 } | |
3666 } | |
3667 } | |
3668 | |
3669 /** | |
3670 * Instances of the class `EnclosedScope` implement a scope that is lexically en
closed in | |
3671 * another scope. | |
3672 */ | |
3673 class EnclosedScope extends Scope { | |
3674 /** | |
3675 * The scope in which this scope is lexically enclosed. | |
3676 */ | |
3677 final Scope enclosingScope; | |
3678 | |
3679 /** | |
3680 * A table mapping names that will be defined in this scope, but right now are
not initialized. | |
3681 * According to the scoping rules these names are hidden, even if they were de
fined in an outer | |
3682 * scope. | |
3683 */ | |
3684 HashMap<String, Element> _hiddenElements = new HashMap<String, Element>(); | |
3685 | |
3686 /** | |
3687 * A flag indicating whether there are any names defined in this scope. | |
3688 */ | |
3689 bool _hasHiddenName = false; | |
3690 | |
3691 /** | |
3692 * Initialize a newly created scope enclosed within another scope. | |
3693 * | |
3694 * @param enclosingScope the scope in which this scope is lexically enclosed | |
3695 */ | |
3696 EnclosedScope(this.enclosingScope); | |
3697 | |
3698 @override | |
3699 AnalysisErrorListener get errorListener => enclosingScope.errorListener; | |
3700 | |
3701 /** | |
3702 * Record that given element is declared in this scope, but hasn't been initia
lized yet, so it is | |
3703 * error to use. If there is already an element with the given name defined in
an outer scope, | |
3704 * then it will become unavailable. | |
3705 * | |
3706 * @param element the element declared, but not initialized in this scope | |
3707 */ | |
3708 void hide(Element element) { | |
3709 if (element != null) { | |
3710 String name = element.name; | |
3711 if (name != null && !name.isEmpty) { | |
3712 _hiddenElements[name] = element; | |
3713 _hasHiddenName = true; | |
3714 } | |
3715 } | |
3716 } | |
3717 | |
3718 @override | |
3719 Element internalLookup( | |
3720 Identifier identifier, String name, LibraryElement referencingLibrary) { | |
3721 Element element = localLookup(name, referencingLibrary); | |
3722 if (element != null) { | |
3723 return element; | |
3724 } | |
3725 // May be there is a hidden Element. | |
3726 if (_hasHiddenName) { | |
3727 Element hiddenElement = _hiddenElements[name]; | |
3728 if (hiddenElement != null) { | |
3729 errorListener.onError(new AnalysisError(getSource(identifier), | |
3730 identifier.offset, identifier.length, | |
3731 CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION, [])); | |
3732 return hiddenElement; | |
3733 } | |
3734 } | |
3735 // Check enclosing scope. | |
3736 return enclosingScope.internalLookup(identifier, name, referencingLibrary); | |
3737 } | |
3738 } | |
3739 | |
3740 /** | |
3741 * Instances of the class `EnumMemberBuilder` build the members in enum declarat
ions. | |
3742 */ | |
3743 class EnumMemberBuilder extends RecursiveAstVisitor<Object> { | |
3744 /** | |
3745 * The type provider used to access the types needed to build an element model
for enum | |
3746 * declarations. | |
3747 */ | |
3748 final TypeProvider _typeProvider; | |
3749 | |
3750 /** | |
3751 * Initialize a newly created enum member builder. | |
3752 * | |
3753 * @param typeProvider the type provider used to access the types needed to bu
ild an element model | |
3754 * for enum declarations | |
3755 */ | |
3756 EnumMemberBuilder(this._typeProvider); | |
3757 | |
3758 @override | |
3759 Object visitEnumDeclaration(EnumDeclaration node) { | |
3760 // | |
3761 // Finish building the enum. | |
3762 // | |
3763 ClassElementImpl enumElement = node.name.staticElement as ClassElementImpl; | |
3764 InterfaceType enumType = enumElement.type; | |
3765 enumElement.supertype = _typeProvider.objectType; | |
3766 // | |
3767 // Populate the fields. | |
3768 // | |
3769 List<FieldElement> fields = new List<FieldElement>(); | |
3770 List<PropertyAccessorElement> getters = new List<PropertyAccessorElement>(); | |
3771 InterfaceType intType = _typeProvider.intType; | |
3772 String indexFieldName = "index"; | |
3773 FieldElementImpl indexField = new FieldElementImpl(indexFieldName, -1); | |
3774 indexField.final2 = true; | |
3775 indexField.synthetic = true; | |
3776 indexField.type = intType; | |
3777 fields.add(indexField); | |
3778 getters.add(_createGetter(indexField)); | |
3779 ConstFieldElementImpl valuesField = new ConstFieldElementImpl("values", -1); | |
3780 valuesField.static = true; | |
3781 valuesField.const3 = true; | |
3782 valuesField.synthetic = true; | |
3783 valuesField.type = _typeProvider.listType.substitute4(<DartType>[enumType]); | |
3784 fields.add(valuesField); | |
3785 getters.add(_createGetter(valuesField)); | |
3786 // | |
3787 // Build the enum constants. | |
3788 // | |
3789 NodeList<EnumConstantDeclaration> constants = node.constants; | |
3790 List<DartObjectImpl> constantValues = new List<DartObjectImpl>(); | |
3791 int constantCount = constants.length; | |
3792 for (int i = 0; i < constantCount; i++) { | |
3793 SimpleIdentifier constantName = constants[i].name; | |
3794 FieldElementImpl constantField = | |
3795 new ConstFieldElementImpl.forNode(constantName); | |
3796 constantField.static = true; | |
3797 constantField.const3 = true; | |
3798 constantField.type = enumType; | |
3799 // | |
3800 // Create a value for the constant. | |
3801 // | |
3802 HashMap<String, DartObjectImpl> fieldMap = | |
3803 new HashMap<String, DartObjectImpl>(); | |
3804 fieldMap[indexFieldName] = new DartObjectImpl(intType, new IntState(i)); | |
3805 DartObjectImpl value = | |
3806 new DartObjectImpl(enumType, new GenericState(fieldMap)); | |
3807 constantValues.add(value); | |
3808 constantField.evaluationResult = new EvaluationResultImpl(value); | |
3809 fields.add(constantField); | |
3810 getters.add(_createGetter(constantField)); | |
3811 constantName.staticElement = constantField; | |
3812 } | |
3813 // | |
3814 // Build the value of the 'values' field. | |
3815 // | |
3816 valuesField.evaluationResult = new EvaluationResultImpl( | |
3817 new DartObjectImpl(valuesField.type, new ListState(constantValues))); | |
3818 // | |
3819 // Finish building the enum. | |
3820 // | |
3821 enumElement.fields = fields; | |
3822 enumElement.accessors = getters; | |
3823 // Client code isn't allowed to invoke the constructor, so we do not model | |
3824 // it. | |
3825 return super.visitEnumDeclaration(node); | |
3826 } | |
3827 | |
3828 /** | |
3829 * Create a getter that corresponds to the given field. | |
3830 * | |
3831 * @param field the field for which a getter is to be created | |
3832 * @return the getter that was created | |
3833 */ | |
3834 PropertyAccessorElement _createGetter(FieldElementImpl field) { | |
3835 PropertyAccessorElementImpl getter = | |
3836 new PropertyAccessorElementImpl.forVariable(field); | |
3837 getter.getter = true; | |
3838 getter.returnType = field.type; | |
3839 getter.type = new FunctionTypeImpl(getter); | |
3840 field.getter = getter; | |
3841 return getter; | |
3842 } | |
3843 } | |
3844 | |
3845 /** | |
3846 * Instances of the class `ExitDetector` determine whether the visited AST node
is guaranteed | |
3847 * to terminate by executing a `return` statement, `throw` expression, `rethrow` | |
3848 * expression, or simple infinite loop such as `while(true)`. | |
3849 */ | |
3850 class ExitDetector extends GeneralizingAstVisitor<bool> { | |
3851 /** | |
3852 * Set to `true` when a `break` is encountered, and reset to `false` when a | |
3853 * `do`, `while`, `for` or `switch` block is entered. | |
3854 */ | |
3855 bool _enclosingBlockContainsBreak = false; | |
3856 | |
3857 @override | |
3858 bool visitArgumentList(ArgumentList node) => | |
3859 _visitExpressions(node.arguments); | |
3860 | |
3861 @override | |
3862 bool visitAsExpression(AsExpression node) => _nodeExits(node.expression); | |
3863 | |
3864 @override | |
3865 bool visitAssertStatement(AssertStatement node) => _nodeExits(node.condition); | |
3866 | |
3867 @override | |
3868 bool visitAssignmentExpression(AssignmentExpression node) => | |
3869 _nodeExits(node.leftHandSide) || _nodeExits(node.rightHandSide); | |
3870 | |
3871 @override | |
3872 bool visitAwaitExpression(AwaitExpression node) => | |
3873 _nodeExits(node.expression); | |
3874 | |
3875 @override | |
3876 bool visitBinaryExpression(BinaryExpression node) { | |
3877 Expression lhsExpression = node.leftOperand; | |
3878 sc.TokenType operatorType = node.operator.type; | |
3879 // If the operator is || and the left hand side is false literal, don't | |
3880 // consider the RHS of the binary expression. | |
3881 // TODO(jwren) Do we want to take constant expressions into account, | |
3882 // evaluate if(false) {} differently than if(<condition>), when <condition> | |
3883 // evaluates to a constant false value? | |
3884 if (operatorType == sc.TokenType.BAR_BAR) { | |
3885 if (lhsExpression is BooleanLiteral) { | |
3886 BooleanLiteral booleanLiteral = lhsExpression; | |
3887 if (!booleanLiteral.value) { | |
3888 return false; | |
3889 } | |
3890 } | |
3891 } | |
3892 // If the operator is && and the left hand side is true literal, don't | |
3893 // consider the RHS of the binary expression. | |
3894 if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) { | |
3895 if (lhsExpression is BooleanLiteral) { | |
3896 BooleanLiteral booleanLiteral = lhsExpression; | |
3897 if (booleanLiteral.value) { | |
3898 return false; | |
3899 } | |
3900 } | |
3901 } | |
3902 Expression rhsExpression = node.rightOperand; | |
3903 return _nodeExits(lhsExpression) || _nodeExits(rhsExpression); | |
3904 } | |
3905 | |
3906 @override | |
3907 bool visitBlock(Block node) => _visitStatements(node.statements); | |
3908 | |
3909 @override | |
3910 bool visitBlockFunctionBody(BlockFunctionBody node) => _nodeExits(node.block); | |
3911 | |
3912 @override | |
3913 bool visitBreakStatement(BreakStatement node) { | |
3914 _enclosingBlockContainsBreak = true; | |
3915 return false; | |
3916 } | |
3917 | |
3918 @override | |
3919 bool visitCascadeExpression(CascadeExpression node) => | |
3920 _nodeExits(node.target) || _visitExpressions(node.cascadeSections); | |
3921 | |
3922 @override | |
3923 bool visitConditionalExpression(ConditionalExpression node) { | |
3924 Expression conditionExpression = node.condition; | |
3925 Expression thenStatement = node.thenExpression; | |
3926 Expression elseStatement = node.elseExpression; | |
3927 // TODO(jwren) Do we want to take constant expressions into account, | |
3928 // evaluate if(false) {} differently than if(<condition>), when <condition> | |
3929 // evaluates to a constant false value? | |
3930 if (_nodeExits(conditionExpression)) { | |
3931 return true; | |
3932 } | |
3933 if (thenStatement == null || elseStatement == null) { | |
3934 return false; | |
3935 } | |
3936 return thenStatement.accept(this) && elseStatement.accept(this); | |
3937 } | |
3938 | |
3939 @override | |
3940 bool visitContinueStatement(ContinueStatement node) => false; | |
3941 | |
3942 @override | |
3943 bool visitDoStatement(DoStatement node) { | |
3944 bool outerBreakValue = _enclosingBlockContainsBreak; | |
3945 _enclosingBlockContainsBreak = false; | |
3946 try { | |
3947 Expression conditionExpression = node.condition; | |
3948 if (_nodeExits(conditionExpression)) { | |
3949 return true; | |
3950 } | |
3951 // TODO(jwren) Do we want to take all constant expressions into account? | |
3952 if (conditionExpression is BooleanLiteral) { | |
3953 BooleanLiteral booleanLiteral = conditionExpression; | |
3954 // If do {} while (true), and the body doesn't return or the body | |
3955 // doesn't have a break, then return true. | |
3956 bool blockReturns = _nodeExits(node.body); | |
3957 if (booleanLiteral.value && | |
3958 (blockReturns || !_enclosingBlockContainsBreak)) { | |
3959 return true; | |
3960 } | |
3961 } | |
3962 return false; | |
3963 } finally { | |
3964 _enclosingBlockContainsBreak = outerBreakValue; | |
3965 } | |
3966 } | |
3967 | |
3968 @override | |
3969 bool visitEmptyStatement(EmptyStatement node) => false; | |
3970 | |
3971 @override | |
3972 bool visitExpressionStatement(ExpressionStatement node) => | |
3973 _nodeExits(node.expression); | |
3974 | |
3975 @override | |
3976 bool visitForEachStatement(ForEachStatement node) { | |
3977 bool outerBreakValue = _enclosingBlockContainsBreak; | |
3978 _enclosingBlockContainsBreak = false; | |
3979 try { | |
3980 return _nodeExits(node.iterable); | |
3981 } finally { | |
3982 _enclosingBlockContainsBreak = outerBreakValue; | |
3983 } | |
3984 } | |
3985 | |
3986 @override | |
3987 bool visitForStatement(ForStatement node) { | |
3988 bool outerBreakValue = _enclosingBlockContainsBreak; | |
3989 _enclosingBlockContainsBreak = false; | |
3990 try { | |
3991 if (node.variables != null && | |
3992 _visitVariableDeclarations(node.variables.variables)) { | |
3993 return true; | |
3994 } | |
3995 if (node.initialization != null && _nodeExits(node.initialization)) { | |
3996 return true; | |
3997 } | |
3998 Expression conditionExpression = node.condition; | |
3999 if (conditionExpression != null && _nodeExits(conditionExpression)) { | |
4000 return true; | |
4001 } | |
4002 if (_visitExpressions(node.updaters)) { | |
4003 return true; | |
4004 } | |
4005 // TODO(jwren) Do we want to take all constant expressions into account? | |
4006 // If for(; true; ) (or for(;;)), and the body doesn't return or the body | |
4007 // doesn't have a break, then return true. | |
4008 bool implicitOrExplictTrue = conditionExpression == null || | |
4009 (conditionExpression is BooleanLiteral && conditionExpression.value); | |
4010 if (implicitOrExplictTrue) { | |
4011 bool blockReturns = _nodeExits(node.body); | |
4012 if (blockReturns || !_enclosingBlockContainsBreak) { | |
4013 return true; | |
4014 } | |
4015 } | |
4016 return false; | |
4017 } finally { | |
4018 _enclosingBlockContainsBreak = outerBreakValue; | |
4019 } | |
4020 } | |
4021 | |
4022 @override | |
4023 bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) => | |
4024 false; | |
4025 | |
4026 @override | |
4027 bool visitFunctionExpression(FunctionExpression node) => false; | |
4028 | |
4029 @override | |
4030 bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | |
4031 if (_nodeExits(node.function)) { | |
4032 return true; | |
4033 } | |
4034 return node.argumentList.accept(this); | |
4035 } | |
4036 | |
4037 @override | |
4038 bool visitIdentifier(Identifier node) => false; | |
4039 | |
4040 @override | |
4041 bool visitIfStatement(IfStatement node) { | |
4042 Expression conditionExpression = node.condition; | |
4043 Statement thenStatement = node.thenStatement; | |
4044 Statement elseStatement = node.elseStatement; | |
4045 if (_nodeExits(conditionExpression)) { | |
4046 return true; | |
4047 } | |
4048 // TODO(jwren) Do we want to take all constant expressions into account? | |
4049 if (conditionExpression is BooleanLiteral) { | |
4050 BooleanLiteral booleanLiteral = conditionExpression; | |
4051 if (booleanLiteral.value) { | |
4052 // if(true) ... | |
4053 return _nodeExits(thenStatement); | |
4054 } else if (elseStatement != null) { | |
4055 // if (false) ... | |
4056 return _nodeExits(elseStatement); | |
4057 } | |
4058 } | |
4059 if (thenStatement == null || elseStatement == null) { | |
4060 return false; | |
4061 } | |
4062 return _nodeExits(thenStatement) && _nodeExits(elseStatement); | |
4063 } | |
4064 | |
4065 @override | |
4066 bool visitIndexExpression(IndexExpression node) { | |
4067 Expression target = node.realTarget; | |
4068 if (_nodeExits(target)) { | |
4069 return true; | |
4070 } | |
4071 if (_nodeExits(node.index)) { | |
4072 return true; | |
4073 } | |
4074 return false; | |
4075 } | |
4076 | |
4077 @override | |
4078 bool visitInstanceCreationExpression(InstanceCreationExpression node) => | |
4079 _nodeExits(node.argumentList); | |
4080 | |
4081 @override | |
4082 bool visitIsExpression(IsExpression node) => node.expression.accept(this); | |
4083 | |
4084 @override | |
4085 bool visitLabel(Label node) => false; | |
4086 | |
4087 @override | |
4088 bool visitLabeledStatement(LabeledStatement node) => | |
4089 node.statement.accept(this); | |
4090 | |
4091 @override | |
4092 bool visitLiteral(Literal node) => false; | |
4093 | |
4094 @override | |
4095 bool visitMethodInvocation(MethodInvocation node) { | |
4096 Expression target = node.realTarget; | |
4097 if (target != null && target.accept(this)) { | |
4098 return true; | |
4099 } | |
4100 return _nodeExits(node.argumentList); | |
4101 } | |
4102 | |
4103 @override | |
4104 bool visitNamedExpression(NamedExpression node) => | |
4105 node.expression.accept(this); | |
4106 | |
4107 @override | |
4108 bool visitParenthesizedExpression(ParenthesizedExpression node) => | |
4109 node.expression.accept(this); | |
4110 | |
4111 @override | |
4112 bool visitPostfixExpression(PostfixExpression node) => false; | |
4113 | |
4114 @override | |
4115 bool visitPrefixExpression(PrefixExpression node) => false; | |
4116 | |
4117 @override | |
4118 bool visitPropertyAccess(PropertyAccess node) { | |
4119 Expression target = node.realTarget; | |
4120 if (target != null && target.accept(this)) { | |
4121 return true; | |
4122 } | |
4123 return false; | |
4124 } | |
4125 | |
4126 @override | |
4127 bool visitRethrowExpression(RethrowExpression node) => true; | |
4128 | |
4129 @override | |
4130 bool visitReturnStatement(ReturnStatement node) => true; | |
4131 | |
4132 @override | |
4133 bool visitSuperExpression(SuperExpression node) => false; | |
4134 | |
4135 @override | |
4136 bool visitSwitchCase(SwitchCase node) => _visitStatements(node.statements); | |
4137 | |
4138 @override | |
4139 bool visitSwitchDefault(SwitchDefault node) => | |
4140 _visitStatements(node.statements); | |
4141 | |
4142 @override | |
4143 bool visitSwitchStatement(SwitchStatement node) { | |
4144 bool outerBreakValue = _enclosingBlockContainsBreak; | |
4145 _enclosingBlockContainsBreak = false; | |
4146 try { | |
4147 bool hasDefault = false; | |
4148 List<SwitchMember> members = node.members; | |
4149 for (int i = 0; i < members.length; i++) { | |
4150 SwitchMember switchMember = members[i]; | |
4151 if (switchMember is SwitchDefault) { | |
4152 hasDefault = true; | |
4153 // If this is the last member and there are no statements, return | |
4154 // false | |
4155 if (switchMember.statements.isEmpty && i + 1 == members.length) { | |
4156 return false; | |
4157 } | |
4158 } | |
4159 // For switch members with no statements, don't visit the children, | |
4160 // otherwise, return false if no return is found in the children | |
4161 // statements. | |
4162 if (!switchMember.statements.isEmpty && !switchMember.accept(this)) { | |
4163 return false; | |
4164 } | |
4165 } | |
4166 // All of the members exit, determine whether there are possible cases | |
4167 // that are not caught by the members. | |
4168 DartType type = node.expression == null ? null : node.expression.bestType; | |
4169 if (type is InterfaceType) { | |
4170 ClassElement element = type.element; | |
4171 if (element != null && element.isEnum) { | |
4172 // If some of the enum values are not covered, then a warning will | |
4173 // have already been generated, so there's no point in generating a | |
4174 // hint. | |
4175 return true; | |
4176 } | |
4177 } | |
4178 return hasDefault; | |
4179 } finally { | |
4180 _enclosingBlockContainsBreak = outerBreakValue; | |
4181 } | |
4182 } | |
4183 | |
4184 @override | |
4185 bool visitThisExpression(ThisExpression node) => false; | |
4186 | |
4187 @override | |
4188 bool visitThrowExpression(ThrowExpression node) => true; | |
4189 | |
4190 @override | |
4191 bool visitTryStatement(TryStatement node) { | |
4192 if (_nodeExits(node.body)) { | |
4193 return true; | |
4194 } | |
4195 Block finallyBlock = node.finallyBlock; | |
4196 if (_nodeExits(finallyBlock)) { | |
4197 return true; | |
4198 } | |
4199 return false; | |
4200 } | |
4201 | |
4202 @override | |
4203 bool visitTypeName(TypeName node) => false; | |
4204 | |
4205 @override | |
4206 bool visitVariableDeclaration(VariableDeclaration node) { | |
4207 Expression initializer = node.initializer; | |
4208 if (initializer != null) { | |
4209 return initializer.accept(this); | |
4210 } | |
4211 return false; | |
4212 } | |
4213 | |
4214 @override | |
4215 bool visitVariableDeclarationList(VariableDeclarationList node) => | |
4216 _visitVariableDeclarations(node.variables); | |
4217 | |
4218 @override | |
4219 bool visitVariableDeclarationStatement(VariableDeclarationStatement node) { | |
4220 NodeList<VariableDeclaration> variables = node.variables.variables; | |
4221 for (int i = 0; i < variables.length; i++) { | |
4222 if (variables[i].accept(this)) { | |
4223 return true; | |
4224 } | |
4225 } | |
4226 return false; | |
4227 } | |
4228 | |
4229 @override | |
4230 bool visitWhileStatement(WhileStatement node) { | |
4231 bool outerBreakValue = _enclosingBlockContainsBreak; | |
4232 _enclosingBlockContainsBreak = false; | |
4233 try { | |
4234 Expression conditionExpression = node.condition; | |
4235 if (conditionExpression.accept(this)) { | |
4236 return true; | |
4237 } | |
4238 // TODO(jwren) Do we want to take all constant expressions into account? | |
4239 if (conditionExpression is BooleanLiteral) { | |
4240 BooleanLiteral booleanLiteral = conditionExpression; | |
4241 // If while(true), and the body doesn't return or the body doesn't have | |
4242 // a break, then return true. | |
4243 bool blockReturns = node.body.accept(this); | |
4244 if (booleanLiteral.value && | |
4245 (blockReturns || !_enclosingBlockContainsBreak)) { | |
4246 return true; | |
4247 } | |
4248 } | |
4249 return false; | |
4250 } finally { | |
4251 _enclosingBlockContainsBreak = outerBreakValue; | |
4252 } | |
4253 } | |
4254 | |
4255 /** | |
4256 * Return `true` if the given node exits. | |
4257 * | |
4258 * @param node the node being tested | |
4259 * @return `true` if the given node exits | |
4260 */ | |
4261 bool _nodeExits(AstNode node) { | |
4262 if (node == null) { | |
4263 return false; | |
4264 } | |
4265 return node.accept(this); | |
4266 } | |
4267 | |
4268 bool _visitExpressions(NodeList<Expression> expressions) { | |
4269 for (int i = expressions.length - 1; i >= 0; i--) { | |
4270 if (expressions[i].accept(this)) { | |
4271 return true; | |
4272 } | |
4273 } | |
4274 return false; | |
4275 } | |
4276 | |
4277 bool _visitStatements(NodeList<Statement> statements) { | |
4278 for (int i = statements.length - 1; i >= 0; i--) { | |
4279 if (statements[i].accept(this)) { | |
4280 return true; | |
4281 } | |
4282 } | |
4283 return false; | |
4284 } | |
4285 | |
4286 bool _visitVariableDeclarations( | |
4287 NodeList<VariableDeclaration> variableDeclarations) { | |
4288 for (int i = variableDeclarations.length - 1; i >= 0; i--) { | |
4289 if (variableDeclarations[i].accept(this)) { | |
4290 return true; | |
4291 } | |
4292 } | |
4293 return false; | |
4294 } | |
4295 | |
4296 /** | |
4297 * Return `true` if the given [node] exits. | |
4298 */ | |
4299 static bool exits(AstNode node) { | |
4300 return new ExitDetector()._nodeExits(node); | |
4301 } | |
4302 } | |
4303 | |
4304 /** | |
4305 * The scope defined by a function. | |
4306 */ | |
4307 class FunctionScope extends EnclosedScope { | |
4308 /** | |
4309 * The element representing the function that defines this scope. | |
4310 */ | |
4311 final ExecutableElement _functionElement; | |
4312 | |
4313 /** | |
4314 * A flag indicating whether the parameters have already been defined, used to | |
4315 * prevent the parameters from being defined multiple times. | |
4316 */ | |
4317 bool _parametersDefined = false; | |
4318 | |
4319 /** | |
4320 * Initialize a newly created scope enclosed within the [enclosingScope] that | |
4321 * represents the given [_functionElement]. | |
4322 */ | |
4323 FunctionScope(Scope enclosingScope, this._functionElement) | |
4324 : super(new EnclosedScope(new EnclosedScope(enclosingScope))) { | |
4325 if (_functionElement == null) { | |
4326 throw new IllegalArgumentException("function element cannot be null"); | |
4327 } | |
4328 _defineTypeParameters(); | |
4329 } | |
4330 | |
4331 /** | |
4332 * Define the parameters for the given function in the scope that encloses | |
4333 * this function. | |
4334 */ | |
4335 void defineParameters() { | |
4336 if (_parametersDefined) { | |
4337 return; | |
4338 } | |
4339 _parametersDefined = true; | |
4340 Scope parameterScope = enclosingScope; | |
4341 for (ParameterElement parameter in _functionElement.parameters) { | |
4342 if (!parameter.isInitializingFormal) { | |
4343 parameterScope.define(parameter); | |
4344 } | |
4345 } | |
4346 } | |
4347 | |
4348 /** | |
4349 * Define the type parameters for the function. | |
4350 */ | |
4351 void _defineTypeParameters() { | |
4352 Scope typeParameterScope = enclosingScope.enclosingScope; | |
4353 for (TypeParameterElement typeParameter | |
4354 in _functionElement.typeParameters) { | |
4355 typeParameterScope.define(typeParameter); | |
4356 } | |
4357 } | |
4358 } | |
4359 | |
4360 /** | |
4361 * The scope defined by a function type alias. | |
4362 */ | |
4363 class FunctionTypeScope extends EnclosedScope { | |
4364 final FunctionTypeAliasElement _typeElement; | |
4365 | |
4366 bool _parametersDefined = false; | |
4367 | |
4368 /** | |
4369 * Initialize a newly created scope enclosed within the [enclosingScope] that | |
4370 * represents the given [_typeElement]. | |
4371 */ | |
4372 FunctionTypeScope(Scope enclosingScope, this._typeElement) | |
4373 : super(new EnclosedScope(enclosingScope)) { | |
4374 _defineTypeParameters(); | |
4375 } | |
4376 | |
4377 /** | |
4378 * Define the parameters for the function type alias. | |
4379 */ | |
4380 void defineParameters() { | |
4381 if (_parametersDefined) { | |
4382 return; | |
4383 } | |
4384 _parametersDefined = true; | |
4385 for (ParameterElement parameter in _typeElement.parameters) { | |
4386 define(parameter); | |
4387 } | |
4388 } | |
4389 | |
4390 /** | |
4391 * Define the type parameters for the function type alias. | |
4392 */ | |
4393 void _defineTypeParameters() { | |
4394 Scope typeParameterScope = enclosingScope; | |
4395 for (TypeParameterElement typeParameter in _typeElement.typeParameters) { | |
4396 typeParameterScope.define(typeParameter); | |
4397 } | |
4398 } | |
4399 } | |
4400 | |
4401 /** | |
4402 * A visitor that visits ASTs and fills [UsedImportedElements]. | |
4403 */ | |
4404 class GatherUsedImportedElementsVisitor extends RecursiveAstVisitor { | |
4405 final LibraryElement library; | |
4406 final UsedImportedElements usedElements = new UsedImportedElements(); | |
4407 | |
4408 GatherUsedImportedElementsVisitor(this.library); | |
4409 | |
4410 @override | |
4411 void visitExportDirective(ExportDirective node) { | |
4412 _visitMetadata(node.metadata); | |
4413 } | |
4414 | |
4415 @override | |
4416 void visitImportDirective(ImportDirective node) { | |
4417 _visitMetadata(node.metadata); | |
4418 } | |
4419 | |
4420 @override | |
4421 void visitLibraryDirective(LibraryDirective node) { | |
4422 _visitMetadata(node.metadata); | |
4423 } | |
4424 | |
4425 @override | |
4426 void visitPrefixedIdentifier(PrefixedIdentifier node) { | |
4427 // If the prefixed identifier references some A.B, where A is a library | |
4428 // prefix, then we can lookup the associated ImportDirective in | |
4429 // prefixElementMap and remove it from the unusedImports list. | |
4430 SimpleIdentifier prefixIdentifier = node.prefix; | |
4431 Element element = prefixIdentifier.staticElement; | |
4432 if (element is PrefixElement) { | |
4433 usedElements.prefixes.add(element); | |
4434 return; | |
4435 } | |
4436 // Otherwise, pass the prefixed identifier element and name onto | |
4437 // visitIdentifier. | |
4438 _visitIdentifier(element, prefixIdentifier.name); | |
4439 } | |
4440 | |
4441 @override | |
4442 void visitSimpleIdentifier(SimpleIdentifier node) { | |
4443 _visitIdentifier(node.staticElement, node.name); | |
4444 } | |
4445 | |
4446 void _visitIdentifier(Element element, String name) { | |
4447 if (element == null) { | |
4448 return; | |
4449 } | |
4450 // If the element is multiply defined then call this method recursively for | |
4451 // each of the conflicting elements. | |
4452 if (element is MultiplyDefinedElement) { | |
4453 MultiplyDefinedElement multiplyDefinedElement = element; | |
4454 for (Element elt in multiplyDefinedElement.conflictingElements) { | |
4455 _visitIdentifier(elt, name); | |
4456 } | |
4457 return; | |
4458 } else if (element is PrefixElement) { | |
4459 usedElements.prefixes.add(element); | |
4460 return; | |
4461 } else if (element.enclosingElement is! CompilationUnitElement) { | |
4462 // Identifiers that aren't a prefix element and whose enclosing element | |
4463 // isn't a CompilationUnit are ignored- this covers the case the | |
4464 // identifier is a relative-reference, a reference to an identifier not | |
4465 // imported by this library. | |
4466 return; | |
4467 } | |
4468 // Ignore if an unknown library. | |
4469 LibraryElement containingLibrary = element.library; | |
4470 if (containingLibrary == null) { | |
4471 return; | |
4472 } | |
4473 // Ignore if a local element. | |
4474 if (library == containingLibrary) { | |
4475 return; | |
4476 } | |
4477 // Remember the element. | |
4478 usedElements.elements.add(element); | |
4479 } | |
4480 | |
4481 /** | |
4482 * Given some [NodeList] of [Annotation]s, ensure that the identifiers are vis
ited by | |
4483 * this visitor. Specifically, this covers the cases where AST nodes don't hav
e their identifiers | |
4484 * visited by this visitor, but still need their annotations visited. | |
4485 * | |
4486 * @param annotations the list of annotations to visit | |
4487 */ | |
4488 void _visitMetadata(NodeList<Annotation> annotations) { | |
4489 int count = annotations.length; | |
4490 for (int i = 0; i < count; i++) { | |
4491 annotations[i].accept(this); | |
4492 } | |
4493 } | |
4494 } | |
4495 | |
4496 /** | |
4497 * An [AstVisitor] that fills [UsedLocalElements]. | |
4498 */ | |
4499 class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { | |
4500 final UsedLocalElements usedElements = new UsedLocalElements(); | |
4501 | |
4502 final LibraryElement _enclosingLibrary; | |
4503 ClassElement _enclosingClass; | |
4504 ExecutableElement _enclosingExec; | |
4505 | |
4506 GatherUsedLocalElementsVisitor(this._enclosingLibrary); | |
4507 | |
4508 @override | |
4509 visitCatchClause(CatchClause node) { | |
4510 SimpleIdentifier exceptionParameter = node.exceptionParameter; | |
4511 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; | |
4512 if (exceptionParameter != null) { | |
4513 Element element = exceptionParameter.staticElement; | |
4514 usedElements.addCatchException(element); | |
4515 if (stackTraceParameter != null || node.onKeyword == null) { | |
4516 usedElements.addElement(element); | |
4517 } | |
4518 } | |
4519 if (stackTraceParameter != null) { | |
4520 Element element = stackTraceParameter.staticElement; | |
4521 usedElements.addCatchStackTrace(element); | |
4522 } | |
4523 super.visitCatchClause(node); | |
4524 } | |
4525 | |
4526 @override | |
4527 visitClassDeclaration(ClassDeclaration node) { | |
4528 ClassElement enclosingClassOld = _enclosingClass; | |
4529 try { | |
4530 _enclosingClass = node.element; | |
4531 super.visitClassDeclaration(node); | |
4532 } finally { | |
4533 _enclosingClass = enclosingClassOld; | |
4534 } | |
4535 } | |
4536 | |
4537 @override | |
4538 visitFunctionDeclaration(FunctionDeclaration node) { | |
4539 ExecutableElement enclosingExecOld = _enclosingExec; | |
4540 try { | |
4541 _enclosingExec = node.element; | |
4542 super.visitFunctionDeclaration(node); | |
4543 } finally { | |
4544 _enclosingExec = enclosingExecOld; | |
4545 } | |
4546 } | |
4547 | |
4548 @override | |
4549 visitFunctionExpression(FunctionExpression node) { | |
4550 if (node.parent is! FunctionDeclaration) { | |
4551 usedElements.addElement(node.element); | |
4552 } | |
4553 super.visitFunctionExpression(node); | |
4554 } | |
4555 | |
4556 @override | |
4557 visitMethodDeclaration(MethodDeclaration node) { | |
4558 ExecutableElement enclosingExecOld = _enclosingExec; | |
4559 try { | |
4560 _enclosingExec = node.element; | |
4561 super.visitMethodDeclaration(node); | |
4562 } finally { | |
4563 _enclosingExec = enclosingExecOld; | |
4564 } | |
4565 } | |
4566 | |
4567 @override | |
4568 visitSimpleIdentifier(SimpleIdentifier node) { | |
4569 if (node.inDeclarationContext()) { | |
4570 return; | |
4571 } | |
4572 Element element = node.staticElement; | |
4573 bool isIdentifierRead = _isReadIdentifier(node); | |
4574 if (element is LocalVariableElement) { | |
4575 if (isIdentifierRead) { | |
4576 usedElements.addElement(element); | |
4577 } | |
4578 } else { | |
4579 _useIdentifierElement(node); | |
4580 if (element == null || | |
4581 element.enclosingElement is ClassElement && | |
4582 !identical(element, _enclosingExec)) { | |
4583 usedElements.members.add(node.name); | |
4584 if (isIdentifierRead) { | |
4585 usedElements.readMembers.add(node.name); | |
4586 } | |
4587 } | |
4588 } | |
4589 } | |
4590 | |
4591 /** | |
4592 * Marks an [Element] of [node] as used in the library. | |
4593 */ | |
4594 void _useIdentifierElement(Identifier node) { | |
4595 Element element = node.staticElement; | |
4596 if (element == null) { | |
4597 return; | |
4598 } | |
4599 // check if a local element | |
4600 if (!identical(element.library, _enclosingLibrary)) { | |
4601 return; | |
4602 } | |
4603 // ignore references to an element from itself | |
4604 if (identical(element, _enclosingClass)) { | |
4605 return; | |
4606 } | |
4607 if (identical(element, _enclosingExec)) { | |
4608 return; | |
4609 } | |
4610 // ignore places where the element is not actually used | |
4611 if (node.parent is TypeName) { | |
4612 if (element is ClassElement) { | |
4613 AstNode parent2 = node.parent.parent; | |
4614 if (parent2 is IsExpression) { | |
4615 return; | |
4616 } | |
4617 if (parent2 is VariableDeclarationList) { | |
4618 return; | |
4619 } | |
4620 } | |
4621 } | |
4622 // OK | |
4623 usedElements.addElement(element); | |
4624 } | |
4625 | |
4626 static bool _isReadIdentifier(SimpleIdentifier node) { | |
4627 // not reading at all | |
4628 if (!node.inGetterContext()) { | |
4629 return false; | |
4630 } | |
4631 // check if useless reading | |
4632 AstNode parent = node.parent; | |
4633 if (parent.parent is ExpressionStatement && | |
4634 (parent is PrefixExpression || | |
4635 parent is PostfixExpression || | |
4636 parent is AssignmentExpression && parent.leftHandSide == node)) { | |
4637 // v++; | |
4638 // ++v; | |
4639 // v += 2; | |
4640 return false; | |
4641 } | |
4642 // OK | |
4643 return true; | |
4644 } | |
4645 } | |
4646 | |
4647 /** | |
4648 * Instances of the class `HintGenerator` traverse a library's worth of dart cod
e at a time to | |
4649 * generate hints over the set of sources. | |
4650 * | |
4651 * See [HintCode]. | |
4652 */ | |
4653 class HintGenerator { | |
4654 final List<CompilationUnit> _compilationUnits; | |
4655 | |
4656 final InternalAnalysisContext _context; | |
4657 | |
4658 final AnalysisErrorListener _errorListener; | |
4659 | |
4660 LibraryElement _library; | |
4661 | |
4662 GatherUsedImportedElementsVisitor _usedImportedElementsVisitor; | |
4663 | |
4664 bool _enableDart2JSHints = false; | |
4665 | |
4666 /** | |
4667 * The inheritance manager used to find overridden methods. | |
4668 */ | |
4669 InheritanceManager _manager; | |
4670 | |
4671 GatherUsedLocalElementsVisitor _usedLocalElementsVisitor; | |
4672 | |
4673 HintGenerator(this._compilationUnits, this._context, this._errorListener) { | |
4674 _library = _compilationUnits[0].element.library; | |
4675 _usedImportedElementsVisitor = | |
4676 new GatherUsedImportedElementsVisitor(_library); | |
4677 _enableDart2JSHints = _context.analysisOptions.dart2jsHint; | |
4678 _manager = new InheritanceManager(_compilationUnits[0].element.library); | |
4679 _usedLocalElementsVisitor = new GatherUsedLocalElementsVisitor(_library); | |
4680 } | |
4681 | |
4682 void generateForLibrary() { | |
4683 PerformanceStatistics.hints.makeCurrentWhile(() { | |
4684 for (CompilationUnit unit in _compilationUnits) { | |
4685 CompilationUnitElement element = unit.element; | |
4686 if (element != null) { | |
4687 _generateForCompilationUnit(unit, element.source); | |
4688 } | |
4689 } | |
4690 CompilationUnit definingUnit = _compilationUnits[0]; | |
4691 ErrorReporter definingUnitErrorReporter = | |
4692 new ErrorReporter(_errorListener, definingUnit.element.source); | |
4693 { | |
4694 ImportsVerifier importsVerifier = new ImportsVerifier(); | |
4695 importsVerifier.addImports(definingUnit); | |
4696 importsVerifier | |
4697 .removeUsedElements(_usedImportedElementsVisitor.usedElements); | |
4698 importsVerifier.generateDuplicateImportHints(definingUnitErrorReporter); | |
4699 importsVerifier.generateUnusedImportHints(definingUnitErrorReporter); | |
4700 } | |
4701 _library.accept(new UnusedLocalElementsVerifier( | |
4702 _errorListener, _usedLocalElementsVisitor.usedElements)); | |
4703 }); | |
4704 } | |
4705 | |
4706 void _generateForCompilationUnit(CompilationUnit unit, Source source) { | |
4707 ErrorReporter errorReporter = new ErrorReporter(_errorListener, source); | |
4708 unit.accept(_usedImportedElementsVisitor); | |
4709 // dead code analysis | |
4710 unit.accept(new DeadCodeVerifier(errorReporter)); | |
4711 unit.accept(_usedLocalElementsVisitor); | |
4712 // dart2js analysis | |
4713 if (_enableDart2JSHints) { | |
4714 unit.accept(new Dart2JSVerifier(errorReporter)); | |
4715 } | |
4716 // Dart best practices | |
4717 unit.accept( | |
4718 new BestPracticesVerifier(errorReporter, _context.typeProvider)); | |
4719 unit.accept(new OverrideVerifier(errorReporter, _manager)); | |
4720 // Find to-do comments | |
4721 new ToDoFinder(errorReporter).findIn(unit); | |
4722 // pub analysis | |
4723 // TODO(danrubel/jwren) Commented out until bugs in the pub verifier are | |
4724 // fixed | |
4725 // unit.accept(new PubVerifier(context, errorReporter)); | |
4726 } | |
4727 } | |
4728 | |
4729 /** | |
4730 * Instances of the class {@code HtmlTagInfo} record information about the tags
used in an HTML | |
4731 * file. | |
4732 */ | |
4733 class HtmlTagInfo { | |
4734 /** | |
4735 * An array containing all of the tags used in the HTML file. | |
4736 */ | |
4737 List<String> allTags; | |
4738 | |
4739 /** | |
4740 * A table mapping the id's defined in the HTML file to an array containing th
e names of tags with | |
4741 * that identifier. | |
4742 */ | |
4743 HashMap<String, String> idToTagMap; | |
4744 | |
4745 /** | |
4746 * A table mapping the classes defined in the HTML file to an array containing
the names of tags | |
4747 * with that class. | |
4748 */ | |
4749 HashMap<String, List<String>> classToTagsMap; | |
4750 | |
4751 /** | |
4752 * Initialize a newly created information holder to hold the given information
about the tags in | |
4753 * an HTML file. | |
4754 * | |
4755 * @param allTags an array containing all of the tags used in the HTML file | |
4756 * @param idToTagMap a table mapping the id's defined in the HTML file to an a
rray containing the | |
4757 * names of tags with that identifier | |
4758 * @param classToTagsMap a table mapping the classes defined in the HTML file
to an array | |
4759 * containing the names of tags with that class | |
4760 */ | |
4761 HtmlTagInfo(List<String> allTags, HashMap<String, String> idToTagMap, | |
4762 HashMap<String, List<String>> classToTagsMap) { | |
4763 this.allTags = allTags; | |
4764 this.idToTagMap = idToTagMap; | |
4765 this.classToTagsMap = classToTagsMap; | |
4766 } | |
4767 | |
4768 /** | |
4769 * Return an array containing the tags that have the given class, or {@code nu
ll} if there are no | |
4770 * such tags. | |
4771 * | |
4772 * @return an array containing the tags that have the given class | |
4773 */ | |
4774 List<String> getTagsWithClass(String identifier) { | |
4775 return classToTagsMap[identifier]; | |
4776 } | |
4777 | |
4778 /** | |
4779 * Return the tag that has the given identifier, or {@code null} if there is n
o such tag (the | |
4780 * identifier is not defined). | |
4781 * | |
4782 * @return the tag that has the given identifier | |
4783 */ | |
4784 String getTagWithId(String identifier) { | |
4785 return idToTagMap[identifier]; | |
4786 } | |
4787 } | |
4788 | |
4789 /** | |
4790 * Instances of the class {@code HtmlTagInfoBuilder} gather information about th
e tags used in one | |
4791 * or more HTML structures. | |
4792 */ | |
4793 class HtmlTagInfoBuilder implements ht.XmlVisitor { | |
4794 /** | |
4795 * The name of the 'id' attribute. | |
4796 */ | |
4797 static final String ID_ATTRIBUTE = "id"; | |
4798 | |
4799 /** | |
4800 * The name of the 'class' attribute. | |
4801 */ | |
4802 static final String ID_CLASS = "class"; | |
4803 | |
4804 /** | |
4805 * A set containing all of the tag names used in the HTML. | |
4806 */ | |
4807 HashSet<String> tagSet = new HashSet<String>(); | |
4808 | |
4809 /** | |
4810 * A table mapping the id's that are defined to the tag name with that id. | |
4811 */ | |
4812 HashMap<String, String> idMap = new HashMap<String, String>(); | |
4813 | |
4814 /** | |
4815 * A table mapping the classes that are defined to a set of the tag names with
that class. | |
4816 */ | |
4817 HashMap<String, HashSet<String>> classMap = | |
4818 new HashMap<String, HashSet<String>>(); | |
4819 | |
4820 /** | |
4821 * Initialize a newly created HTML tag info builder. | |
4822 */ | |
4823 HtmlTagInfoBuilder(); | |
4824 | |
4825 /** | |
4826 * Create a tag information holder holding all of the information gathered abo
ut the tags in the | |
4827 * HTML structures that were visited. | |
4828 * | |
4829 * @return the information gathered about the tags in the visited HTML structu
res | |
4830 */ | |
4831 HtmlTagInfo getTagInfo() { | |
4832 List<String> allTags = tagSet.toList(); | |
4833 HashMap<String, List<String>> classToTagsMap = | |
4834 new HashMap<String, List<String>>(); | |
4835 classMap.forEach((String key, Set<String> tags) { | |
4836 classToTagsMap[key] = tags.toList(); | |
4837 }); | |
4838 return new HtmlTagInfo(allTags, idMap, classToTagsMap); | |
4839 } | |
4840 | |
4841 @override | |
4842 visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { | |
4843 visitXmlTagNode(node); | |
4844 } | |
4845 | |
4846 @override | |
4847 visitHtmlUnit(ht.HtmlUnit node) { | |
4848 node.visitChildren(this); | |
4849 } | |
4850 | |
4851 @override | |
4852 visitXmlAttributeNode(ht.XmlAttributeNode node) {} | |
4853 | |
4854 @override | |
4855 visitXmlTagNode(ht.XmlTagNode node) { | |
4856 node.visitChildren(this); | |
4857 String tagName = node.tag; | |
4858 tagSet.add(tagName); | |
4859 for (ht.XmlAttributeNode attribute in node.attributes) { | |
4860 String attributeName = attribute.name; | |
4861 if (attributeName == ID_ATTRIBUTE) { | |
4862 String attributeValue = attribute.text; | |
4863 if (attributeValue != null) { | |
4864 String tag = idMap[attributeValue]; | |
4865 if (tag == null) { | |
4866 idMap[attributeValue] = tagName; | |
4867 } else { | |
4868 // reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken); | |
4869 } | |
4870 } | |
4871 } else if (attributeName == ID_CLASS) { | |
4872 String attributeValue = attribute.text; | |
4873 if (attributeValue != null) { | |
4874 HashSet<String> tagList = classMap[attributeValue]; | |
4875 if (tagList == null) { | |
4876 tagList = new HashSet<String>(); | |
4877 classMap[attributeValue] = tagList; | |
4878 } else { | |
4879 // reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken); | |
4880 } | |
4881 tagList.add(tagName); | |
4882 } | |
4883 } | |
4884 } | |
4885 } | |
4886 | |
4887 // /** | |
4888 // * Report an error with the given error code at the given location. Use the
given arguments to | |
4889 // * compose the error message. | |
4890 // * | |
4891 // * @param errorCode the error code of the error to be reported | |
4892 // * @param offset the offset of the first character to be highlighted | |
4893 // * @param length the number of characters to be highlighted | |
4894 // * @param arguments the arguments used to compose the error message | |
4895 // */ | |
4896 // private void reportError(ErrorCode errorCode, Token token, Object... argumen
ts) { | |
4897 // errorListener.onError(new AnalysisError( | |
4898 // htmlElement.getSource(), | |
4899 // token.getOffset(), | |
4900 // token.getLength(), | |
4901 // errorCode, | |
4902 // arguments)); | |
4903 // } | |
4904 // | |
4905 // /** | |
4906 // * Report an error with the given error code at the given location. Use the
given arguments to | |
4907 // * compose the error message. | |
4908 // * | |
4909 // * @param errorCode the error code of the error to be reported | |
4910 // * @param offset the offset of the first character to be highlighted | |
4911 // * @param length the number of characters to be highlighted | |
4912 // * @param arguments the arguments used to compose the error message | |
4913 // */ | |
4914 // private void reportError(ErrorCode errorCode, int offset, int length, Object
... arguments) { | |
4915 // errorListener.onError(new AnalysisError( | |
4916 // htmlElement.getSource(), | |
4917 // offset, | |
4918 // length, | |
4919 // errorCode, | |
4920 // arguments)); | |
4921 // } | |
4922 } | |
4923 | |
4924 /** | |
4925 * Instances of the class `HtmlUnitBuilder` build an element model for a single
HTML unit. | |
4926 */ | |
4927 class HtmlUnitBuilder implements ht.XmlVisitor<Object> { | |
4928 static String _SRC = "src"; | |
4929 | |
4930 /** | |
4931 * The analysis context in which the element model will be built. | |
4932 */ | |
4933 final InternalAnalysisContext _context; | |
4934 | |
4935 /** | |
4936 * The error listener to which errors will be reported. | |
4937 */ | |
4938 RecordingErrorListener _errorListener; | |
4939 | |
4940 /** | |
4941 * The HTML element being built. | |
4942 */ | |
4943 HtmlElementImpl _htmlElement; | |
4944 | |
4945 /** | |
4946 * The elements in the path from the HTML unit to the current tag node. | |
4947 */ | |
4948 List<ht.XmlTagNode> _parentNodes; | |
4949 | |
4950 /** | |
4951 * The script elements being built. | |
4952 */ | |
4953 List<HtmlScriptElement> _scripts; | |
4954 | |
4955 /** | |
4956 * A set of the libraries that were resolved while resolving the HTML unit. | |
4957 */ | |
4958 Set<Library> _resolvedLibraries = new HashSet<Library>(); | |
4959 | |
4960 /** | |
4961 * Initialize a newly created HTML unit builder. | |
4962 * | |
4963 * @param context the analysis context in which the element model will be buil
t | |
4964 */ | |
4965 HtmlUnitBuilder(this._context) { | |
4966 this._errorListener = new RecordingErrorListener(); | |
4967 } | |
4968 | |
4969 /** | |
4970 * Return the listener to which analysis errors will be reported. | |
4971 * | |
4972 * @return the listener to which analysis errors will be reported | |
4973 */ | |
4974 RecordingErrorListener get errorListener => _errorListener; | |
4975 | |
4976 /** | |
4977 * Return an array containing information about all of the libraries that were
resolved. | |
4978 * | |
4979 * @return an array containing the libraries that were resolved | |
4980 */ | |
4981 Set<Library> get resolvedLibraries => _resolvedLibraries; | |
4982 | |
4983 /** | |
4984 * Build the HTML element for the given source. | |
4985 * | |
4986 * @param source the source describing the compilation unit | |
4987 * @param unit the AST structure representing the HTML | |
4988 * @throws AnalysisException if the analysis could not be performed | |
4989 */ | |
4990 HtmlElementImpl buildHtmlElement(Source source, ht.HtmlUnit unit) { | |
4991 HtmlElementImpl result = new HtmlElementImpl(_context, source.shortName); | |
4992 result.source = source; | |
4993 _htmlElement = result; | |
4994 unit.accept(this); | |
4995 _htmlElement = null; | |
4996 unit.element = result; | |
4997 return result; | |
4998 } | |
4999 | |
5000 @override | |
5001 Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { | |
5002 if (_parentNodes.contains(node)) { | |
5003 return _reportCircularity(node); | |
5004 } | |
5005 _parentNodes.add(node); | |
5006 try { | |
5007 Source htmlSource = _htmlElement.source; | |
5008 ht.XmlAttributeNode scriptAttribute = _getScriptSourcePath(node); | |
5009 String scriptSourcePath = | |
5010 scriptAttribute == null ? null : scriptAttribute.text; | |
5011 if (node.attributeEnd.type == ht.TokenType.GT && | |
5012 scriptSourcePath == null) { | |
5013 EmbeddedHtmlScriptElementImpl script = | |
5014 new EmbeddedHtmlScriptElementImpl(node); | |
5015 try { | |
5016 LibraryResolver resolver = new LibraryResolver(_context); | |
5017 LibraryElementImpl library = | |
5018 resolver.resolveEmbeddedLibrary(htmlSource, node.script, true); | |
5019 script.scriptLibrary = library; | |
5020 _resolvedLibraries.addAll(resolver.resolvedLibraries); | |
5021 _errorListener.addAll(resolver.errorListener); | |
5022 } on AnalysisException catch (exception, stackTrace) { | |
5023 //TODO (danrubel): Handle or forward the exception | |
5024 AnalysisEngine.instance.logger.logError( | |
5025 "Could not resolve script tag", | |
5026 new CaughtException(exception, stackTrace)); | |
5027 } | |
5028 node.scriptElement = script; | |
5029 _scripts.add(script); | |
5030 } else { | |
5031 ExternalHtmlScriptElementImpl script = | |
5032 new ExternalHtmlScriptElementImpl(node); | |
5033 if (scriptSourcePath != null) { | |
5034 try { | |
5035 scriptSourcePath = Uri.encodeFull(scriptSourcePath); | |
5036 // Force an exception to be thrown if the URI is invalid so that we | |
5037 // can report the problem. | |
5038 parseUriWithException(scriptSourcePath); | |
5039 Source scriptSource = | |
5040 _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath); | |
5041 script.scriptSource = scriptSource; | |
5042 if (!_context.exists(scriptSource)) { | |
5043 _reportValueError(HtmlWarningCode.URI_DOES_NOT_EXIST, | |
5044 scriptAttribute, [scriptSourcePath]); | |
5045 } | |
5046 } on URISyntaxException { | |
5047 _reportValueError(HtmlWarningCode.INVALID_URI, scriptAttribute, | |
5048 [scriptSourcePath]); | |
5049 } | |
5050 } | |
5051 node.scriptElement = script; | |
5052 _scripts.add(script); | |
5053 } | |
5054 } finally { | |
5055 _parentNodes.remove(node); | |
5056 } | |
5057 return null; | |
5058 } | |
5059 | |
5060 @override | |
5061 Object visitHtmlUnit(ht.HtmlUnit node) { | |
5062 _parentNodes = new List<ht.XmlTagNode>(); | |
5063 _scripts = new List<HtmlScriptElement>(); | |
5064 try { | |
5065 node.visitChildren(this); | |
5066 _htmlElement.scripts = new List.from(_scripts); | |
5067 } finally { | |
5068 _scripts = null; | |
5069 _parentNodes = null; | |
5070 } | |
5071 return null; | |
5072 } | |
5073 | |
5074 @override | |
5075 Object visitXmlAttributeNode(ht.XmlAttributeNode node) => null; | |
5076 | |
5077 @override | |
5078 Object visitXmlTagNode(ht.XmlTagNode node) { | |
5079 if (_parentNodes.contains(node)) { | |
5080 return _reportCircularity(node); | |
5081 } | |
5082 _parentNodes.add(node); | |
5083 try { | |
5084 node.visitChildren(this); | |
5085 } finally { | |
5086 _parentNodes.remove(node); | |
5087 } | |
5088 return null; | |
5089 } | |
5090 | |
5091 /** | |
5092 * Return the first source attribute for the given tag node, or `null` if it d
oes not exist. | |
5093 * | |
5094 * @param node the node containing attributes | |
5095 * @return the source attribute contained in the given tag | |
5096 */ | |
5097 ht.XmlAttributeNode _getScriptSourcePath(ht.XmlTagNode node) { | |
5098 for (ht.XmlAttributeNode attribute in node.attributes) { | |
5099 if (attribute.name == _SRC) { | |
5100 return attribute; | |
5101 } | |
5102 } | |
5103 return null; | |
5104 } | |
5105 | |
5106 Object _reportCircularity(ht.XmlTagNode node) { | |
5107 // | |
5108 // This should not be possible, but we have an error report that suggests | |
5109 // that it happened at least once. This code will guard against infinite | |
5110 // recursion and might help us identify the cause of the issue. | |
5111 // | |
5112 StringBuffer buffer = new StringBuffer(); | |
5113 buffer.write("Found circularity in XML nodes: "); | |
5114 bool first = true; | |
5115 for (ht.XmlTagNode pathNode in _parentNodes) { | |
5116 if (first) { | |
5117 first = false; | |
5118 } else { | |
5119 buffer.write(", "); | |
5120 } | |
5121 String tagName = pathNode.tag; | |
5122 if (identical(pathNode, node)) { | |
5123 buffer.write("*"); | |
5124 buffer.write(tagName); | |
5125 buffer.write("*"); | |
5126 } else { | |
5127 buffer.write(tagName); | |
5128 } | |
5129 } | |
5130 AnalysisEngine.instance.logger.logError(buffer.toString()); | |
5131 return null; | |
5132 } | |
5133 | |
5134 /** | |
5135 * Report an error with the given error code at the given location. Use the gi
ven arguments to | |
5136 * compose the error message. | |
5137 * | |
5138 * @param errorCode the error code of the error to be reported | |
5139 * @param offset the offset of the first character to be highlighted | |
5140 * @param length the number of characters to be highlighted | |
5141 * @param arguments the arguments used to compose the error message | |
5142 */ | |
5143 void _reportErrorForOffset( | |
5144 ErrorCode errorCode, int offset, int length, List<Object> arguments) { | |
5145 _errorListener.onError(new AnalysisError( | |
5146 _htmlElement.source, offset, length, errorCode, arguments)); | |
5147 } | |
5148 | |
5149 /** | |
5150 * Report an error with the given error code at the location of the value of t
he given attribute. | |
5151 * Use the given arguments to compose the error message. | |
5152 * | |
5153 * @param errorCode the error code of the error to be reported | |
5154 * @param offset the offset of the first character to be highlighted | |
5155 * @param length the number of characters to be highlighted | |
5156 * @param arguments the arguments used to compose the error message | |
5157 */ | |
5158 void _reportValueError(ErrorCode errorCode, ht.XmlAttributeNode attribute, | |
5159 List<Object> arguments) { | |
5160 int offset = attribute.valueToken.offset + 1; | |
5161 int length = attribute.valueToken.length - 2; | |
5162 _reportErrorForOffset(errorCode, offset, length, arguments); | |
5163 } | |
5164 } | |
5165 | |
5166 /** | |
5167 * Instances of the class `ImplicitLabelScope` represent the scope statements | |
5168 * that can be the target of unlabeled break and continue statements. | |
5169 */ | |
5170 class ImplicitLabelScope { | |
5171 /** | |
5172 * The implicit label scope associated with the top level of a function. | |
5173 */ | |
5174 static const ImplicitLabelScope ROOT = const ImplicitLabelScope._(null, null); | |
5175 | |
5176 /** | |
5177 * The implicit label scope enclosing this implicit label scope. | |
5178 */ | |
5179 final ImplicitLabelScope outerScope; | |
5180 | |
5181 /** | |
5182 * The statement that acts as a target for break and/or continue statements | |
5183 * at this scoping level. | |
5184 */ | |
5185 final Statement statement; | |
5186 | |
5187 /** | |
5188 * Private constructor. | |
5189 */ | |
5190 const ImplicitLabelScope._(this.outerScope, this.statement); | |
5191 | |
5192 /** | |
5193 * Get the statement which should be the target of an unlabeled `break` or | |
5194 * `continue` statement, or `null` if there is no appropriate target. | |
5195 */ | |
5196 Statement getTarget(bool isContinue) { | |
5197 if (outerScope == null) { | |
5198 // This scope represents the toplevel of a function body, so it doesn't | |
5199 // match either break or continue. | |
5200 return null; | |
5201 } | |
5202 if (isContinue && statement is SwitchStatement) { | |
5203 return outerScope.getTarget(isContinue); | |
5204 } | |
5205 return statement; | |
5206 } | |
5207 | |
5208 /** | |
5209 * Initialize a newly created scope to represent a switch statement or loop | |
5210 * nested within the current scope. [statement] is the statement associated | |
5211 * with the newly created scope. | |
5212 */ | |
5213 ImplicitLabelScope nest(Statement statement) => | |
5214 new ImplicitLabelScope._(this, statement); | |
5215 } | |
5216 | |
5217 /** | |
5218 * Instances of the class `ImportsVerifier` visit all of the referenced librarie
s in the | |
5219 * source code verifying that all of the imports are used, otherwise a | |
5220 * [HintCode.UNUSED_IMPORT] is generated with | |
5221 * [generateUnusedImportHints]. | |
5222 * | |
5223 * While this class does not yet have support for an "Organize Imports" action,
this logic built up | |
5224 * in this class could be used for such an action in the future. | |
5225 */ | |
5226 class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ { | |
5227 /** | |
5228 * A list of [ImportDirective]s that the current library imports, as identifie
rs are visited | |
5229 * by this visitor and an import has been identified as being used by the libr
ary, the | |
5230 * [ImportDirective] is removed from this list. After all the sources in the l
ibrary have | |
5231 * been evaluated, this list represents the set of unused imports. | |
5232 * | |
5233 * See [ImportsVerifier.generateUnusedImportErrors]. | |
5234 */ | |
5235 final List<ImportDirective> _unusedImports = <ImportDirective>[]; | |
5236 | |
5237 /** | |
5238 * After the list of [unusedImports] has been computed, this list is a proper
subset of the | |
5239 * unused imports that are listed more than once. | |
5240 */ | |
5241 final List<ImportDirective> _duplicateImports = <ImportDirective>[]; | |
5242 | |
5243 /** | |
5244 * This is a map between the set of [LibraryElement]s that the current library
imports, and | |
5245 * a list of [ImportDirective]s that imports the library. In cases where the c
urrent library | |
5246 * imports a library with a single directive (such as `import lib1.dart;`), th
e library | |
5247 * element will map to a list of one [ImportDirective], which will then be rem
oved from the | |
5248 * [unusedImports] list. In cases where the current library imports a library
with multiple | |
5249 * directives (such as `import lib1.dart; import lib1.dart show C;`), the | |
5250 * [LibraryElement] will be mapped to a list of the import directives, and the
namespace | |
5251 * will need to be used to compute the correct [ImportDirective] being used, s
ee | |
5252 * [namespaceMap]. | |
5253 */ | |
5254 final HashMap<LibraryElement, List<ImportDirective>> _libraryMap = | |
5255 new HashMap<LibraryElement, List<ImportDirective>>(); | |
5256 | |
5257 /** | |
5258 * In cases where there is more than one import directive per library element,
this mapping is | |
5259 * used to determine which of the multiple import directives are used by gener
ating a | |
5260 * [Namespace] for each of the imports to do lookups in the same way that they
are done from | |
5261 * the [ElementResolver]. | |
5262 */ | |
5263 final HashMap<ImportDirective, Namespace> _namespaceMap = | |
5264 new HashMap<ImportDirective, Namespace>(); | |
5265 | |
5266 /** | |
5267 * This is a map between prefix elements and the import directives from which
they are derived. In | |
5268 * cases where a type is referenced via a prefix element, the import directive
can be marked as | |
5269 * used (removed from the unusedImports) by looking at the resolved `lib` in `
lib.X`, | |
5270 * instead of looking at which library the `lib.X` resolves. | |
5271 * | |
5272 * TODO (jwren) Since multiple [ImportDirective]s can share the same [PrefixEl
ement], | |
5273 * it is possible to have an unreported unused import in situations where two
imports use the same | |
5274 * prefix and at least one import directive is used. | |
5275 */ | |
5276 final HashMap<PrefixElement, List<ImportDirective>> _prefixElementMap = | |
5277 new HashMap<PrefixElement, List<ImportDirective>>(); | |
5278 | |
5279 void addImports(CompilationUnit node) { | |
5280 for (Directive directive in node.directives) { | |
5281 if (directive is ImportDirective) { | |
5282 ImportDirective importDirective = directive; | |
5283 LibraryElement libraryElement = importDirective.uriElement; | |
5284 if (libraryElement != null) { | |
5285 _unusedImports.add(importDirective); | |
5286 // | |
5287 // Initialize prefixElementMap | |
5288 // | |
5289 if (importDirective.asKeyword != null) { | |
5290 SimpleIdentifier prefixIdentifier = importDirective.prefix; | |
5291 if (prefixIdentifier != null) { | |
5292 Element element = prefixIdentifier.staticElement; | |
5293 if (element is PrefixElement) { | |
5294 PrefixElement prefixElementKey = element; | |
5295 List<ImportDirective> list = | |
5296 _prefixElementMap[prefixElementKey]; | |
5297 if (list == null) { | |
5298 list = new List<ImportDirective>(); | |
5299 _prefixElementMap[prefixElementKey] = list; | |
5300 } | |
5301 list.add(importDirective); | |
5302 } | |
5303 // TODO (jwren) Can the element ever not be a PrefixElement? | |
5304 } | |
5305 } | |
5306 // | |
5307 // Initialize libraryMap: libraryElement -> importDirective | |
5308 // | |
5309 _putIntoLibraryMap(libraryElement, importDirective); | |
5310 // | |
5311 // For this new addition to the libraryMap, also recursively add any | |
5312 // exports from the libraryElement. | |
5313 // | |
5314 _addAdditionalLibrariesForExports( | |
5315 libraryElement, importDirective, new List<LibraryElement>()); | |
5316 } | |
5317 } | |
5318 } | |
5319 if (_unusedImports.length > 1) { | |
5320 // order the list of unusedImports to find duplicates in faster than | |
5321 // O(n^2) time | |
5322 List<ImportDirective> importDirectiveArray = | |
5323 new List<ImportDirective>.from(_unusedImports); | |
5324 importDirectiveArray.sort(ImportDirective.COMPARATOR); | |
5325 ImportDirective currentDirective = importDirectiveArray[0]; | |
5326 for (int i = 1; i < importDirectiveArray.length; i++) { | |
5327 ImportDirective nextDirective = importDirectiveArray[i]; | |
5328 if (ImportDirective.COMPARATOR(currentDirective, nextDirective) == 0) { | |
5329 // Add either the currentDirective or nextDirective depending on which | |
5330 // comes second, this guarantees that the first of the duplicates | |
5331 // won't be highlighted. | |
5332 if (currentDirective.offset < nextDirective.offset) { | |
5333 _duplicateImports.add(nextDirective); | |
5334 } else { | |
5335 _duplicateImports.add(currentDirective); | |
5336 } | |
5337 } | |
5338 currentDirective = nextDirective; | |
5339 } | |
5340 } | |
5341 } | |
5342 | |
5343 /** | |
5344 * Any time after the defining compilation unit has been visited by this visit
or, this method can | |
5345 * be called to report an [HintCode.DUPLICATE_IMPORT] hint for each of the imp
ort directives | |
5346 * in the [duplicateImports] list. | |
5347 * | |
5348 * @param errorReporter the error reporter to report the set of [HintCode.DUPL
ICATE_IMPORT] | |
5349 * hints to | |
5350 */ | |
5351 void generateDuplicateImportHints(ErrorReporter errorReporter) { | |
5352 for (ImportDirective duplicateImport in _duplicateImports) { | |
5353 errorReporter.reportErrorForNode( | |
5354 HintCode.DUPLICATE_IMPORT, duplicateImport.uri); | |
5355 } | |
5356 } | |
5357 | |
5358 /** | |
5359 * After all of the compilation units have been visited by this visitor, this
method can be called | |
5360 * to report an [HintCode.UNUSED_IMPORT] hint for each of the import directive
s in the | |
5361 * [unusedImports] list. | |
5362 * | |
5363 * @param errorReporter the error reporter to report the set of [HintCode.UNUS
ED_IMPORT] | |
5364 * hints to | |
5365 */ | |
5366 void generateUnusedImportHints(ErrorReporter errorReporter) { | |
5367 for (ImportDirective unusedImport in _unusedImports) { | |
5368 // Check that the import isn't dart:core | |
5369 ImportElement importElement = unusedImport.element; | |
5370 if (importElement != null) { | |
5371 LibraryElement libraryElement = importElement.importedLibrary; | |
5372 if (libraryElement != null && libraryElement.isDartCore) { | |
5373 continue; | |
5374 } | |
5375 } | |
5376 errorReporter.reportErrorForNode( | |
5377 HintCode.UNUSED_IMPORT, unusedImport.uri); | |
5378 } | |
5379 } | |
5380 | |
5381 /** | |
5382 * Remove elements from [_unusedImports] using the given [usedElements]. | |
5383 */ | |
5384 void removeUsedElements(UsedImportedElements usedElements) { | |
5385 // Stop if all the imports are known to be used. | |
5386 if (_unusedImports.isEmpty) { | |
5387 return; | |
5388 } | |
5389 // Process import prefixes. | |
5390 for (PrefixElement prefix in usedElements.prefixes) { | |
5391 List<ImportDirective> importDirectives = _prefixElementMap[prefix]; | |
5392 if (importDirectives != null) { | |
5393 for (ImportDirective importDirective in importDirectives) { | |
5394 _unusedImports.remove(importDirective); | |
5395 } | |
5396 } | |
5397 } | |
5398 // Process top-level elements. | |
5399 for (Element element in usedElements.elements) { | |
5400 // Stop if all the imports are known to be used. | |
5401 if (_unusedImports.isEmpty) { | |
5402 return; | |
5403 } | |
5404 // Prepare import directives for this library. | |
5405 LibraryElement library = element.library; | |
5406 List<ImportDirective> importsLibrary = _libraryMap[library]; | |
5407 if (importsLibrary == null) { | |
5408 continue; | |
5409 } | |
5410 // If there is only one import directive for this library, then it must be | |
5411 // the directive that this element is imported with, remove it from the | |
5412 // unusedImports list. | |
5413 if (importsLibrary.length == 1) { | |
5414 ImportDirective usedImportDirective = importsLibrary[0]; | |
5415 _unusedImports.remove(usedImportDirective); | |
5416 continue; | |
5417 } | |
5418 // Otherwise, find import directives using namespaces. | |
5419 String name = element.displayName; | |
5420 for (ImportDirective importDirective in importsLibrary) { | |
5421 Namespace namespace = _computeNamespace(importDirective); | |
5422 if (namespace != null && namespace.get(name) != null) { | |
5423 _unusedImports.remove(importDirective); | |
5424 } | |
5425 } | |
5426 } | |
5427 } | |
5428 | |
5429 /** | |
5430 * Recursively add any exported library elements into the [libraryMap]. | |
5431 */ | |
5432 void _addAdditionalLibrariesForExports(LibraryElement library, | |
5433 ImportDirective importDirective, List<LibraryElement> exportPath) { | |
5434 if (exportPath.contains(library)) { | |
5435 return; | |
5436 } | |
5437 exportPath.add(library); | |
5438 for (LibraryElement exportedLibraryElt in library.exportedLibraries) { | |
5439 _putIntoLibraryMap(exportedLibraryElt, importDirective); | |
5440 _addAdditionalLibrariesForExports( | |
5441 exportedLibraryElt, importDirective, exportPath); | |
5442 } | |
5443 } | |
5444 | |
5445 /** | |
5446 * Lookup and return the [Namespace] from the [namespaceMap], if the map does
not | |
5447 * have the computed namespace, compute it and cache it in the map. If the imp
ort directive is not | |
5448 * resolved or is not resolvable, `null` is returned. | |
5449 * | |
5450 * @param importDirective the import directive used to compute the returned na
mespace | |
5451 * @return the computed or looked up [Namespace] | |
5452 */ | |
5453 Namespace _computeNamespace(ImportDirective importDirective) { | |
5454 Namespace namespace = _namespaceMap[importDirective]; | |
5455 if (namespace == null) { | |
5456 // If the namespace isn't in the namespaceMap, then compute and put it in | |
5457 // the map. | |
5458 ImportElement importElement = importDirective.element; | |
5459 if (importElement != null) { | |
5460 NamespaceBuilder builder = new NamespaceBuilder(); | |
5461 namespace = builder.createImportNamespaceForDirective(importElement); | |
5462 _namespaceMap[importDirective] = namespace; | |
5463 } | |
5464 } | |
5465 return namespace; | |
5466 } | |
5467 | |
5468 /** | |
5469 * The [libraryMap] is a mapping between a library elements and a list of impo
rt | |
5470 * directives, but when adding these mappings into the [libraryMap], this meth
od can be | |
5471 * used to simply add the mapping between the library element an an import dir
ective without | |
5472 * needing to check to see if a list needs to be created. | |
5473 */ | |
5474 void _putIntoLibraryMap( | |
5475 LibraryElement libraryElement, ImportDirective importDirective) { | |
5476 List<ImportDirective> importList = _libraryMap[libraryElement]; | |
5477 if (importList == null) { | |
5478 importList = new List<ImportDirective>(); | |
5479 _libraryMap[libraryElement] = importList; | |
5480 } | |
5481 importList.add(importDirective); | |
5482 } | |
5483 } | |
5484 | |
5485 /** | |
5486 * Instances of the class `InheritanceManager` manage the knowledge of where cla
ss members | |
5487 * (methods, getters & setters) are inherited from. | |
5488 */ | |
5489 class InheritanceManager { | |
5490 /** | |
5491 * The [LibraryElement] that is managed by this manager. | |
5492 */ | |
5493 LibraryElement _library; | |
5494 | |
5495 /** | |
5496 * This is a mapping between each [ClassElement] and a map between the [String
] member | |
5497 * names and the associated [ExecutableElement] in the mixin and superclass ch
ain. | |
5498 */ | |
5499 HashMap<ClassElement, MemberMap> _classLookup; | |
5500 | |
5501 /** | |
5502 * This is a mapping between each [ClassElement] and a map between the [String
] member | |
5503 * names and the associated [ExecutableElement] in the interface set. | |
5504 */ | |
5505 HashMap<ClassElement, MemberMap> _interfaceLookup; | |
5506 | |
5507 /** | |
5508 * A map between each visited [ClassElement] and the set of [AnalysisError]s f
ound on | |
5509 * the class element. | |
5510 */ | |
5511 HashMap<ClassElement, HashSet<AnalysisError>> _errorsInClassElement = | |
5512 new HashMap<ClassElement, HashSet<AnalysisError>>(); | |
5513 | |
5514 /** | |
5515 * Initialize a newly created inheritance manager. | |
5516 * | |
5517 * @param library the library element context that the inheritance mappings ar
e being generated | |
5518 */ | |
5519 InheritanceManager(LibraryElement library) { | |
5520 this._library = library; | |
5521 _classLookup = new HashMap<ClassElement, MemberMap>(); | |
5522 _interfaceLookup = new HashMap<ClassElement, MemberMap>(); | |
5523 } | |
5524 | |
5525 /** | |
5526 * Set the new library element context. | |
5527 * | |
5528 * @param library the new library element | |
5529 */ | |
5530 void set libraryElement(LibraryElement library) { | |
5531 this._library = library; | |
5532 } | |
5533 | |
5534 /** | |
5535 * Return the set of [AnalysisError]s found on the passed [ClassElement], or | |
5536 * `null` if there are none. | |
5537 * | |
5538 * @param classElt the class element to query | |
5539 * @return the set of [AnalysisError]s found on the passed [ClassElement], or | |
5540 * `null` if there are none | |
5541 */ | |
5542 HashSet<AnalysisError> getErrors(ClassElement classElt) => | |
5543 _errorsInClassElement[classElt]; | |
5544 | |
5545 /** | |
5546 * Get and return a mapping between the set of all string names of the members
inherited from the | |
5547 * passed [ClassElement] superclass hierarchy, and the associated [ExecutableE
lement]. | |
5548 * | |
5549 * @param classElt the class element to query | |
5550 * @return a mapping between the set of all members inherited from the passed
[ClassElement] | |
5551 * superclass hierarchy, and the associated [ExecutableElement] | |
5552 */ | |
5553 MemberMap getMapOfMembersInheritedFromClasses(ClassElement classElt) => | |
5554 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>()); | |
5555 | |
5556 /** | |
5557 * Get and return a mapping between the set of all string names of the members
inherited from the | |
5558 * passed [ClassElement] interface hierarchy, and the associated [ExecutableEl
ement]. | |
5559 * | |
5560 * @param classElt the class element to query | |
5561 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
5562 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement]. | |
5563 */ | |
5564 MemberMap getMapOfMembersInheritedFromInterfaces(ClassElement classElt) => | |
5565 _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()); | |
5566 | |
5567 /** | |
5568 * Given some [ClassElement] and some member name, this returns the | |
5569 * [ExecutableElement] that the class inherits from the mixins, | |
5570 * superclasses or interfaces, that has the member name, if no member is inher
ited `null` is | |
5571 * returned. | |
5572 * | |
5573 * @param classElt the class element to query | |
5574 * @param memberName the name of the executable element to find and return | |
5575 * @return the inherited executable element with the member name, or `null` if
no such | |
5576 * member exists | |
5577 */ | |
5578 ExecutableElement lookupInheritance( | |
5579 ClassElement classElt, String memberName) { | |
5580 if (memberName == null || memberName.isEmpty) { | |
5581 return null; | |
5582 } | |
5583 ExecutableElement executable = _computeClassChainLookupMap( | |
5584 classElt, new HashSet<ClassElement>()).get(memberName); | |
5585 if (executable == null) { | |
5586 return _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()) | |
5587 .get(memberName); | |
5588 } | |
5589 return executable; | |
5590 } | |
5591 | |
5592 /** | |
5593 * Given some [ClassElement] and some member name, this returns the | |
5594 * [ExecutableElement] that the class either declares itself, or | |
5595 * inherits, that has the member name, if no member is inherited `null` is ret
urned. | |
5596 * | |
5597 * @param classElt the class element to query | |
5598 * @param memberName the name of the executable element to find and return | |
5599 * @return the inherited executable element with the member name, or `null` if
no such | |
5600 * member exists | |
5601 */ | |
5602 ExecutableElement lookupMember(ClassElement classElt, String memberName) { | |
5603 ExecutableElement element = _lookupMemberInClass(classElt, memberName); | |
5604 if (element != null) { | |
5605 return element; | |
5606 } | |
5607 return lookupInheritance(classElt, memberName); | |
5608 } | |
5609 | |
5610 /** | |
5611 * Given some [InterfaceType] and some member name, this returns the | |
5612 * [FunctionType] of the [ExecutableElement] that the | |
5613 * class either declares itself, or inherits, that has the member name, if no
member is inherited | |
5614 * `null` is returned. The returned [FunctionType] has all type | |
5615 * parameters substituted with corresponding type arguments from the given [In
terfaceType]. | |
5616 * | |
5617 * @param interfaceType the interface type to query | |
5618 * @param memberName the name of the executable element to find and return | |
5619 * @return the member's function type, or `null` if no such member exists | |
5620 */ | |
5621 FunctionType lookupMemberType( | |
5622 InterfaceType interfaceType, String memberName) { | |
5623 ExecutableElement iteratorMember = | |
5624 lookupMember(interfaceType.element, memberName); | |
5625 if (iteratorMember == null) { | |
5626 return null; | |
5627 } | |
5628 return substituteTypeArgumentsInMemberFromInheritance( | |
5629 iteratorMember.type, memberName, interfaceType); | |
5630 } | |
5631 | |
5632 /** | |
5633 * Determine the set of methods which is overridden by the given class member.
If no member is | |
5634 * inherited, an empty list is returned. If one of the inherited members is a | |
5635 * [MultiplyInheritedExecutableElement], then it is expanded into its constitu
ent inherited | |
5636 * elements. | |
5637 * | |
5638 * @param classElt the class to query | |
5639 * @param memberName the name of the class member to query | |
5640 * @return a list of overridden methods | |
5641 */ | |
5642 List<ExecutableElement> lookupOverrides( | |
5643 ClassElement classElt, String memberName) { | |
5644 List<ExecutableElement> result = new List<ExecutableElement>(); | |
5645 if (memberName == null || memberName.isEmpty) { | |
5646 return result; | |
5647 } | |
5648 List<MemberMap> interfaceMaps = | |
5649 _gatherInterfaceLookupMaps(classElt, new HashSet<ClassElement>()); | |
5650 if (interfaceMaps != null) { | |
5651 for (MemberMap interfaceMap in interfaceMaps) { | |
5652 ExecutableElement overriddenElement = interfaceMap.get(memberName); | |
5653 if (overriddenElement != null) { | |
5654 if (overriddenElement is MultiplyInheritedExecutableElement) { | |
5655 MultiplyInheritedExecutableElement multiplyInheritedElement = | |
5656 overriddenElement; | |
5657 for (ExecutableElement element | |
5658 in multiplyInheritedElement.inheritedElements) { | |
5659 result.add(element); | |
5660 } | |
5661 } else { | |
5662 result.add(overriddenElement); | |
5663 } | |
5664 } | |
5665 } | |
5666 } | |
5667 return result; | |
5668 } | |
5669 | |
5670 /** | |
5671 * This method takes some inherited [FunctionType], and resolves all the param
eterized types | |
5672 * in the function type, dependent on the class in which it is being overridde
n. | |
5673 * | |
5674 * @param baseFunctionType the function type that is being overridden | |
5675 * @param memberName the name of the member, this is used to lookup the inheri
tance path of the | |
5676 * override | |
5677 * @param definingType the type that is overriding the member | |
5678 * @return the passed function type with any parameterized types substituted | |
5679 */ | |
5680 FunctionType substituteTypeArgumentsInMemberFromInheritance( | |
5681 FunctionType baseFunctionType, String memberName, | |
5682 InterfaceType definingType) { | |
5683 // if the baseFunctionType is null, or does not have any parameters, | |
5684 // return it. | |
5685 if (baseFunctionType == null || | |
5686 baseFunctionType.typeArguments.length == 0) { | |
5687 return baseFunctionType; | |
5688 } | |
5689 // First, generate the path from the defining type to the overridden member | |
5690 Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>(); | |
5691 _computeInheritancePath(inheritancePath, definingType, memberName); | |
5692 if (inheritancePath == null || inheritancePath.isEmpty) { | |
5693 // TODO(jwren) log analysis engine error | |
5694 return baseFunctionType; | |
5695 } | |
5696 FunctionType functionTypeToReturn = baseFunctionType; | |
5697 // loop backward through the list substituting as we go: | |
5698 while (!inheritancePath.isEmpty) { | |
5699 InterfaceType lastType = inheritancePath.removeLast(); | |
5700 List<DartType> parameterTypes = lastType.element.type.typeArguments; | |
5701 List<DartType> argumentTypes = lastType.typeArguments; | |
5702 functionTypeToReturn = | |
5703 functionTypeToReturn.substitute2(argumentTypes, parameterTypes); | |
5704 } | |
5705 return functionTypeToReturn; | |
5706 } | |
5707 | |
5708 /** | |
5709 * Compute and return a mapping between the set of all string names of the mem
bers inherited from | |
5710 * the passed [ClassElement] superclass hierarchy, and the associated | |
5711 * [ExecutableElement]. | |
5712 * | |
5713 * @param classElt the class element to query | |
5714 * @param visitedClasses a set of visited classes passed back into this method
when it calls | |
5715 * itself recursively | |
5716 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
5717 * [ClassElement] superclass hierarchy, and the associated [Executable
Element] | |
5718 */ | |
5719 MemberMap _computeClassChainLookupMap( | |
5720 ClassElement classElt, HashSet<ClassElement> visitedClasses) { | |
5721 MemberMap resultMap = _classLookup[classElt]; | |
5722 if (resultMap != null) { | |
5723 return resultMap; | |
5724 } else { | |
5725 resultMap = new MemberMap(); | |
5726 } | |
5727 ClassElement superclassElt = null; | |
5728 InterfaceType supertype = classElt.supertype; | |
5729 if (supertype != null) { | |
5730 superclassElt = supertype.element; | |
5731 } else { | |
5732 // classElt is Object | |
5733 _classLookup[classElt] = resultMap; | |
5734 return resultMap; | |
5735 } | |
5736 if (superclassElt != null) { | |
5737 if (!visitedClasses.contains(superclassElt)) { | |
5738 visitedClasses.add(superclassElt); | |
5739 try { | |
5740 resultMap = new MemberMap.from( | |
5741 _computeClassChainLookupMap(superclassElt, visitedClasses)); | |
5742 // | |
5743 // Substitute the super types down the hierarchy. | |
5744 // | |
5745 _substituteTypeParametersDownHierarchy(supertype, resultMap); | |
5746 // | |
5747 // Include the members from the superclass in the resultMap. | |
5748 // | |
5749 _recordMapWithClassMembers(resultMap, supertype, false); | |
5750 } finally { | |
5751 visitedClasses.remove(superclassElt); | |
5752 } | |
5753 } else { | |
5754 // This case happens only when the superclass was previously visited and | |
5755 // not in the lookup, meaning this is meant to shorten the compute for | |
5756 // recursive cases. | |
5757 _classLookup[superclassElt] = resultMap; | |
5758 return resultMap; | |
5759 } | |
5760 } | |
5761 // | |
5762 // Include the members from the mixins in the resultMap. If there are | |
5763 // multiple mixins, visit them in the order listed so that methods in later | |
5764 // mixins will overwrite identically-named methods in earlier mixins. | |
5765 // | |
5766 List<InterfaceType> mixins = classElt.mixins; | |
5767 for (InterfaceType mixin in mixins) { | |
5768 ClassElement mixinElement = mixin.element; | |
5769 if (mixinElement != null) { | |
5770 if (!visitedClasses.contains(mixinElement)) { | |
5771 visitedClasses.add(mixinElement); | |
5772 try { | |
5773 MemberMap map = new MemberMap.from( | |
5774 _computeClassChainLookupMap(mixinElement, visitedClasses)); | |
5775 // | |
5776 // Substitute the super types down the hierarchy. | |
5777 // | |
5778 _substituteTypeParametersDownHierarchy(mixin, map); | |
5779 // | |
5780 // Include the members from the superclass in the resultMap. | |
5781 // | |
5782 _recordMapWithClassMembers(map, mixin, false); | |
5783 // | |
5784 // Add the members from map into result map. | |
5785 // | |
5786 for (int j = 0; j < map.size; j++) { | |
5787 String key = map.getKey(j); | |
5788 ExecutableElement value = map.getValue(j); | |
5789 if (key != null) { | |
5790 ClassElement definingClass = value | |
5791 .getAncestor((Element element) => element is ClassElement); | |
5792 if (!definingClass.type.isObject) { | |
5793 ExecutableElement existingValue = resultMap.get(key); | |
5794 if (existingValue == null || | |
5795 (existingValue != null && !_isAbstract(value))) { | |
5796 resultMap.put(key, value); | |
5797 } | |
5798 } | |
5799 } | |
5800 } | |
5801 } finally { | |
5802 visitedClasses.remove(mixinElement); | |
5803 } | |
5804 } else { | |
5805 // This case happens only when the superclass was previously visited | |
5806 // and not in the lookup, meaning this is meant to shorten the compute | |
5807 // for recursive cases. | |
5808 _classLookup[mixinElement] = resultMap; | |
5809 return resultMap; | |
5810 } | |
5811 } | |
5812 } | |
5813 _classLookup[classElt] = resultMap; | |
5814 return resultMap; | |
5815 } | |
5816 | |
5817 /** | |
5818 * Compute and return the inheritance path given the context of a type and a m
ember that is | |
5819 * overridden in the inheritance path (for which the type is in the path). | |
5820 * | |
5821 * @param chain the inheritance path that is built up as this method calls its
elf recursively, | |
5822 * when this method is called an empty [LinkedList] should be provide
d | |
5823 * @param currentType the current type in the inheritance path | |
5824 * @param memberName the name of the member that is being looked up the inheri
tance path | |
5825 */ | |
5826 void _computeInheritancePath(Queue<InterfaceType> chain, | |
5827 InterfaceType currentType, String memberName) { | |
5828 // TODO (jwren) create a public version of this method which doesn't require | |
5829 // the initial chain to be provided, then provided tests for this | |
5830 // functionality in InheritanceManagerTest | |
5831 chain.add(currentType); | |
5832 ClassElement classElt = currentType.element; | |
5833 InterfaceType supertype = classElt.supertype; | |
5834 // Base case- reached Object | |
5835 if (supertype == null) { | |
5836 // Looked up the chain all the way to Object, return null. | |
5837 // This should never happen. | |
5838 return; | |
5839 } | |
5840 // If we are done, return the chain | |
5841 // We are not done if this is the first recursive call on this method. | |
5842 if (chain.length != 1) { | |
5843 // We are done however if the member is in this classElt | |
5844 if (_lookupMemberInClass(classElt, memberName) != null) { | |
5845 return; | |
5846 } | |
5847 } | |
5848 // Mixins- note that mixins call lookupMemberInClass, not lookupMember | |
5849 List<InterfaceType> mixins = classElt.mixins; | |
5850 for (int i = mixins.length - 1; i >= 0; i--) { | |
5851 ClassElement mixinElement = mixins[i].element; | |
5852 if (mixinElement != null) { | |
5853 ExecutableElement elt = _lookupMemberInClass(mixinElement, memberName); | |
5854 if (elt != null) { | |
5855 // this is equivalent (but faster than) calling this method | |
5856 // recursively | |
5857 // (return computeInheritancePath(chain, mixins[i], memberName);) | |
5858 chain.add(mixins[i]); | |
5859 return; | |
5860 } | |
5861 } | |
5862 } | |
5863 // Superclass | |
5864 ClassElement superclassElt = supertype.element; | |
5865 if (lookupMember(superclassElt, memberName) != null) { | |
5866 _computeInheritancePath(chain, supertype, memberName); | |
5867 return; | |
5868 } | |
5869 // Interfaces | |
5870 List<InterfaceType> interfaces = classElt.interfaces; | |
5871 for (InterfaceType interfaceType in interfaces) { | |
5872 ClassElement interfaceElement = interfaceType.element; | |
5873 if (interfaceElement != null && | |
5874 lookupMember(interfaceElement, memberName) != null) { | |
5875 _computeInheritancePath(chain, interfaceType, memberName); | |
5876 return; | |
5877 } | |
5878 } | |
5879 } | |
5880 | |
5881 /** | |
5882 * Compute and return a mapping between the set of all string names of the mem
bers inherited from | |
5883 * the passed [ClassElement] interface hierarchy, and the associated | |
5884 * [ExecutableElement]. | |
5885 * | |
5886 * @param classElt the class element to query | |
5887 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls | |
5888 * itself recursively | |
5889 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
5890 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement] | |
5891 */ | |
5892 MemberMap _computeInterfaceLookupMap( | |
5893 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { | |
5894 MemberMap resultMap = _interfaceLookup[classElt]; | |
5895 if (resultMap != null) { | |
5896 return resultMap; | |
5897 } | |
5898 List<MemberMap> lookupMaps = | |
5899 _gatherInterfaceLookupMaps(classElt, visitedInterfaces); | |
5900 if (lookupMaps == null) { | |
5901 resultMap = new MemberMap(); | |
5902 } else { | |
5903 HashMap<String, List<ExecutableElement>> unionMap = | |
5904 _unionInterfaceLookupMaps(lookupMaps); | |
5905 resultMap = _resolveInheritanceLookup(classElt, unionMap); | |
5906 } | |
5907 _interfaceLookup[classElt] = resultMap; | |
5908 return resultMap; | |
5909 } | |
5910 | |
5911 /** | |
5912 * Collect a list of interface lookup maps whose elements correspond to all of
the classes | |
5913 * directly above [classElt] in the class hierarchy (the direct superclass if
any, all | |
5914 * mixins, and all direct superinterfaces). Each item in the list is the inter
face lookup map | |
5915 * returned by [computeInterfaceLookupMap] for the corresponding super, except
with type | |
5916 * parameters appropriately substituted. | |
5917 * | |
5918 * @param classElt the class element to query | |
5919 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls | |
5920 * itself recursively | |
5921 * @return `null` if there was a problem (such as a loop in the class hierarch
y) or if there | |
5922 * are no classes above this one in the class hierarchy. Otherwise, a
list of interface | |
5923 * lookup maps. | |
5924 */ | |
5925 List<MemberMap> _gatherInterfaceLookupMaps( | |
5926 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { | |
5927 InterfaceType supertype = classElt.supertype; | |
5928 ClassElement superclassElement = | |
5929 supertype != null ? supertype.element : null; | |
5930 List<InterfaceType> mixins = classElt.mixins; | |
5931 List<InterfaceType> interfaces = classElt.interfaces; | |
5932 // Recursively collect the list of mappings from all of the interface types | |
5933 List<MemberMap> lookupMaps = new List<MemberMap>(); | |
5934 // | |
5935 // Superclass element | |
5936 // | |
5937 if (superclassElement != null) { | |
5938 if (!visitedInterfaces.contains(superclassElement)) { | |
5939 try { | |
5940 visitedInterfaces.add(superclassElement); | |
5941 // | |
5942 // Recursively compute the map for the super type. | |
5943 // | |
5944 MemberMap map = | |
5945 _computeInterfaceLookupMap(superclassElement, visitedInterfaces); | |
5946 map = new MemberMap.from(map); | |
5947 // | |
5948 // Substitute the super type down the hierarchy. | |
5949 // | |
5950 _substituteTypeParametersDownHierarchy(supertype, map); | |
5951 // | |
5952 // Add any members from the super type into the map as well. | |
5953 // | |
5954 _recordMapWithClassMembers(map, supertype, true); | |
5955 lookupMaps.add(map); | |
5956 } finally { | |
5957 visitedInterfaces.remove(superclassElement); | |
5958 } | |
5959 } else { | |
5960 return null; | |
5961 } | |
5962 } | |
5963 // | |
5964 // Mixin elements | |
5965 // | |
5966 for (int i = mixins.length - 1; i >= 0; i--) { | |
5967 InterfaceType mixinType = mixins[i]; | |
5968 ClassElement mixinElement = mixinType.element; | |
5969 if (mixinElement != null) { | |
5970 if (!visitedInterfaces.contains(mixinElement)) { | |
5971 try { | |
5972 visitedInterfaces.add(mixinElement); | |
5973 // | |
5974 // Recursively compute the map for the mixin. | |
5975 // | |
5976 MemberMap map = | |
5977 _computeInterfaceLookupMap(mixinElement, visitedInterfaces); | |
5978 map = new MemberMap.from(map); | |
5979 // | |
5980 // Substitute the mixin type down the hierarchy. | |
5981 // | |
5982 _substituteTypeParametersDownHierarchy(mixinType, map); | |
5983 // | |
5984 // Add any members from the mixin type into the map as well. | |
5985 // | |
5986 _recordMapWithClassMembers(map, mixinType, true); | |
5987 lookupMaps.add(map); | |
5988 } finally { | |
5989 visitedInterfaces.remove(mixinElement); | |
5990 } | |
5991 } else { | |
5992 return null; | |
5993 } | |
5994 } | |
5995 } | |
5996 // | |
5997 // Interface elements | |
5998 // | |
5999 for (InterfaceType interfaceType in interfaces) { | |
6000 ClassElement interfaceElement = interfaceType.element; | |
6001 if (interfaceElement != null) { | |
6002 if (!visitedInterfaces.contains(interfaceElement)) { | |
6003 try { | |
6004 visitedInterfaces.add(interfaceElement); | |
6005 // | |
6006 // Recursively compute the map for the interfaces. | |
6007 // | |
6008 MemberMap map = | |
6009 _computeInterfaceLookupMap(interfaceElement, visitedInterfaces); | |
6010 map = new MemberMap.from(map); | |
6011 // | |
6012 // Substitute the supertypes down the hierarchy | |
6013 // | |
6014 _substituteTypeParametersDownHierarchy(interfaceType, map); | |
6015 // | |
6016 // And add any members from the interface into the map as well. | |
6017 // | |
6018 _recordMapWithClassMembers(map, interfaceType, true); | |
6019 lookupMaps.add(map); | |
6020 } finally { | |
6021 visitedInterfaces.remove(interfaceElement); | |
6022 } | |
6023 } else { | |
6024 return null; | |
6025 } | |
6026 } | |
6027 } | |
6028 if (lookupMaps.length == 0) { | |
6029 return null; | |
6030 } | |
6031 return lookupMaps; | |
6032 } | |
6033 | |
6034 /** | |
6035 * Given some [ClassElement], this method finds and returns the [ExecutableEle
ment] of | |
6036 * the passed name in the class element. Static members, members in super type
s and members not | |
6037 * accessible from the current library are not considered. | |
6038 * | |
6039 * @param classElt the class element to query | |
6040 * @param memberName the name of the member to lookup in the class | |
6041 * @return the found [ExecutableElement], or `null` if no such member was foun
d | |
6042 */ | |
6043 ExecutableElement _lookupMemberInClass( | |
6044 ClassElement classElt, String memberName) { | |
6045 List<MethodElement> methods = classElt.methods; | |
6046 for (MethodElement method in methods) { | |
6047 if (memberName == method.name && | |
6048 method.isAccessibleIn(_library) && | |
6049 !method.isStatic) { | |
6050 return method; | |
6051 } | |
6052 } | |
6053 List<PropertyAccessorElement> accessors = classElt.accessors; | |
6054 for (PropertyAccessorElement accessor in accessors) { | |
6055 if (memberName == accessor.name && | |
6056 accessor.isAccessibleIn(_library) && | |
6057 !accessor.isStatic) { | |
6058 return accessor; | |
6059 } | |
6060 } | |
6061 return null; | |
6062 } | |
6063 | |
6064 /** | |
6065 * Record the passed map with the set of all members (methods, getters and set
ters) in the type | |
6066 * into the passed map. | |
6067 * | |
6068 * @param map some non-`null` map to put the methods and accessors from the pa
ssed | |
6069 * [ClassElement] into | |
6070 * @param type the type that will be recorded into the passed map | |
6071 * @param doIncludeAbstract `true` if abstract members will be put into the ma
p | |
6072 */ | |
6073 void _recordMapWithClassMembers( | |
6074 MemberMap map, InterfaceType type, bool doIncludeAbstract) { | |
6075 List<MethodElement> methods = type.methods; | |
6076 for (MethodElement method in methods) { | |
6077 if (method.isAccessibleIn(_library) && | |
6078 !method.isStatic && | |
6079 (doIncludeAbstract || !method.isAbstract)) { | |
6080 map.put(method.name, method); | |
6081 } | |
6082 } | |
6083 List<PropertyAccessorElement> accessors = type.accessors; | |
6084 for (PropertyAccessorElement accessor in accessors) { | |
6085 if (accessor.isAccessibleIn(_library) && | |
6086 !accessor.isStatic && | |
6087 (doIncludeAbstract || !accessor.isAbstract)) { | |
6088 map.put(accessor.name, accessor); | |
6089 } | |
6090 } | |
6091 } | |
6092 | |
6093 /** | |
6094 * This method is used to report errors on when they are found computing inher
itance information. | |
6095 * See [ErrorVerifier.checkForInconsistentMethodInheritance] to see where thes
e generated | |
6096 * error codes are reported back into the analysis engine. | |
6097 * | |
6098 * @param classElt the location of the source for which the exception occurred | |
6099 * @param offset the offset of the location of the error | |
6100 * @param length the length of the location of the error | |
6101 * @param errorCode the error code to be associated with this error | |
6102 * @param arguments the arguments used to build the error message | |
6103 */ | |
6104 void _reportError(ClassElement classElt, int offset, int length, | |
6105 ErrorCode errorCode, List<Object> arguments) { | |
6106 HashSet<AnalysisError> errorSet = _errorsInClassElement[classElt]; | |
6107 if (errorSet == null) { | |
6108 errorSet = new HashSet<AnalysisError>(); | |
6109 _errorsInClassElement[classElt] = errorSet; | |
6110 } | |
6111 errorSet.add(new AnalysisError( | |
6112 classElt.source, offset, length, errorCode, arguments)); | |
6113 } | |
6114 | |
6115 /** | |
6116 * Given the set of methods defined by classes above [classElt] in the class h
ierarchy, | |
6117 * apply the appropriate inheritance rules to determine those methods inherite
d by or overridden | |
6118 * by [classElt]. Also report static warnings | |
6119 * [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE] and | |
6120 * [StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD] if ap
propriate. | |
6121 * | |
6122 * @param classElt the class element to query. | |
6123 * @param unionMap a mapping from method name to the set of unique (in terms o
f signature) methods | |
6124 * defined in superclasses of [classElt]. | |
6125 * @return the inheritance lookup map for [classElt]. | |
6126 */ | |
6127 MemberMap _resolveInheritanceLookup(ClassElement classElt, | |
6128 HashMap<String, List<ExecutableElement>> unionMap) { | |
6129 MemberMap resultMap = new MemberMap(); | |
6130 unionMap.forEach((String key, List<ExecutableElement> list) { | |
6131 int numOfEltsWithMatchingNames = list.length; | |
6132 if (numOfEltsWithMatchingNames == 1) { | |
6133 // | |
6134 // Example: class A inherits only 1 method named 'm'. | |
6135 // Since it is the only such method, it is inherited. | |
6136 // Another example: class A inherits 2 methods named 'm' from 2 | |
6137 // different interfaces, but they both have the same signature, so it is | |
6138 // the method inherited. | |
6139 // | |
6140 resultMap.put(key, list[0]); | |
6141 } else { | |
6142 // | |
6143 // Then numOfEltsWithMatchingNames > 1, check for the warning cases. | |
6144 // | |
6145 bool allMethods = true; | |
6146 bool allSetters = true; | |
6147 bool allGetters = true; | |
6148 for (ExecutableElement executableElement in list) { | |
6149 if (executableElement is PropertyAccessorElement) { | |
6150 allMethods = false; | |
6151 if (executableElement.isSetter) { | |
6152 allGetters = false; | |
6153 } else { | |
6154 allSetters = false; | |
6155 } | |
6156 } else { | |
6157 allGetters = false; | |
6158 allSetters = false; | |
6159 } | |
6160 } | |
6161 // | |
6162 // If there isn't a mixture of methods with getters, then continue, | |
6163 // otherwise create a warning. | |
6164 // | |
6165 if (allMethods || allGetters || allSetters) { | |
6166 // | |
6167 // Compute the element whose type is the subtype of all of the other | |
6168 // types. | |
6169 // | |
6170 List<ExecutableElement> elements = new List.from(list); | |
6171 List<FunctionType> executableElementTypes = | |
6172 new List<FunctionType>(numOfEltsWithMatchingNames); | |
6173 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { | |
6174 executableElementTypes[i] = elements[i].type; | |
6175 } | |
6176 List<int> subtypesOfAllOtherTypesIndexes = new List<int>(); | |
6177 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { | |
6178 FunctionType subtype = executableElementTypes[i]; | |
6179 if (subtype == null) { | |
6180 continue; | |
6181 } | |
6182 bool subtypeOfAllTypes = true; | |
6183 for (int j = 0; | |
6184 j < numOfEltsWithMatchingNames && subtypeOfAllTypes; | |
6185 j++) { | |
6186 if (i != j) { | |
6187 if (!subtype.isSubtypeOf(executableElementTypes[j])) { | |
6188 subtypeOfAllTypes = false; | |
6189 break; | |
6190 } | |
6191 } | |
6192 } | |
6193 if (subtypeOfAllTypes) { | |
6194 subtypesOfAllOtherTypesIndexes.add(i); | |
6195 } | |
6196 } | |
6197 // | |
6198 // The following is split into three cases determined by the number of | |
6199 // elements in subtypesOfAllOtherTypes | |
6200 // | |
6201 if (subtypesOfAllOtherTypesIndexes.length == 1) { | |
6202 // | |
6203 // Example: class A inherited only 2 method named 'm'. | |
6204 // One has the function type '() -> dynamic' and one has the | |
6205 // function type '([int]) -> dynamic'. Since the second method is a | |
6206 // subtype of all the others, it is the inherited method. | |
6207 // Tests: InheritanceManagerTest. | |
6208 // test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_* | |
6209 // | |
6210 resultMap.put(key, elements[subtypesOfAllOtherTypesIndexes[0]]); | |
6211 } else { | |
6212 if (subtypesOfAllOtherTypesIndexes.isEmpty) { | |
6213 // | |
6214 // Determine if the current class has a method or accessor with | |
6215 // the member name, if it does then then this class does not | |
6216 // "inherit" from any of the supertypes. See issue 16134. | |
6217 // | |
6218 bool classHasMember = false; | |
6219 if (allMethods) { | |
6220 classHasMember = classElt.getMethod(key) != null; | |
6221 } else { | |
6222 List<PropertyAccessorElement> accessors = classElt.accessors; | |
6223 for (int i = 0; i < accessors.length; i++) { | |
6224 if (accessors[i].name == key) { | |
6225 classHasMember = true; | |
6226 } | |
6227 } | |
6228 } | |
6229 // | |
6230 // Example: class A inherited only 2 method named 'm'. | |
6231 // One has the function type '() -> int' and one has the function | |
6232 // type '() -> String'. Since neither is a subtype of the other, | |
6233 // we create a warning, and have this class inherit nothing. | |
6234 // | |
6235 if (!classHasMember) { | |
6236 String firstTwoFuntionTypesStr = | |
6237 "${executableElementTypes[0]}, ${executableElementTypes[1]}"
; | |
6238 _reportError(classElt, classElt.nameOffset, | |
6239 classElt.displayName.length, | |
6240 StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, [ | |
6241 key, | |
6242 firstTwoFuntionTypesStr | |
6243 ]); | |
6244 } | |
6245 } else { | |
6246 // | |
6247 // Example: class A inherits 2 methods named 'm'. | |
6248 // One has the function type '(int) -> dynamic' and one has the | |
6249 // function type '(num) -> dynamic'. Since they are both a subtype | |
6250 // of the other, a synthetic function '(dynamic) -> dynamic' is | |
6251 // inherited. | |
6252 // Tests: test_getMapOfMembersInheritedFromInterfaces_ | |
6253 // union_multipleSubtypes_* | |
6254 // | |
6255 List<ExecutableElement> elementArrayToMerge = | |
6256 new List<ExecutableElement>( | |
6257 subtypesOfAllOtherTypesIndexes.length); | |
6258 for (int i = 0; i < elementArrayToMerge.length; i++) { | |
6259 elementArrayToMerge[i] = | |
6260 elements[subtypesOfAllOtherTypesIndexes[i]]; | |
6261 } | |
6262 ExecutableElement mergedExecutableElement = | |
6263 _computeMergedExecutableElement(elementArrayToMerge); | |
6264 resultMap.put(key, mergedExecutableElement); | |
6265 } | |
6266 } | |
6267 } else { | |
6268 _reportError(classElt, classElt.nameOffset, | |
6269 classElt.displayName.length, | |
6270 StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHO
D, | |
6271 [key]); | |
6272 } | |
6273 } | |
6274 }); | |
6275 return resultMap; | |
6276 } | |
6277 | |
6278 /** | |
6279 * Loop through all of the members in some [MemberMap], performing type parame
ter | |
6280 * substitutions using a passed supertype. | |
6281 * | |
6282 * @param superType the supertype to substitute into the members of the [Membe
rMap] | |
6283 * @param map the MemberMap to perform the substitutions on | |
6284 */ | |
6285 void _substituteTypeParametersDownHierarchy( | |
6286 InterfaceType superType, MemberMap map) { | |
6287 for (int i = 0; i < map.size; i++) { | |
6288 ExecutableElement executableElement = map.getValue(i); | |
6289 if (executableElement is MethodMember) { | |
6290 executableElement = | |
6291 MethodMember.from(executableElement as MethodMember, superType); | |
6292 map.setValue(i, executableElement); | |
6293 } else if (executableElement is PropertyAccessorMember) { | |
6294 executableElement = PropertyAccessorMember.from( | |
6295 executableElement as PropertyAccessorMember, superType); | |
6296 map.setValue(i, executableElement); | |
6297 } | |
6298 } | |
6299 } | |
6300 | |
6301 /** | |
6302 * Union all of the [lookupMaps] together into a single map, grouping the Exec
utableElements | |
6303 * into a list where none of the elements are equal where equality is determin
ed by having equal | |
6304 * function types. (We also take note too of the kind of the element: ()->int
and () -> int may | |
6305 * not be equal if one is a getter and the other is a method.) | |
6306 * | |
6307 * @param lookupMaps the maps to be unioned together. | |
6308 * @return the resulting union map. | |
6309 */ | |
6310 HashMap<String, List<ExecutableElement>> _unionInterfaceLookupMaps( | |
6311 List<MemberMap> lookupMaps) { | |
6312 HashMap<String, List<ExecutableElement>> unionMap = | |
6313 new HashMap<String, List<ExecutableElement>>(); | |
6314 for (MemberMap lookupMap in lookupMaps) { | |
6315 int lookupMapSize = lookupMap.size; | |
6316 for (int i = 0; i < lookupMapSize; i++) { | |
6317 // Get the string key, if null, break. | |
6318 String key = lookupMap.getKey(i); | |
6319 if (key == null) { | |
6320 break; | |
6321 } | |
6322 // Get the list value out of the unionMap | |
6323 List<ExecutableElement> list = unionMap[key]; | |
6324 // If we haven't created such a map for this key yet, do create it and | |
6325 // put the list entry into the unionMap. | |
6326 if (list == null) { | |
6327 list = new List<ExecutableElement>(); | |
6328 unionMap[key] = list; | |
6329 } | |
6330 // Fetch the entry out of this lookupMap | |
6331 ExecutableElement newExecutableElementEntry = lookupMap.getValue(i); | |
6332 if (list.isEmpty) { | |
6333 // If the list is empty, just the new value | |
6334 list.add(newExecutableElementEntry); | |
6335 } else { | |
6336 // Otherwise, only add the newExecutableElementEntry if it isn't | |
6337 // already in the list, this covers situation where a class inherits | |
6338 // two methods (or two getters) that are identical. | |
6339 bool alreadyInList = false; | |
6340 bool isMethod1 = newExecutableElementEntry is MethodElement; | |
6341 for (ExecutableElement executableElementInList in list) { | |
6342 bool isMethod2 = executableElementInList is MethodElement; | |
6343 if (isMethod1 == isMethod2 && | |
6344 executableElementInList.type == | |
6345 newExecutableElementEntry.type) { | |
6346 alreadyInList = true; | |
6347 break; | |
6348 } | |
6349 } | |
6350 if (!alreadyInList) { | |
6351 list.add(newExecutableElementEntry); | |
6352 } | |
6353 } | |
6354 } | |
6355 } | |
6356 return unionMap; | |
6357 } | |
6358 | |
6359 /** | |
6360 * Given some array of [ExecutableElement]s, this method creates a synthetic e
lement as | |
6361 * described in 8.1.1: | |
6362 * | |
6363 * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional pa
rameters of a | |
6364 * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote t
he number of | |
6365 * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denot
e the set of all | |
6366 * named parameters of the <i>m<sub>1</sub>, …, m<sub>k</sub></i>. Then
let | |
6367 * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i> | |
6368 * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <=
i <= k.</i> | |
6369 * Then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameter
s of type | |
6370 * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, name
d parameters | |
6371 * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>. | |
6372 * | |
6373 */ | |
6374 static ExecutableElement _computeMergedExecutableElement( | |
6375 List<ExecutableElement> elementArrayToMerge) { | |
6376 int h = _getNumOfPositionalParameters(elementArrayToMerge[0]); | |
6377 int r = _getNumOfRequiredParameters(elementArrayToMerge[0]); | |
6378 Set<String> namedParametersList = new HashSet<String>(); | |
6379 for (int i = 1; i < elementArrayToMerge.length; i++) { | |
6380 ExecutableElement element = elementArrayToMerge[i]; | |
6381 int numOfPositionalParams = _getNumOfPositionalParameters(element); | |
6382 if (h < numOfPositionalParams) { | |
6383 h = numOfPositionalParams; | |
6384 } | |
6385 int numOfRequiredParams = _getNumOfRequiredParameters(element); | |
6386 if (r > numOfRequiredParams) { | |
6387 r = numOfRequiredParams; | |
6388 } | |
6389 namedParametersList.addAll(_getNamedParameterNames(element)); | |
6390 } | |
6391 return _createSyntheticExecutableElement(elementArrayToMerge, | |
6392 elementArrayToMerge[0].displayName, r, h - r, | |
6393 new List.from(namedParametersList)); | |
6394 } | |
6395 | |
6396 /** | |
6397 * Used by [computeMergedExecutableElement] to actually create the | |
6398 * synthetic element. | |
6399 * | |
6400 * @param elementArrayToMerge the array used to create the synthetic element | |
6401 * @param name the name of the method, getter or setter | |
6402 * @param numOfRequiredParameters the number of required parameters | |
6403 * @param numOfPositionalParameters the number of positional parameters | |
6404 * @param namedParameters the list of [String]s that are the named parameters | |
6405 * @return the created synthetic element | |
6406 */ | |
6407 static ExecutableElement _createSyntheticExecutableElement( | |
6408 List<ExecutableElement> elementArrayToMerge, String name, | |
6409 int numOfRequiredParameters, int numOfPositionalParameters, | |
6410 List<String> namedParameters) { | |
6411 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance; | |
6412 SimpleIdentifier nameIdentifier = new SimpleIdentifier( | |
6413 new sc.StringToken(sc.TokenType.IDENTIFIER, name, 0)); | |
6414 ExecutableElementImpl executable; | |
6415 if (elementArrayToMerge[0] is MethodElement) { | |
6416 MultiplyInheritedMethodElementImpl unionedMethod = | |
6417 new MultiplyInheritedMethodElementImpl(nameIdentifier); | |
6418 unionedMethod.inheritedElements = elementArrayToMerge; | |
6419 executable = unionedMethod; | |
6420 } else { | |
6421 MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor = | |
6422 new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier); | |
6423 unionedPropertyAccessor.getter = | |
6424 (elementArrayToMerge[0] as PropertyAccessorElement).isGetter; | |
6425 unionedPropertyAccessor.setter = | |
6426 (elementArrayToMerge[0] as PropertyAccessorElement).isSetter; | |
6427 unionedPropertyAccessor.inheritedElements = elementArrayToMerge; | |
6428 executable = unionedPropertyAccessor; | |
6429 } | |
6430 int numOfParameters = numOfRequiredParameters + | |
6431 numOfPositionalParameters + | |
6432 namedParameters.length; | |
6433 List<ParameterElement> parameters = | |
6434 new List<ParameterElement>(numOfParameters); | |
6435 int i = 0; | |
6436 for (int j = 0; j < numOfRequiredParameters; j++, i++) { | |
6437 ParameterElementImpl parameter = new ParameterElementImpl("", 0); | |
6438 parameter.type = dynamicType; | |
6439 parameter.parameterKind = ParameterKind.REQUIRED; | |
6440 parameters[i] = parameter; | |
6441 } | |
6442 for (int k = 0; k < numOfPositionalParameters; k++, i++) { | |
6443 ParameterElementImpl parameter = new ParameterElementImpl("", 0); | |
6444 parameter.type = dynamicType; | |
6445 parameter.parameterKind = ParameterKind.POSITIONAL; | |
6446 parameters[i] = parameter; | |
6447 } | |
6448 for (int m = 0; m < namedParameters.length; m++, i++) { | |
6449 ParameterElementImpl parameter = | |
6450 new ParameterElementImpl(namedParameters[m], 0); | |
6451 parameter.type = dynamicType; | |
6452 parameter.parameterKind = ParameterKind.NAMED; | |
6453 parameters[i] = parameter; | |
6454 } | |
6455 executable.returnType = dynamicType; | |
6456 executable.parameters = parameters; | |
6457 FunctionTypeImpl methodType = new FunctionTypeImpl(executable); | |
6458 executable.type = methodType; | |
6459 return executable; | |
6460 } | |
6461 | |
6462 /** | |
6463 * Given some [ExecutableElement], return the list of named parameters. | |
6464 */ | |
6465 static List<String> _getNamedParameterNames( | |
6466 ExecutableElement executableElement) { | |
6467 List<String> namedParameterNames = new List<String>(); | |
6468 List<ParameterElement> parameters = executableElement.parameters; | |
6469 for (int i = 0; i < parameters.length; i++) { | |
6470 ParameterElement parameterElement = parameters[i]; | |
6471 if (parameterElement.parameterKind == ParameterKind.NAMED) { | |
6472 namedParameterNames.add(parameterElement.name); | |
6473 } | |
6474 } | |
6475 return namedParameterNames; | |
6476 } | |
6477 | |
6478 /** | |
6479 * Given some [ExecutableElement] return the number of parameters of the speci
fied kind. | |
6480 */ | |
6481 static int _getNumOfParameters( | |
6482 ExecutableElement executableElement, ParameterKind parameterKind) { | |
6483 int parameterCount = 0; | |
6484 List<ParameterElement> parameters = executableElement.parameters; | |
6485 for (int i = 0; i < parameters.length; i++) { | |
6486 ParameterElement parameterElement = parameters[i]; | |
6487 if (parameterElement.parameterKind == parameterKind) { | |
6488 parameterCount++; | |
6489 } | |
6490 } | |
6491 return parameterCount; | |
6492 } | |
6493 | |
6494 /** | |
6495 * Given some [ExecutableElement] return the number of positional parameters. | |
6496 * | |
6497 * Note: by positional we mean [ParameterKind.REQUIRED] or [ParameterKind.POSI
TIONAL]. | |
6498 */ | |
6499 static int _getNumOfPositionalParameters( | |
6500 ExecutableElement executableElement) => | |
6501 _getNumOfParameters(executableElement, ParameterKind.REQUIRED) + | |
6502 _getNumOfParameters(executableElement, ParameterKind.POSITIONAL); | |
6503 | |
6504 /** | |
6505 * Given some [ExecutableElement] return the number of required parameters. | |
6506 */ | |
6507 static int _getNumOfRequiredParameters(ExecutableElement executableElement) => | |
6508 _getNumOfParameters(executableElement, ParameterKind.REQUIRED); | |
6509 | |
6510 /** | |
6511 * Given some [ExecutableElement] returns `true` if it is an abstract member o
f a | |
6512 * class. | |
6513 * | |
6514 * @param executableElement some [ExecutableElement] to evaluate | |
6515 * @return `true` if the given element is an abstract member of a class | |
6516 */ | |
6517 static bool _isAbstract(ExecutableElement executableElement) { | |
6518 if (executableElement is MethodElement) { | |
6519 return executableElement.isAbstract; | |
6520 } else if (executableElement is PropertyAccessorElement) { | |
6521 return executableElement.isAbstract; | |
6522 } | |
6523 return false; | |
6524 } | |
6525 } | |
6526 | |
6527 /** | |
6528 * This enum holds one of four states of a field initialization state through a
constructor | |
6529 * signature, not initialized, initialized in the field declaration, initialized
in the field | |
6530 * formal, and finally, initialized in the initializers list. | |
6531 */ | |
6532 class INIT_STATE extends Enum<INIT_STATE> { | |
6533 static const INIT_STATE NOT_INIT = const INIT_STATE('NOT_INIT', 0); | |
6534 | |
6535 static const INIT_STATE INIT_IN_DECLARATION = | |
6536 const INIT_STATE('INIT_IN_DECLARATION', 1); | |
6537 | |
6538 static const INIT_STATE INIT_IN_FIELD_FORMAL = | |
6539 const INIT_STATE('INIT_IN_FIELD_FORMAL', 2); | |
6540 | |
6541 static const INIT_STATE INIT_IN_INITIALIZERS = | |
6542 const INIT_STATE('INIT_IN_INITIALIZERS', 3); | |
6543 | |
6544 static const List<INIT_STATE> values = const [ | |
6545 NOT_INIT, | |
6546 INIT_IN_DECLARATION, | |
6547 INIT_IN_FIELD_FORMAL, | |
6548 INIT_IN_INITIALIZERS | |
6549 ]; | |
6550 | |
6551 const INIT_STATE(String name, int ordinal) : super(name, ordinal); | |
6552 } | |
6553 | |
6554 /** | |
6555 * Instances of the class `LabelScope` represent a scope in which a single label
is defined. | |
6556 */ | |
6557 class LabelScope { | |
6558 /** | |
6559 * The label scope enclosing this label scope. | |
6560 */ | |
6561 final LabelScope _outerScope; | |
6562 | |
6563 /** | |
6564 * The label defined in this scope. | |
6565 */ | |
6566 final String _label; | |
6567 | |
6568 /** | |
6569 * The element to which the label resolves. | |
6570 */ | |
6571 final LabelElement element; | |
6572 | |
6573 /** | |
6574 * The AST node to which the label resolves. | |
6575 */ | |
6576 final AstNode node; | |
6577 | |
6578 /** | |
6579 * Initialize a newly created scope to represent the label [_label]. | |
6580 * [_outerScope] is the scope enclosing the new label scope. [node] is the | |
6581 * AST node the label resolves to. [element] is the element the label | |
6582 * resolves to. | |
6583 */ | |
6584 LabelScope(this._outerScope, this._label, this.node, this.element); | |
6585 | |
6586 /** | |
6587 * Return the LabelScope which defines [targetLabel], or `null` if it is not | |
6588 * defined in this scope. | |
6589 */ | |
6590 LabelScope lookup(String targetLabel) { | |
6591 if (_label == targetLabel) { | |
6592 return this; | |
6593 } else if (_outerScope != null) { | |
6594 return _outerScope.lookup(targetLabel); | |
6595 } else { | |
6596 return null; | |
6597 } | |
6598 } | |
6599 } | |
6600 | |
6601 /** | |
6602 * Instances of the class `Library` represent the data about a single library du
ring the | |
6603 * resolution of some (possibly different) library. They are not intended to be
used except during | |
6604 * the resolution process. | |
6605 */ | |
6606 class Library { | |
6607 /** | |
6608 * An empty list that can be used to initialize lists of libraries. | |
6609 */ | |
6610 static const List<Library> _EMPTY_ARRAY = const <Library>[]; | |
6611 | |
6612 /** | |
6613 * The prefix of a URI using the dart-ext scheme to reference a native code li
brary. | |
6614 */ | |
6615 static String _DART_EXT_SCHEME = "dart-ext:"; | |
6616 | |
6617 /** | |
6618 * The analysis context in which this library is being analyzed. | |
6619 */ | |
6620 final InternalAnalysisContext _analysisContext; | |
6621 | |
6622 /** | |
6623 * The inheritance manager which is used for this member lookups in this libra
ry. | |
6624 */ | |
6625 InheritanceManager _inheritanceManager; | |
6626 | |
6627 /** | |
6628 * The listener to which analysis errors will be reported. | |
6629 */ | |
6630 final AnalysisErrorListener errorListener; | |
6631 | |
6632 /** | |
6633 * The source specifying the defining compilation unit of this library. | |
6634 */ | |
6635 final Source librarySource; | |
6636 | |
6637 /** | |
6638 * The library element representing this library. | |
6639 */ | |
6640 LibraryElementImpl _libraryElement; | |
6641 | |
6642 /** | |
6643 * A list containing all of the libraries that are imported into this library. | |
6644 */ | |
6645 List<Library> _importedLibraries = _EMPTY_ARRAY; | |
6646 | |
6647 /** | |
6648 * A table mapping URI-based directive to the actual URI value. | |
6649 */ | |
6650 HashMap<UriBasedDirective, String> _directiveUris = | |
6651 new HashMap<UriBasedDirective, String>(); | |
6652 | |
6653 /** | |
6654 * A flag indicating whether this library explicitly imports core. | |
6655 */ | |
6656 bool explicitlyImportsCore = false; | |
6657 | |
6658 /** | |
6659 * A list containing all of the libraries that are exported from this library. | |
6660 */ | |
6661 List<Library> _exportedLibraries = _EMPTY_ARRAY; | |
6662 | |
6663 /** | |
6664 * A table mapping the sources for the compilation units in this library to th
eir corresponding | |
6665 * AST structures. | |
6666 */ | |
6667 HashMap<Source, CompilationUnit> _astMap = | |
6668 new HashMap<Source, CompilationUnit>(); | |
6669 | |
6670 /** | |
6671 * The library scope used when resolving elements within this library's compil
ation units. | |
6672 */ | |
6673 LibraryScope _libraryScope; | |
6674 | |
6675 /** | |
6676 * Initialize a newly created data holder that can maintain the data associate
d with a library. | |
6677 * | |
6678 * @param analysisContext the analysis context in which this library is being
analyzed | |
6679 * @param errorListener the listener to which analysis errors will be reported | |
6680 * @param librarySource the source specifying the defining compilation unit of
this library | |
6681 */ | |
6682 Library(this._analysisContext, this.errorListener, this.librarySource) { | |
6683 this._libraryElement = | |
6684 _analysisContext.getLibraryElement(librarySource) as LibraryElementImpl; | |
6685 } | |
6686 | |
6687 /** | |
6688 * Return an array of the [CompilationUnit]s that make up the library. The fir
st unit is | |
6689 * always the defining unit. | |
6690 * | |
6691 * @return an array of the [CompilationUnit]s that make up the library. The fi
rst unit is | |
6692 * always the defining unit | |
6693 */ | |
6694 List<CompilationUnit> get compilationUnits { | |
6695 List<CompilationUnit> unitArrayList = new List<CompilationUnit>(); | |
6696 unitArrayList.add(definingCompilationUnit); | |
6697 for (Source source in _astMap.keys.toSet()) { | |
6698 if (librarySource != source) { | |
6699 unitArrayList.add(getAST(source)); | |
6700 } | |
6701 } | |
6702 return unitArrayList; | |
6703 } | |
6704 | |
6705 /** | |
6706 * Return a collection containing the sources for the compilation units in thi
s library, including | |
6707 * the defining compilation unit. | |
6708 * | |
6709 * @return the sources for the compilation units in this library | |
6710 */ | |
6711 Set<Source> get compilationUnitSources => _astMap.keys.toSet(); | |
6712 | |
6713 /** | |
6714 * Return the AST structure associated with the defining compilation unit for
this library. | |
6715 * | |
6716 * @return the AST structure associated with the defining compilation unit for
this library | |
6717 * @throws AnalysisException if an AST structure could not be created for the
defining compilation | |
6718 * unit | |
6719 */ | |
6720 CompilationUnit get definingCompilationUnit => getAST(librarySource); | |
6721 | |
6722 /** | |
6723 * Set the libraries that are exported by this library to be those in the give
n array. | |
6724 * | |
6725 * @param exportedLibraries the libraries that are exported by this library | |
6726 */ | |
6727 void set exportedLibraries(List<Library> exportedLibraries) { | |
6728 this._exportedLibraries = exportedLibraries; | |
6729 } | |
6730 | |
6731 /** | |
6732 * Return an array containing the libraries that are exported from this librar
y. | |
6733 * | |
6734 * @return an array containing the libraries that are exported from this libra
ry | |
6735 */ | |
6736 List<Library> get exports => _exportedLibraries; | |
6737 | |
6738 /** | |
6739 * Set the libraries that are imported into this library to be those in the gi
ven array. | |
6740 * | |
6741 * @param importedLibraries the libraries that are imported into this library | |
6742 */ | |
6743 void set importedLibraries(List<Library> importedLibraries) { | |
6744 this._importedLibraries = importedLibraries; | |
6745 } | |
6746 | |
6747 /** | |
6748 * Return an array containing the libraries that are imported into this librar
y. | |
6749 * | |
6750 * @return an array containing the libraries that are imported into this libra
ry | |
6751 */ | |
6752 List<Library> get imports => _importedLibraries; | |
6753 | |
6754 /** | |
6755 * Return an array containing the libraries that are either imported or export
ed from this | |
6756 * library. | |
6757 * | |
6758 * @return the libraries that are either imported or exported from this librar
y | |
6759 */ | |
6760 List<Library> get importsAndExports { | |
6761 HashSet<Library> libraries = new HashSet<Library>(); | |
6762 for (Library library in _importedLibraries) { | |
6763 libraries.add(library); | |
6764 } | |
6765 for (Library library in _exportedLibraries) { | |
6766 libraries.add(library); | |
6767 } | |
6768 return new List.from(libraries); | |
6769 } | |
6770 | |
6771 /** | |
6772 * Return the inheritance manager for this library. | |
6773 * | |
6774 * @return the inheritance manager for this library | |
6775 */ | |
6776 InheritanceManager get inheritanceManager { | |
6777 if (_inheritanceManager == null) { | |
6778 return _inheritanceManager = new InheritanceManager(_libraryElement); | |
6779 } | |
6780 return _inheritanceManager; | |
6781 } | |
6782 | |
6783 /** | |
6784 * Return the library element representing this library, creating it if necess
ary. | |
6785 * | |
6786 * @return the library element representing this library | |
6787 */ | |
6788 LibraryElementImpl get libraryElement { | |
6789 if (_libraryElement == null) { | |
6790 try { | |
6791 _libraryElement = _analysisContext | |
6792 .computeLibraryElement(librarySource) as LibraryElementImpl; | |
6793 } on AnalysisException catch (exception, stackTrace) { | |
6794 AnalysisEngine.instance.logger.logError( | |
6795 "Could not compute library element for ${librarySource.fullName}", | |
6796 new CaughtException(exception, stackTrace)); | |
6797 } | |
6798 } | |
6799 return _libraryElement; | |
6800 } | |
6801 | |
6802 /** | |
6803 * Set the library element representing this library to the given library elem
ent. | |
6804 * | |
6805 * @param libraryElement the library element representing this library | |
6806 */ | |
6807 void set libraryElement(LibraryElementImpl libraryElement) { | |
6808 this._libraryElement = libraryElement; | |
6809 if (_inheritanceManager != null) { | |
6810 _inheritanceManager.libraryElement = libraryElement; | |
6811 } | |
6812 } | |
6813 | |
6814 /** | |
6815 * Return the library scope used when resolving elements within this library's
compilation units. | |
6816 * | |
6817 * @return the library scope used when resolving elements within this library'
s compilation units | |
6818 */ | |
6819 LibraryScope get libraryScope { | |
6820 if (_libraryScope == null) { | |
6821 _libraryScope = new LibraryScope(_libraryElement, errorListener); | |
6822 } | |
6823 return _libraryScope; | |
6824 } | |
6825 | |
6826 /** | |
6827 * Return the AST structure associated with the given source. | |
6828 * | |
6829 * @param source the source representing the compilation unit whose AST is to
be returned | |
6830 * @return the AST structure associated with the given source | |
6831 * @throws AnalysisException if an AST structure could not be created for the
compilation unit | |
6832 */ | |
6833 CompilationUnit getAST(Source source) { | |
6834 CompilationUnit unit = _astMap[source]; | |
6835 if (unit == null) { | |
6836 unit = _analysisContext.computeResolvableCompilationUnit(source); | |
6837 _astMap[source] = unit; | |
6838 } | |
6839 return unit; | |
6840 } | |
6841 | |
6842 /** | |
6843 * Return the result of resolving the URI of the given URI-based directive aga
inst the URI of the | |
6844 * library, or `null` if the URI is not valid. If the URI is not valid, report
the error. | |
6845 * | |
6846 * @param directive the directive which URI should be resolved | |
6847 * @return the result of resolving the URI against the URI of the library | |
6848 */ | |
6849 Source getSource(UriBasedDirective directive) { | |
6850 StringLiteral uriLiteral = directive.uri; | |
6851 if (uriLiteral is StringInterpolation) { | |
6852 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, | |
6853 uriLiteral.length, CompileTimeErrorCode.URI_WITH_INTERPOLATION)); | |
6854 return null; | |
6855 } | |
6856 String uriContent = uriLiteral.stringValue.trim(); | |
6857 _directiveUris[directive] = uriContent; | |
6858 uriContent = Uri.encodeFull(uriContent); | |
6859 if (directive is ImportDirective && | |
6860 uriContent.startsWith(_DART_EXT_SCHEME)) { | |
6861 _libraryElement.hasExtUri = true; | |
6862 return null; | |
6863 } | |
6864 try { | |
6865 parseUriWithException(uriContent); | |
6866 Source source = | |
6867 _analysisContext.sourceFactory.resolveUri(librarySource, uriContent); | |
6868 if (!_analysisContext.exists(source)) { | |
6869 errorListener.onError(new AnalysisError(librarySource, | |
6870 uriLiteral.offset, uriLiteral.length, | |
6871 CompileTimeErrorCode.URI_DOES_NOT_EXIST, [uriContent])); | |
6872 } | |
6873 return source; | |
6874 } on URISyntaxException { | |
6875 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, | |
6876 uriLiteral.length, CompileTimeErrorCode.INVALID_URI, [uriContent])); | |
6877 } | |
6878 return null; | |
6879 } | |
6880 | |
6881 /** | |
6882 * Returns the URI value of the given directive. | |
6883 */ | |
6884 String getUri(UriBasedDirective directive) => _directiveUris[directive]; | |
6885 | |
6886 /** | |
6887 * Set the AST structure associated with the defining compilation unit for thi
s library to the | |
6888 * given AST structure. | |
6889 * | |
6890 * @param unit the AST structure associated with the defining compilation unit
for this library | |
6891 */ | |
6892 void setDefiningCompilationUnit(CompilationUnit unit) { | |
6893 _astMap[librarySource] = unit; | |
6894 } | |
6895 | |
6896 @override | |
6897 String toString() => librarySource.shortName; | |
6898 } | |
6899 | |
6900 /** | |
6901 * Instances of the class `LibraryElementBuilder` build an element model for a s
ingle library. | |
6902 */ | |
6903 class LibraryElementBuilder { | |
6904 /** | |
6905 * The analysis context in which the element model will be built. | |
6906 */ | |
6907 final InternalAnalysisContext _analysisContext; | |
6908 | |
6909 /** | |
6910 * The listener to which errors will be reported. | |
6911 */ | |
6912 final AnalysisErrorListener _errorListener; | |
6913 | |
6914 /** | |
6915 * Initialize a newly created library element builder. | |
6916 * | |
6917 * @param analysisContext the analysis context in which the element model will
be built | |
6918 * @param errorListener the listener to which errors will be reported | |
6919 */ | |
6920 LibraryElementBuilder(this._analysisContext, this._errorListener); | |
6921 | |
6922 /** | |
6923 * Build the library element for the given library. | |
6924 * | |
6925 * @param library the library for which an element model is to be built | |
6926 * @return the library element that was built | |
6927 * @throws AnalysisException if the analysis could not be performed | |
6928 */ | |
6929 LibraryElementImpl buildLibrary(Library library) { | |
6930 CompilationUnitBuilder builder = new CompilationUnitBuilder(); | |
6931 Source librarySource = library.librarySource; | |
6932 CompilationUnit definingCompilationUnit = library.definingCompilationUnit; | |
6933 CompilationUnitElementImpl definingCompilationUnitElement = builder | |
6934 .buildCompilationUnit( | |
6935 librarySource, definingCompilationUnit, librarySource); | |
6936 NodeList<Directive> directives = definingCompilationUnit.directives; | |
6937 LibraryIdentifier libraryNameNode = null; | |
6938 bool hasPartDirective = false; | |
6939 FunctionElement entryPoint = | |
6940 _findEntryPoint(definingCompilationUnitElement); | |
6941 List<Directive> directivesToResolve = new List<Directive>(); | |
6942 List<CompilationUnitElementImpl> sourcedCompilationUnits = | |
6943 new List<CompilationUnitElementImpl>(); | |
6944 for (Directive directive in directives) { | |
6945 // | |
6946 // We do not build the elements representing the import and export | |
6947 // directives at this point. That is not done until we get to | |
6948 // LibraryResolver.buildDirectiveModels() because we need the | |
6949 // LibraryElements for the referenced libraries, which might not exist at | |
6950 // this point (due to the possibility of circular references). | |
6951 // | |
6952 if (directive is LibraryDirective) { | |
6953 if (libraryNameNode == null) { | |
6954 libraryNameNode = directive.name; | |
6955 directivesToResolve.add(directive); | |
6956 } | |
6957 } else if (directive is PartDirective) { | |
6958 PartDirective partDirective = directive; | |
6959 StringLiteral partUri = partDirective.uri; | |
6960 Source partSource = partDirective.source; | |
6961 if (_analysisContext.exists(partSource)) { | |
6962 hasPartDirective = true; | |
6963 CompilationUnit partUnit = library.getAST(partSource); | |
6964 CompilationUnitElementImpl part = | |
6965 builder.buildCompilationUnit(partSource, partUnit, librarySource); | |
6966 part.uriOffset = partUri.offset; | |
6967 part.uriEnd = partUri.end; | |
6968 part.uri = partDirective.uriContent; | |
6969 // | |
6970 // Validate that the part contains a part-of directive with the same | |
6971 // name as the library. | |
6972 // | |
6973 String partLibraryName = | |
6974 _getPartLibraryName(partSource, partUnit, directivesToResolve); | |
6975 if (partLibraryName == null) { | |
6976 _errorListener.onError(new AnalysisError(librarySource, | |
6977 partUri.offset, partUri.length, | |
6978 CompileTimeErrorCode.PART_OF_NON_PART, [partUri.toSource()])); | |
6979 } else if (libraryNameNode == null) { | |
6980 // TODO(brianwilkerson) Collect the names declared by the part. | |
6981 // If they are all the same then we can use that name as the | |
6982 // inferred name of the library and present it in a quick-fix. | |
6983 // partLibraryNames.add(partLibraryName); | |
6984 } else if (libraryNameNode.name != partLibraryName) { | |
6985 _errorListener.onError(new AnalysisError(librarySource, | |
6986 partUri.offset, partUri.length, | |
6987 StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, [ | |
6988 libraryNameNode.name, | |
6989 partLibraryName | |
6990 ])); | |
6991 } | |
6992 if (entryPoint == null) { | |
6993 entryPoint = _findEntryPoint(part); | |
6994 } | |
6995 directive.element = part; | |
6996 sourcedCompilationUnits.add(part); | |
6997 } | |
6998 } | |
6999 } | |
7000 if (hasPartDirective && libraryNameNode == null) { | |
7001 _errorListener.onError(new AnalysisError(librarySource, 0, 0, | |
7002 ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART)); | |
7003 } | |
7004 // | |
7005 // Create and populate the library element. | |
7006 // | |
7007 LibraryElementImpl libraryElement = new LibraryElementImpl.forNode( | |
7008 _analysisContext.getContextFor(librarySource), libraryNameNode); | |
7009 libraryElement.definingCompilationUnit = definingCompilationUnitElement; | |
7010 if (entryPoint != null) { | |
7011 libraryElement.entryPoint = entryPoint; | |
7012 } | |
7013 int sourcedUnitCount = sourcedCompilationUnits.length; | |
7014 libraryElement.parts = sourcedCompilationUnits; | |
7015 for (Directive directive in directivesToResolve) { | |
7016 directive.element = libraryElement; | |
7017 } | |
7018 library.libraryElement = libraryElement; | |
7019 if (sourcedUnitCount > 0) { | |
7020 _patchTopLevelAccessors(libraryElement); | |
7021 } | |
7022 return libraryElement; | |
7023 } | |
7024 | |
7025 /** | |
7026 * Build the library element for the given library. The resulting element is | |
7027 * stored in the [ResolvableLibrary] structure. | |
7028 * | |
7029 * @param library the library for which an element model is to be built | |
7030 * @throws AnalysisException if the analysis could not be performed | |
7031 */ | |
7032 void buildLibrary2(ResolvableLibrary library) { | |
7033 CompilationUnitBuilder builder = new CompilationUnitBuilder(); | |
7034 Source librarySource = library.librarySource; | |
7035 CompilationUnit definingCompilationUnit = library.definingCompilationUnit; | |
7036 CompilationUnitElementImpl definingCompilationUnitElement = builder | |
7037 .buildCompilationUnit( | |
7038 librarySource, definingCompilationUnit, librarySource); | |
7039 NodeList<Directive> directives = definingCompilationUnit.directives; | |
7040 LibraryIdentifier libraryNameNode = null; | |
7041 bool hasPartDirective = false; | |
7042 FunctionElement entryPoint = | |
7043 _findEntryPoint(definingCompilationUnitElement); | |
7044 List<Directive> directivesToResolve = new List<Directive>(); | |
7045 List<CompilationUnitElementImpl> sourcedCompilationUnits = | |
7046 new List<CompilationUnitElementImpl>(); | |
7047 for (Directive directive in directives) { | |
7048 // | |
7049 // We do not build the elements representing the import and export | |
7050 // directives at this point. That is not done until we get to | |
7051 // LibraryResolver.buildDirectiveModels() because we need the | |
7052 // LibraryElements for the referenced libraries, which might not exist at | |
7053 // this point (due to the possibility of circular references). | |
7054 // | |
7055 if (directive is LibraryDirective) { | |
7056 if (libraryNameNode == null) { | |
7057 libraryNameNode = directive.name; | |
7058 directivesToResolve.add(directive); | |
7059 } | |
7060 } else if (directive is PartDirective) { | |
7061 PartDirective partDirective = directive; | |
7062 StringLiteral partUri = partDirective.uri; | |
7063 Source partSource = partDirective.source; | |
7064 if (_analysisContext.exists(partSource)) { | |
7065 hasPartDirective = true; | |
7066 CompilationUnit partUnit = library.getAST(partSource); | |
7067 if (partUnit != null) { | |
7068 CompilationUnitElementImpl part = builder.buildCompilationUnit( | |
7069 partSource, partUnit, librarySource); | |
7070 part.uriOffset = partUri.offset; | |
7071 part.uriEnd = partUri.end; | |
7072 part.uri = partDirective.uriContent; | |
7073 // | |
7074 // Validate that the part contains a part-of directive with the same | |
7075 // name as the library. | |
7076 // | |
7077 String partLibraryName = | |
7078 _getPartLibraryName(partSource, partUnit, directivesToResolve); | |
7079 if (partLibraryName == null) { | |
7080 _errorListener.onError(new AnalysisError(librarySource, | |
7081 partUri.offset, partUri.length, | |
7082 CompileTimeErrorCode.PART_OF_NON_PART, [partUri.toSource()])); | |
7083 } else if (libraryNameNode == null) { | |
7084 // TODO(brianwilkerson) Collect the names declared by the part. | |
7085 // If they are all the same then we can use that name as the | |
7086 // inferred name of the library and present it in a quick-fix. | |
7087 // partLibraryNames.add(partLibraryName); | |
7088 } else if (libraryNameNode.name != partLibraryName) { | |
7089 _errorListener.onError(new AnalysisError(librarySource, | |
7090 partUri.offset, partUri.length, | |
7091 StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, [ | |
7092 libraryNameNode.name, | |
7093 partLibraryName | |
7094 ])); | |
7095 } | |
7096 if (entryPoint == null) { | |
7097 entryPoint = _findEntryPoint(part); | |
7098 } | |
7099 directive.element = part; | |
7100 sourcedCompilationUnits.add(part); | |
7101 } | |
7102 } | |
7103 } | |
7104 } | |
7105 if (hasPartDirective && libraryNameNode == null) { | |
7106 _errorListener.onError(new AnalysisError(librarySource, 0, 0, | |
7107 ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART)); | |
7108 } | |
7109 // | |
7110 // Create and populate the library element. | |
7111 // | |
7112 LibraryElementImpl libraryElement = new LibraryElementImpl.forNode( | |
7113 _analysisContext.getContextFor(librarySource), libraryNameNode); | |
7114 libraryElement.definingCompilationUnit = definingCompilationUnitElement; | |
7115 if (entryPoint != null) { | |
7116 libraryElement.entryPoint = entryPoint; | |
7117 } | |
7118 int sourcedUnitCount = sourcedCompilationUnits.length; | |
7119 libraryElement.parts = sourcedCompilationUnits; | |
7120 for (Directive directive in directivesToResolve) { | |
7121 directive.element = libraryElement; | |
7122 } | |
7123 library.libraryElement = libraryElement; | |
7124 if (sourcedUnitCount > 0) { | |
7125 _patchTopLevelAccessors(libraryElement); | |
7126 } | |
7127 } | |
7128 | |
7129 /** | |
7130 * Add all of the non-synthetic getters and setters defined in the given compi
lation unit that | |
7131 * have no corresponding accessor to one of the given collections. | |
7132 * | |
7133 * @param getters the map to which getters are to be added | |
7134 * @param setters the list to which setters are to be added | |
7135 * @param unit the compilation unit defining the accessors that are potentiall
y being added | |
7136 */ | |
7137 void _collectAccessors(HashMap<String, PropertyAccessorElement> getters, | |
7138 List<PropertyAccessorElement> setters, CompilationUnitElement unit) { | |
7139 for (PropertyAccessorElement accessor in unit.accessors) { | |
7140 if (accessor.isGetter) { | |
7141 if (!accessor.isSynthetic && accessor.correspondingSetter == null) { | |
7142 getters[accessor.displayName] = accessor; | |
7143 } | |
7144 } else { | |
7145 if (!accessor.isSynthetic && accessor.correspondingGetter == null) { | |
7146 setters.add(accessor); | |
7147 } | |
7148 } | |
7149 } | |
7150 } | |
7151 | |
7152 /** | |
7153 * Search the top-level functions defined in the given compilation unit for th
e entry point. | |
7154 * | |
7155 * @param element the compilation unit to be searched | |
7156 * @return the entry point that was found, or `null` if the compilation unit d
oes not define | |
7157 * an entry point | |
7158 */ | |
7159 FunctionElement _findEntryPoint(CompilationUnitElementImpl element) { | |
7160 for (FunctionElement function in element.functions) { | |
7161 if (function.isEntryPoint) { | |
7162 return function; | |
7163 } | |
7164 } | |
7165 return null; | |
7166 } | |
7167 | |
7168 /** | |
7169 * Return the name of the library that the given part is declared to be a part
of, or `null` | |
7170 * if the part does not contain a part-of directive. | |
7171 * | |
7172 * @param partSource the source representing the part | |
7173 * @param partUnit the AST structure of the part | |
7174 * @param directivesToResolve a list of directives that should be resolved to
the library being | |
7175 * built | |
7176 * @return the name of the library that the given part is declared to be a par
t of | |
7177 */ | |
7178 String _getPartLibraryName(Source partSource, CompilationUnit partUnit, | |
7179 List<Directive> directivesToResolve) { | |
7180 for (Directive directive in partUnit.directives) { | |
7181 if (directive is PartOfDirective) { | |
7182 directivesToResolve.add(directive); | |
7183 LibraryIdentifier libraryName = directive.libraryName; | |
7184 if (libraryName != null) { | |
7185 return libraryName.name; | |
7186 } | |
7187 } | |
7188 } | |
7189 return null; | |
7190 } | |
7191 | |
7192 /** | |
7193 * Look through all of the compilation units defined for the given library, lo
oking for getters | |
7194 * and setters that are defined in different compilation units but that have t
he same names. If | |
7195 * any are found, make sure that they have the same variable element. | |
7196 * | |
7197 * @param libraryElement the library defining the compilation units to be proc
essed | |
7198 */ | |
7199 void _patchTopLevelAccessors(LibraryElementImpl libraryElement) { | |
7200 HashMap<String, PropertyAccessorElement> getters = | |
7201 new HashMap<String, PropertyAccessorElement>(); | |
7202 List<PropertyAccessorElement> setters = new List<PropertyAccessorElement>(); | |
7203 _collectAccessors(getters, setters, libraryElement.definingCompilationUnit); | |
7204 for (CompilationUnitElement unit in libraryElement.parts) { | |
7205 _collectAccessors(getters, setters, unit); | |
7206 } | |
7207 for (PropertyAccessorElement setter in setters) { | |
7208 PropertyAccessorElement getter = getters[setter.displayName]; | |
7209 if (getter != null) { | |
7210 PropertyInducingElementImpl variable = | |
7211 getter.variable as PropertyInducingElementImpl; | |
7212 variable.setter = setter; | |
7213 (setter as PropertyAccessorElementImpl).variable = variable; | |
7214 } | |
7215 } | |
7216 } | |
7217 } | |
7218 | |
7219 /** | |
7220 * Instances of the class `LibraryImportScope` represent the scope containing al
l of the names | |
7221 * available from imported libraries. | |
7222 */ | |
7223 class LibraryImportScope extends Scope { | |
7224 /** | |
7225 * The element representing the library in which this scope is enclosed. | |
7226 */ | |
7227 final LibraryElement _definingLibrary; | |
7228 | |
7229 /** | |
7230 * The listener that is to be informed when an error is encountered. | |
7231 */ | |
7232 final AnalysisErrorListener errorListener; | |
7233 | |
7234 /** | |
7235 * A list of the namespaces representing the names that are available in this
scope from imported | |
7236 * libraries. | |
7237 */ | |
7238 List<Namespace> _importedNamespaces; | |
7239 | |
7240 /** | |
7241 * Initialize a newly created scope representing the names imported into the g
iven library. | |
7242 * | |
7243 * @param definingLibrary the element representing the library that imports th
e names defined in | |
7244 * this scope | |
7245 * @param errorListener the listener that is to be informed when an error is e
ncountered | |
7246 */ | |
7247 LibraryImportScope(this._definingLibrary, this.errorListener) { | |
7248 _createImportedNamespaces(); | |
7249 } | |
7250 | |
7251 @override | |
7252 void define(Element element) { | |
7253 if (!Scope.isPrivateName(element.displayName)) { | |
7254 super.define(element); | |
7255 } | |
7256 } | |
7257 | |
7258 @override | |
7259 Source getSource(AstNode node) { | |
7260 Source source = super.getSource(node); | |
7261 if (source == null) { | |
7262 source = _definingLibrary.definingCompilationUnit.source; | |
7263 } | |
7264 return source; | |
7265 } | |
7266 | |
7267 @override | |
7268 Element internalLookup( | |
7269 Identifier identifier, String name, LibraryElement referencingLibrary) { | |
7270 Element foundElement = localLookup(name, referencingLibrary); | |
7271 if (foundElement != null) { | |
7272 return foundElement; | |
7273 } | |
7274 for (int i = 0; i < _importedNamespaces.length; i++) { | |
7275 Namespace nameSpace = _importedNamespaces[i]; | |
7276 Element element = nameSpace.get(name); | |
7277 if (element != null) { | |
7278 if (foundElement == null) { | |
7279 foundElement = element; | |
7280 } else if (!identical(foundElement, element)) { | |
7281 foundElement = MultiplyDefinedElementImpl.fromElements( | |
7282 _definingLibrary.context, foundElement, element); | |
7283 } | |
7284 } | |
7285 } | |
7286 if (foundElement is MultiplyDefinedElementImpl) { | |
7287 foundElement = _removeSdkElements( | |
7288 identifier, name, foundElement as MultiplyDefinedElementImpl); | |
7289 } | |
7290 if (foundElement is MultiplyDefinedElementImpl) { | |
7291 String foundEltName = foundElement.displayName; | |
7292 List<Element> conflictingMembers = foundElement.conflictingElements; | |
7293 int count = conflictingMembers.length; | |
7294 List<String> libraryNames = new List<String>(count); | |
7295 for (int i = 0; i < count; i++) { | |
7296 libraryNames[i] = _getLibraryName(conflictingMembers[i]); | |
7297 } | |
7298 libraryNames.sort(); | |
7299 errorListener.onError(new AnalysisError(getSource(identifier), | |
7300 identifier.offset, identifier.length, | |
7301 StaticWarningCode.AMBIGUOUS_IMPORT, [ | |
7302 foundEltName, | |
7303 StringUtilities.printListOfQuotedNames(libraryNames) | |
7304 ])); | |
7305 return foundElement; | |
7306 } | |
7307 if (foundElement != null) { | |
7308 defineNameWithoutChecking(name, foundElement); | |
7309 } | |
7310 return foundElement; | |
7311 } | |
7312 | |
7313 /** | |
7314 * Create all of the namespaces associated with the libraries imported into th
is library. The | |
7315 * names are not added to this scope, but are stored for later reference. | |
7316 * | |
7317 * @param definingLibrary the element representing the library that imports th
e libraries for | |
7318 * which namespaces will be created | |
7319 */ | |
7320 void _createImportedNamespaces() { | |
7321 NamespaceBuilder builder = new NamespaceBuilder(); | |
7322 List<ImportElement> imports = _definingLibrary.imports; | |
7323 int count = imports.length; | |
7324 _importedNamespaces = new List<Namespace>(count); | |
7325 for (int i = 0; i < count; i++) { | |
7326 _importedNamespaces[i] = | |
7327 builder.createImportNamespaceForDirective(imports[i]); | |
7328 } | |
7329 } | |
7330 | |
7331 /** | |
7332 * Returns the name of the library that defines given element. | |
7333 * | |
7334 * @param element the element to get library name | |
7335 * @return the name of the library that defines given element | |
7336 */ | |
7337 String _getLibraryName(Element element) { | |
7338 if (element == null) { | |
7339 return StringUtilities.EMPTY; | |
7340 } | |
7341 LibraryElement library = element.library; | |
7342 if (library == null) { | |
7343 return StringUtilities.EMPTY; | |
7344 } | |
7345 List<ImportElement> imports = _definingLibrary.imports; | |
7346 int count = imports.length; | |
7347 for (int i = 0; i < count; i++) { | |
7348 if (identical(imports[i].importedLibrary, library)) { | |
7349 return library.definingCompilationUnit.displayName; | |
7350 } | |
7351 } | |
7352 List<String> indirectSources = new List<String>(); | |
7353 for (int i = 0; i < count; i++) { | |
7354 LibraryElement importedLibrary = imports[i].importedLibrary; | |
7355 if (importedLibrary != null) { | |
7356 for (LibraryElement exportedLibrary | |
7357 in importedLibrary.exportedLibraries) { | |
7358 if (identical(exportedLibrary, library)) { | |
7359 indirectSources | |
7360 .add(importedLibrary.definingCompilationUnit.displayName); | |
7361 } | |
7362 } | |
7363 } | |
7364 } | |
7365 int indirectCount = indirectSources.length; | |
7366 StringBuffer buffer = new StringBuffer(); | |
7367 buffer.write(library.definingCompilationUnit.displayName); | |
7368 if (indirectCount > 0) { | |
7369 buffer.write(" (via "); | |
7370 if (indirectCount > 1) { | |
7371 indirectSources.sort(); | |
7372 buffer.write(StringUtilities.printListOfQuotedNames(indirectSources)); | |
7373 } else { | |
7374 buffer.write(indirectSources[0]); | |
7375 } | |
7376 buffer.write(")"); | |
7377 } | |
7378 return buffer.toString(); | |
7379 } | |
7380 | |
7381 /** | |
7382 * Given a collection of elements (captured by the [foundElement]) that the | |
7383 * [identifier] (with the given [name]) resolved to, remove from the list all | |
7384 * of the names defined in the SDK and return the element(s) that remain. | |
7385 */ | |
7386 Element _removeSdkElements(Identifier identifier, String name, | |
7387 MultiplyDefinedElementImpl foundElement) { | |
7388 List<Element> conflictingElements = foundElement.conflictingElements; | |
7389 List<Element> nonSdkElements = new List<Element>(); | |
7390 Element sdkElement = null; | |
7391 for (Element member in conflictingElements) { | |
7392 if (member.library.isInSdk) { | |
7393 sdkElement = member; | |
7394 } else { | |
7395 nonSdkElements.add(member); | |
7396 } | |
7397 } | |
7398 if (sdkElement != null && nonSdkElements.length > 0) { | |
7399 String sdkLibName = _getLibraryName(sdkElement); | |
7400 String otherLibName = _getLibraryName(nonSdkElements[0]); | |
7401 errorListener.onError(new AnalysisError(getSource(identifier), | |
7402 identifier.offset, identifier.length, | |
7403 StaticWarningCode.CONFLICTING_DART_IMPORT, [ | |
7404 name, | |
7405 sdkLibName, | |
7406 otherLibName | |
7407 ])); | |
7408 } | |
7409 if (nonSdkElements.length == conflictingElements.length) { | |
7410 // None of the members were removed | |
7411 return foundElement; | |
7412 } else if (nonSdkElements.length == 1) { | |
7413 // All but one member was removed | |
7414 return nonSdkElements[0]; | |
7415 } else if (nonSdkElements.length == 0) { | |
7416 // All members were removed | |
7417 AnalysisEngine.instance.logger | |
7418 .logInformation("Multiply defined SDK element: $foundElement"); | |
7419 return foundElement; | |
7420 } | |
7421 return new MultiplyDefinedElementImpl( | |
7422 _definingLibrary.context, nonSdkElements); | |
7423 } | |
7424 } | |
7425 | |
7426 /** | |
7427 * Instances of the class `LibraryResolver` are used to resolve one or more mutu
ally dependent | |
7428 * libraries within a single context. | |
7429 */ | |
7430 class LibraryResolver { | |
7431 /** | |
7432 * The analysis context in which the libraries are being analyzed. | |
7433 */ | |
7434 final InternalAnalysisContext analysisContext; | |
7435 | |
7436 /** | |
7437 * The listener to which analysis errors will be reported, this error listener
is either | |
7438 * references [recordingErrorListener], or it unions the passed | |
7439 * [AnalysisErrorListener] with the [recordingErrorListener]. | |
7440 */ | |
7441 RecordingErrorListener _errorListener; | |
7442 | |
7443 /** | |
7444 * A source object representing the core library (dart:core). | |
7445 */ | |
7446 Source _coreLibrarySource; | |
7447 | |
7448 /** | |
7449 * A Source object representing the async library (dart:async). | |
7450 */ | |
7451 Source _asyncLibrarySource; | |
7452 | |
7453 /** | |
7454 * The object representing the core library. | |
7455 */ | |
7456 Library _coreLibrary; | |
7457 | |
7458 /** | |
7459 * The object representing the async library. | |
7460 */ | |
7461 Library _asyncLibrary; | |
7462 | |
7463 /** | |
7464 * The object used to access the types from the core library. | |
7465 */ | |
7466 TypeProvider _typeProvider; | |
7467 | |
7468 /** | |
7469 * A table mapping library sources to the information being maintained for tho
se libraries. | |
7470 */ | |
7471 HashMap<Source, Library> _libraryMap = new HashMap<Source, Library>(); | |
7472 | |
7473 /** | |
7474 * A collection containing the libraries that are being resolved together. | |
7475 */ | |
7476 Set<Library> _librariesInCycles; | |
7477 | |
7478 /** | |
7479 * Initialize a newly created library resolver to resolve libraries within the
given context. | |
7480 * | |
7481 * @param analysisContext the analysis context in which the library is being a
nalyzed | |
7482 */ | |
7483 LibraryResolver(this.analysisContext) { | |
7484 this._errorListener = new RecordingErrorListener(); | |
7485 _coreLibrarySource = | |
7486 analysisContext.sourceFactory.forUri(DartSdk.DART_CORE); | |
7487 _asyncLibrarySource = | |
7488 analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC); | |
7489 } | |
7490 | |
7491 /** | |
7492 * Return the listener to which analysis errors will be reported. | |
7493 * | |
7494 * @return the listener to which analysis errors will be reported | |
7495 */ | |
7496 RecordingErrorListener get errorListener => _errorListener; | |
7497 | |
7498 /** | |
7499 * Return an array containing information about all of the libraries that were
resolved. | |
7500 * | |
7501 * @return an array containing the libraries that were resolved | |
7502 */ | |
7503 Set<Library> get resolvedLibraries => _librariesInCycles; | |
7504 | |
7505 /** | |
7506 * The object used to access the types from the core library. | |
7507 */ | |
7508 TypeProvider get typeProvider => _typeProvider; | |
7509 | |
7510 /** | |
7511 * Create an object to represent the information about the library defined by
the compilation unit | |
7512 * with the given source. | |
7513 * | |
7514 * @param librarySource the source of the library's defining compilation unit | |
7515 * @return the library object that was created | |
7516 * @throws AnalysisException if the library source is not valid | |
7517 */ | |
7518 Library createLibrary(Source librarySource) { | |
7519 Library library = | |
7520 new Library(analysisContext, _errorListener, librarySource); | |
7521 _libraryMap[librarySource] = library; | |
7522 return library; | |
7523 } | |
7524 | |
7525 /** | |
7526 * Resolve the library specified by the given source in the given context. The
library is assumed | |
7527 * to be embedded in the given source. | |
7528 * | |
7529 * @param librarySource the source specifying the defining compilation unit of
the library to be | |
7530 * resolved | |
7531 * @param unit the compilation unit representing the embedded library | |
7532 * @param fullAnalysis `true` if a full analysis should be performed | |
7533 * @return the element representing the resolved library | |
7534 * @throws AnalysisException if the library could not be resolved for some rea
son | |
7535 */ | |
7536 LibraryElement resolveEmbeddedLibrary( | |
7537 Source librarySource, CompilationUnit unit, bool fullAnalysis) { | |
7538 // | |
7539 // Create the objects representing the library being resolved and the core | |
7540 // library. | |
7541 // | |
7542 Library targetLibrary = _createLibraryWithUnit(librarySource, unit); | |
7543 _coreLibrary = _libraryMap[_coreLibrarySource]; | |
7544 if (_coreLibrary == null) { | |
7545 // This will only happen if the library being analyzed is the core | |
7546 // library. | |
7547 _coreLibrary = createLibrary(_coreLibrarySource); | |
7548 if (_coreLibrary == null) { | |
7549 LibraryResolver2.missingCoreLibrary( | |
7550 analysisContext, _coreLibrarySource); | |
7551 } | |
7552 } | |
7553 _asyncLibrary = _libraryMap[_asyncLibrarySource]; | |
7554 if (_asyncLibrary == null) { | |
7555 // This will only happen if the library being analyzed is the async | |
7556 // library. | |
7557 _asyncLibrary = createLibrary(_asyncLibrarySource); | |
7558 if (_asyncLibrary == null) { | |
7559 LibraryResolver2.missingAsyncLibrary( | |
7560 analysisContext, _asyncLibrarySource); | |
7561 } | |
7562 } | |
7563 // | |
7564 // Compute the set of libraries that need to be resolved together. | |
7565 // | |
7566 _computeEmbeddedLibraryDependencies(targetLibrary, unit); | |
7567 _librariesInCycles = _computeLibrariesInCycles(targetLibrary); | |
7568 // | |
7569 // Build the element models representing the libraries being resolved. | |
7570 // This is done in three steps: | |
7571 // | |
7572 // 1. Build the basic element models without making any connections | |
7573 // between elements other than the basic parent/child relationships. | |
7574 // This includes building the elements representing the libraries. | |
7575 // 2. Build the elements for the import and export directives. This | |
7576 // requires that we have the elements built for the referenced | |
7577 // libraries, but because of the possibility of circular references | |
7578 // needs to happen after all of the library elements have been created. | |
7579 // 3. Build the rest of the type model by connecting superclasses, mixins, | |
7580 // and interfaces. This requires that we be able to compute the names | |
7581 // visible in the libraries being resolved, which in turn requires that | |
7582 // we have resolved the import directives. | |
7583 // | |
7584 _buildElementModels(); | |
7585 LibraryElement coreElement = _coreLibrary.libraryElement; | |
7586 if (coreElement == null) { | |
7587 throw new AnalysisException("Could not resolve dart:core"); | |
7588 } | |
7589 LibraryElement asyncElement = _asyncLibrary.libraryElement; | |
7590 if (asyncElement == null) { | |
7591 throw new AnalysisException("Could not resolve dart:async"); | |
7592 } | |
7593 _buildDirectiveModels(); | |
7594 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); | |
7595 _buildTypeHierarchies(); | |
7596 // | |
7597 // Perform resolution and type analysis. | |
7598 // | |
7599 // TODO(brianwilkerson) Decide whether we want to resolve all of the | |
7600 // libraries or whether we want to only resolve the target library. | |
7601 // The advantage to resolving everything is that we have already done part | |
7602 // of the work so we'll avoid duplicated effort. The disadvantage of | |
7603 // resolving everything is that we might do extra work that we don't | |
7604 // really care about. Another possibility is to add a parameter to this | |
7605 // method and punt the decision to the clients. | |
7606 // | |
7607 //if (analyzeAll) { | |
7608 resolveReferencesAndTypes(); | |
7609 //} else { | |
7610 // resolveReferencesAndTypes(targetLibrary); | |
7611 //} | |
7612 _performConstantEvaluation(); | |
7613 return targetLibrary.libraryElement; | |
7614 } | |
7615 | |
7616 /** | |
7617 * Resolve the library specified by the given source in the given context. | |
7618 * | |
7619 * Note that because Dart allows circular imports between libraries, it is pos
sible that more than | |
7620 * one library will need to be resolved. In such cases the error listener can
receive errors from | |
7621 * multiple libraries. | |
7622 * | |
7623 * @param librarySource the source specifying the defining compilation unit of
the library to be | |
7624 * resolved | |
7625 * @param fullAnalysis `true` if a full analysis should be performed | |
7626 * @return the element representing the resolved library | |
7627 * @throws AnalysisException if the library could not be resolved for some rea
son | |
7628 */ | |
7629 LibraryElement resolveLibrary(Source librarySource, bool fullAnalysis) { | |
7630 // | |
7631 // Create the object representing the library being resolved and compute | |
7632 // the dependency relationship. Note that all libraries depend implicitly | |
7633 // on core, and we inject an ersatz dependency on async, so once this is | |
7634 // done the core and async library elements will have been created. | |
7635 // | |
7636 Library targetLibrary = createLibrary(librarySource); | |
7637 _computeLibraryDependencies(targetLibrary); | |
7638 _coreLibrary = _libraryMap[_coreLibrarySource]; | |
7639 _asyncLibrary = _libraryMap[_asyncLibrarySource]; | |
7640 // | |
7641 // Compute the set of libraries that need to be resolved together. | |
7642 // | |
7643 _librariesInCycles = _computeLibrariesInCycles(targetLibrary); | |
7644 // | |
7645 // Build the element models representing the libraries being resolved. | |
7646 // This is done in three steps: | |
7647 // | |
7648 // 1. Build the basic element models without making any connections | |
7649 // between elements other than the basic parent/child relationships. | |
7650 // This includes building the elements representing the libraries, but | |
7651 // excludes members defined in enums. | |
7652 // 2. Build the elements for the import and export directives. This | |
7653 // requires that we have the elements built for the referenced | |
7654 // libraries, but because of the possibility of circular references | |
7655 // needs to happen after all of the library elements have been created. | |
7656 // 3. Build the members in enum declarations. | |
7657 // 4. Build the rest of the type model by connecting superclasses, mixins, | |
7658 // and interfaces. This requires that we be able to compute the names | |
7659 // visible in the libraries being resolved, which in turn requires that | |
7660 // we have resolved the import directives. | |
7661 // | |
7662 _buildElementModels(); | |
7663 LibraryElement coreElement = _coreLibrary.libraryElement; | |
7664 if (coreElement == null) { | |
7665 throw new AnalysisException("Could not resolve dart:core"); | |
7666 } | |
7667 LibraryElement asyncElement = _asyncLibrary.libraryElement; | |
7668 if (asyncElement == null) { | |
7669 throw new AnalysisException("Could not resolve dart:async"); | |
7670 } | |
7671 _buildDirectiveModels(); | |
7672 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); | |
7673 _buildEnumMembers(); | |
7674 _buildTypeHierarchies(); | |
7675 // | |
7676 // Perform resolution and type analysis. | |
7677 // | |
7678 // TODO(brianwilkerson) Decide whether we want to resolve all of the | |
7679 // libraries or whether we want to only resolve the target library. The | |
7680 // advantage to resolving everything is that we have already done part of | |
7681 // the work so we'll avoid duplicated effort. The disadvantage of | |
7682 // resolving everything is that we might do extra work that we don't | |
7683 // really care about. Another possibility is to add a parameter to this | |
7684 // method and punt the decision to the clients. | |
7685 // | |
7686 //if (analyzeAll) { | |
7687 resolveReferencesAndTypes(); | |
7688 //} else { | |
7689 // resolveReferencesAndTypes(targetLibrary); | |
7690 //} | |
7691 _performConstantEvaluation(); | |
7692 return targetLibrary.libraryElement; | |
7693 } | |
7694 | |
7695 /** | |
7696 * Resolve the identifiers and perform type analysis in the libraries in the c
urrent cycle. | |
7697 * | |
7698 * @throws AnalysisException if any of the identifiers could not be resolved o
r if any of the | |
7699 * libraries could not have their types analyzed | |
7700 */ | |
7701 void resolveReferencesAndTypes() { | |
7702 for (Library library in _librariesInCycles) { | |
7703 _resolveReferencesAndTypesInLibrary(library); | |
7704 } | |
7705 } | |
7706 | |
7707 /** | |
7708 * Add a dependency to the given map from the referencing library to the refer
enced library. | |
7709 * | |
7710 * @param dependencyMap the map to which the dependency is to be added | |
7711 * @param referencingLibrary the library that references the referenced librar
y | |
7712 * @param referencedLibrary the library referenced by the referencing library | |
7713 */ | |
7714 void _addDependencyToMap(HashMap<Library, List<Library>> dependencyMap, | |
7715 Library referencingLibrary, Library referencedLibrary) { | |
7716 List<Library> dependentLibraries = dependencyMap[referencedLibrary]; | |
7717 if (dependentLibraries == null) { | |
7718 dependentLibraries = new List<Library>(); | |
7719 dependencyMap[referencedLibrary] = dependentLibraries; | |
7720 } | |
7721 dependentLibraries.add(referencingLibrary); | |
7722 } | |
7723 | |
7724 /** | |
7725 * Given a library that is part of a cycle that includes the root library, add
to the given set of | |
7726 * libraries all of the libraries reachable from the root library that are als
o included in the | |
7727 * cycle. | |
7728 * | |
7729 * @param library the library to be added to the collection of libraries in cy
cles | |
7730 * @param librariesInCycle a collection of the libraries that are in the cycle | |
7731 * @param dependencyMap a table mapping libraries to the collection of librari
es from which those | |
7732 * libraries are referenced | |
7733 */ | |
7734 void _addLibrariesInCycle(Library library, Set<Library> librariesInCycle, | |
7735 HashMap<Library, List<Library>> dependencyMap) { | |
7736 if (librariesInCycle.add(library)) { | |
7737 List<Library> dependentLibraries = dependencyMap[library]; | |
7738 if (dependentLibraries != null) { | |
7739 for (Library dependentLibrary in dependentLibraries) { | |
7740 _addLibrariesInCycle( | |
7741 dependentLibrary, librariesInCycle, dependencyMap); | |
7742 } | |
7743 } | |
7744 } | |
7745 } | |
7746 | |
7747 /** | |
7748 * Add the given library, and all libraries reachable from it that have not al
ready been visited, | |
7749 * to the given dependency map. | |
7750 * | |
7751 * @param library the library currently being added to the dependency map | |
7752 * @param dependencyMap the dependency map being computed | |
7753 * @param visitedLibraries the libraries that have already been visited, used
to prevent infinite | |
7754 * recursion | |
7755 */ | |
7756 void _addToDependencyMap(Library library, | |
7757 HashMap<Library, List<Library>> dependencyMap, | |
7758 Set<Library> visitedLibraries) { | |
7759 if (visitedLibraries.add(library)) { | |
7760 bool asyncFound = false; | |
7761 for (Library referencedLibrary in library.importsAndExports) { | |
7762 _addDependencyToMap(dependencyMap, library, referencedLibrary); | |
7763 _addToDependencyMap(referencedLibrary, dependencyMap, visitedLibraries); | |
7764 if (identical(referencedLibrary, _asyncLibrary)) { | |
7765 asyncFound = true; | |
7766 } | |
7767 } | |
7768 if (!library.explicitlyImportsCore && !identical(library, _coreLibrary)) { | |
7769 _addDependencyToMap(dependencyMap, library, _coreLibrary); | |
7770 } | |
7771 if (!asyncFound && !identical(library, _asyncLibrary)) { | |
7772 _addDependencyToMap(dependencyMap, library, _asyncLibrary); | |
7773 _addToDependencyMap(_asyncLibrary, dependencyMap, visitedLibraries); | |
7774 } | |
7775 } | |
7776 } | |
7777 | |
7778 /** | |
7779 * Build the element model representing the combinators declared by the given
directive. | |
7780 * | |
7781 * @param directive the directive that declares the combinators | |
7782 * @return an array containing the import combinators that were built | |
7783 */ | |
7784 List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) { | |
7785 List<NamespaceCombinator> combinators = new List<NamespaceCombinator>(); | |
7786 for (Combinator combinator in directive.combinators) { | |
7787 if (combinator is HideCombinator) { | |
7788 HideElementCombinatorImpl hide = new HideElementCombinatorImpl(); | |
7789 hide.hiddenNames = _getIdentifiers(combinator.hiddenNames); | |
7790 combinators.add(hide); | |
7791 } else { | |
7792 ShowElementCombinatorImpl show = new ShowElementCombinatorImpl(); | |
7793 show.offset = combinator.offset; | |
7794 show.end = combinator.end; | |
7795 show.shownNames = | |
7796 _getIdentifiers((combinator as ShowCombinator).shownNames); | |
7797 combinators.add(show); | |
7798 } | |
7799 } | |
7800 return combinators; | |
7801 } | |
7802 | |
7803 /** | |
7804 * Every library now has a corresponding [LibraryElement], so it is now possib
le to resolve | |
7805 * the import and export directives. | |
7806 * | |
7807 * @throws AnalysisException if the defining compilation unit for any of the l
ibraries could not | |
7808 * be accessed | |
7809 */ | |
7810 void _buildDirectiveModels() { | |
7811 for (Library library in _librariesInCycles) { | |
7812 HashMap<String, PrefixElementImpl> nameToPrefixMap = | |
7813 new HashMap<String, PrefixElementImpl>(); | |
7814 List<ImportElement> imports = new List<ImportElement>(); | |
7815 List<ExportElement> exports = new List<ExportElement>(); | |
7816 for (Directive directive in library.definingCompilationUnit.directives) { | |
7817 if (directive is ImportDirective) { | |
7818 ImportDirective importDirective = directive; | |
7819 String uriContent = importDirective.uriContent; | |
7820 if (DartUriResolver.isDartExtUri(uriContent)) { | |
7821 library.libraryElement.hasExtUri = true; | |
7822 } | |
7823 Source importedSource = importDirective.source; | |
7824 if (importedSource != null) { | |
7825 // The imported source will be null if the URI in the import | |
7826 // directive was invalid. | |
7827 Library importedLibrary = _libraryMap[importedSource]; | |
7828 if (importedLibrary != null) { | |
7829 ImportElementImpl importElement = | |
7830 new ImportElementImpl(directive.offset); | |
7831 StringLiteral uriLiteral = importDirective.uri; | |
7832 importElement.uriOffset = uriLiteral.offset; | |
7833 importElement.uriEnd = uriLiteral.end; | |
7834 importElement.uri = uriContent; | |
7835 importElement.deferred = importDirective.deferredKeyword != null; | |
7836 importElement.combinators = _buildCombinators(importDirective); | |
7837 LibraryElement importedLibraryElement = | |
7838 importedLibrary.libraryElement; | |
7839 if (importedLibraryElement != null) { | |
7840 importElement.importedLibrary = importedLibraryElement; | |
7841 } | |
7842 SimpleIdentifier prefixNode = directive.prefix; | |
7843 if (prefixNode != null) { | |
7844 importElement.prefixOffset = prefixNode.offset; | |
7845 String prefixName = prefixNode.name; | |
7846 PrefixElementImpl prefix = nameToPrefixMap[prefixName]; | |
7847 if (prefix == null) { | |
7848 prefix = new PrefixElementImpl.forNode(prefixNode); | |
7849 nameToPrefixMap[prefixName] = prefix; | |
7850 } | |
7851 importElement.prefix = prefix; | |
7852 prefixNode.staticElement = prefix; | |
7853 } | |
7854 directive.element = importElement; | |
7855 imports.add(importElement); | |
7856 if (analysisContext.computeKindOf(importedSource) != | |
7857 SourceKind.LIBRARY) { | |
7858 ErrorCode errorCode = (importElement.isDeferred | |
7859 ? StaticWarningCode.IMPORT_OF_NON_LIBRARY | |
7860 : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY); | |
7861 _errorListener.onError(new AnalysisError(library.librarySource, | |
7862 uriLiteral.offset, uriLiteral.length, errorCode, | |
7863 [uriLiteral.toSource()])); | |
7864 } | |
7865 } | |
7866 } | |
7867 } else if (directive is ExportDirective) { | |
7868 ExportDirective exportDirective = directive; | |
7869 Source exportedSource = exportDirective.source; | |
7870 if (exportedSource != null) { | |
7871 // The exported source will be null if the URI in the export | |
7872 // directive was invalid. | |
7873 Library exportedLibrary = _libraryMap[exportedSource]; | |
7874 if (exportedLibrary != null) { | |
7875 ExportElementImpl exportElement = | |
7876 new ExportElementImpl(directive.offset); | |
7877 StringLiteral uriLiteral = exportDirective.uri; | |
7878 exportElement.uriOffset = uriLiteral.offset; | |
7879 exportElement.uriEnd = uriLiteral.end; | |
7880 exportElement.uri = exportDirective.uriContent; | |
7881 exportElement.combinators = _buildCombinators(exportDirective); | |
7882 LibraryElement exportedLibraryElement = | |
7883 exportedLibrary.libraryElement; | |
7884 if (exportedLibraryElement != null) { | |
7885 exportElement.exportedLibrary = exportedLibraryElement; | |
7886 } | |
7887 directive.element = exportElement; | |
7888 exports.add(exportElement); | |
7889 if (analysisContext.computeKindOf(exportedSource) != | |
7890 SourceKind.LIBRARY) { | |
7891 _errorListener.onError(new AnalysisError(library.librarySource, | |
7892 uriLiteral.offset, uriLiteral.length, | |
7893 CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY, | |
7894 [uriLiteral.toSource()])); | |
7895 } | |
7896 } | |
7897 } | |
7898 } | |
7899 } | |
7900 Source librarySource = library.librarySource; | |
7901 if (!library.explicitlyImportsCore && | |
7902 _coreLibrarySource != librarySource) { | |
7903 ImportElementImpl importElement = new ImportElementImpl(-1); | |
7904 importElement.importedLibrary = _coreLibrary.libraryElement; | |
7905 importElement.synthetic = true; | |
7906 imports.add(importElement); | |
7907 } | |
7908 LibraryElementImpl libraryElement = library.libraryElement; | |
7909 libraryElement.imports = imports; | |
7910 libraryElement.exports = exports; | |
7911 if (libraryElement.entryPoint == null) { | |
7912 Namespace namespace = new NamespaceBuilder() | |
7913 .createExportNamespaceForLibrary(libraryElement); | |
7914 Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME); | |
7915 if (element is FunctionElement) { | |
7916 libraryElement.entryPoint = element; | |
7917 } | |
7918 } | |
7919 } | |
7920 } | |
7921 | |
7922 /** | |
7923 * Build element models for all of the libraries in the current cycle. | |
7924 * | |
7925 * @throws AnalysisException if any of the element models cannot be built | |
7926 */ | |
7927 void _buildElementModels() { | |
7928 for (Library library in _librariesInCycles) { | |
7929 LibraryElementBuilder builder = | |
7930 new LibraryElementBuilder(analysisContext, errorListener); | |
7931 LibraryElementImpl libraryElement = builder.buildLibrary(library); | |
7932 library.libraryElement = libraryElement; | |
7933 } | |
7934 } | |
7935 | |
7936 /** | |
7937 * Build the members in enum declarations. This cannot be done while building
the rest of the | |
7938 * element model because it depends on being able to access core types, which
cannot happen until | |
7939 * the rest of the element model has been built (when resolving the core libra
ry). | |
7940 * | |
7941 * @throws AnalysisException if any of the enum members could not be built | |
7942 */ | |
7943 void _buildEnumMembers() { | |
7944 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
7945 for (Library library in _librariesInCycles) { | |
7946 for (Source source in library.compilationUnitSources) { | |
7947 EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider); | |
7948 library.getAST(source).accept(builder); | |
7949 } | |
7950 } | |
7951 }); | |
7952 } | |
7953 | |
7954 /** | |
7955 * Resolve the type hierarchy across all of the types declared in the librarie
s in the current | |
7956 * cycle. | |
7957 * | |
7958 * @throws AnalysisException if any of the type hierarchies could not be resol
ved | |
7959 */ | |
7960 void _buildTypeHierarchies() { | |
7961 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
7962 for (Library library in _librariesInCycles) { | |
7963 for (Source source in library.compilationUnitSources) { | |
7964 TypeResolverVisitorFactory typeResolverVisitorFactory = | |
7965 analysisContext.typeResolverVisitorFactory; | |
7966 TypeResolverVisitor visitor = (typeResolverVisitorFactory == null) | |
7967 ? new TypeResolverVisitor(library.libraryElement, source, | |
7968 _typeProvider, library.errorListener, | |
7969 nameScope: library.libraryScope) | |
7970 : typeResolverVisitorFactory(library, source, _typeProvider); | |
7971 library.getAST(source).accept(visitor); | |
7972 } | |
7973 } | |
7974 }); | |
7975 } | |
7976 | |
7977 /** | |
7978 * Compute a dependency map of libraries reachable from the given library. A d
ependency map is a | |
7979 * table that maps individual libraries to a list of the libraries that either
import or export | |
7980 * those libraries. | |
7981 * | |
7982 * This map is used to compute all of the libraries involved in a cycle that i
nclude the root | |
7983 * library. Given that we only add libraries that are reachable from the root
library, when we | |
7984 * work backward we are guaranteed to only get libraries in the cycle. | |
7985 * | |
7986 * @param library the library currently being added to the dependency map | |
7987 */ | |
7988 HashMap<Library, List<Library>> _computeDependencyMap(Library library) { | |
7989 HashMap<Library, List<Library>> dependencyMap = | |
7990 new HashMap<Library, List<Library>>(); | |
7991 _addToDependencyMap(library, dependencyMap, new HashSet<Library>()); | |
7992 return dependencyMap; | |
7993 } | |
7994 | |
7995 /** | |
7996 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the | |
7997 * class [Library] to represent them, and record the references in the library
objects. | |
7998 * | |
7999 * @param library the library to be processed to find libraries that have not
yet been traversed | |
8000 * @throws AnalysisException if some portion of the library graph could not be
traversed | |
8001 */ | |
8002 void _computeEmbeddedLibraryDependencies( | |
8003 Library library, CompilationUnit unit) { | |
8004 Source librarySource = library.librarySource; | |
8005 HashSet<Source> exportedSources = new HashSet<Source>(); | |
8006 HashSet<Source> importedSources = new HashSet<Source>(); | |
8007 for (Directive directive in unit.directives) { | |
8008 if (directive is ExportDirective) { | |
8009 Source exportSource = _resolveSource(librarySource, directive); | |
8010 if (exportSource != null) { | |
8011 exportedSources.add(exportSource); | |
8012 } | |
8013 } else if (directive is ImportDirective) { | |
8014 Source importSource = _resolveSource(librarySource, directive); | |
8015 if (importSource != null) { | |
8016 importedSources.add(importSource); | |
8017 } | |
8018 } | |
8019 } | |
8020 _computeLibraryDependenciesFromDirectives(library, | |
8021 new List.from(importedSources), new List.from(exportedSources)); | |
8022 } | |
8023 | |
8024 /** | |
8025 * Return a collection containing all of the libraries reachable from the give
n library that are | |
8026 * contained in a cycle that includes the given library. | |
8027 * | |
8028 * @param library the library that must be included in any cycles whose member
s are to be returned | |
8029 * @return all of the libraries referenced by the given library that have a ci
rcular reference | |
8030 * back to the given library | |
8031 */ | |
8032 Set<Library> _computeLibrariesInCycles(Library library) { | |
8033 HashMap<Library, List<Library>> dependencyMap = | |
8034 _computeDependencyMap(library); | |
8035 Set<Library> librariesInCycle = new HashSet<Library>(); | |
8036 _addLibrariesInCycle(library, librariesInCycle, dependencyMap); | |
8037 return librariesInCycle; | |
8038 } | |
8039 | |
8040 /** | |
8041 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the | |
8042 * class [Library] to represent them, and record the references in the library
objects. | |
8043 * | |
8044 * @param library the library to be processed to find libraries that have not
yet been traversed | |
8045 * @throws AnalysisException if some portion of the library graph could not be
traversed | |
8046 */ | |
8047 void _computeLibraryDependencies(Library library) { | |
8048 Source librarySource = library.librarySource; | |
8049 _computeLibraryDependenciesFromDirectives(library, | |
8050 analysisContext.computeImportedLibraries(librarySource), | |
8051 analysisContext.computeExportedLibraries(librarySource)); | |
8052 } | |
8053 | |
8054 /** | |
8055 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the | |
8056 * class [Library] to represent them, and record the references in the library
objects. | |
8057 * | |
8058 * @param library the library to be processed to find libraries that have not
yet been traversed | |
8059 * @param importedSources an array containing the sources that are imported in
to the given library | |
8060 * @param exportedSources an array containing the sources that are exported fr
om the given library | |
8061 * @throws AnalysisException if some portion of the library graph could not be
traversed | |
8062 */ | |
8063 void _computeLibraryDependenciesFromDirectives(Library library, | |
8064 List<Source> importedSources, List<Source> exportedSources) { | |
8065 List<Library> importedLibraries = new List<Library>(); | |
8066 bool explicitlyImportsCore = false; | |
8067 bool importsAsync = false; | |
8068 for (Source importedSource in importedSources) { | |
8069 if (importedSource == _coreLibrarySource) { | |
8070 explicitlyImportsCore = true; | |
8071 } | |
8072 if (importedSource == _asyncLibrarySource) { | |
8073 importsAsync = true; | |
8074 } | |
8075 Library importedLibrary = _libraryMap[importedSource]; | |
8076 if (importedLibrary == null) { | |
8077 importedLibrary = _createLibraryOrNull(importedSource); | |
8078 if (importedLibrary != null) { | |
8079 _computeLibraryDependencies(importedLibrary); | |
8080 } | |
8081 } | |
8082 if (importedLibrary != null) { | |
8083 importedLibraries.add(importedLibrary); | |
8084 } | |
8085 } | |
8086 library.importedLibraries = importedLibraries; | |
8087 List<Library> exportedLibraries = new List<Library>(); | |
8088 for (Source exportedSource in exportedSources) { | |
8089 Library exportedLibrary = _libraryMap[exportedSource]; | |
8090 if (exportedLibrary == null) { | |
8091 exportedLibrary = _createLibraryOrNull(exportedSource); | |
8092 if (exportedLibrary != null) { | |
8093 _computeLibraryDependencies(exportedLibrary); | |
8094 } | |
8095 } | |
8096 if (exportedLibrary != null) { | |
8097 exportedLibraries.add(exportedLibrary); | |
8098 } | |
8099 } | |
8100 library.exportedLibraries = exportedLibraries; | |
8101 library.explicitlyImportsCore = explicitlyImportsCore; | |
8102 if (!explicitlyImportsCore && _coreLibrarySource != library.librarySource) { | |
8103 Library importedLibrary = _libraryMap[_coreLibrarySource]; | |
8104 if (importedLibrary == null) { | |
8105 importedLibrary = _createLibraryOrNull(_coreLibrarySource); | |
8106 if (importedLibrary != null) { | |
8107 _computeLibraryDependencies(importedLibrary); | |
8108 } | |
8109 } | |
8110 } | |
8111 if (!importsAsync && _asyncLibrarySource != library.librarySource) { | |
8112 Library importedLibrary = _libraryMap[_asyncLibrarySource]; | |
8113 if (importedLibrary == null) { | |
8114 importedLibrary = _createLibraryOrNull(_asyncLibrarySource); | |
8115 if (importedLibrary != null) { | |
8116 _computeLibraryDependencies(importedLibrary); | |
8117 } | |
8118 } | |
8119 } | |
8120 } | |
8121 | |
8122 /** | |
8123 * Create an object to represent the information about the library defined by
the compilation unit | |
8124 * with the given source. Return the library object that was created, or `null
` if the | |
8125 * source is not valid. | |
8126 * | |
8127 * @param librarySource the source of the library's defining compilation unit | |
8128 * @return the library object that was created | |
8129 */ | |
8130 Library _createLibraryOrNull(Source librarySource) { | |
8131 if (!analysisContext.exists(librarySource)) { | |
8132 return null; | |
8133 } | |
8134 Library library = | |
8135 new Library(analysisContext, _errorListener, librarySource); | |
8136 _libraryMap[librarySource] = library; | |
8137 return library; | |
8138 } | |
8139 | |
8140 /** | |
8141 * Create an object to represent the information about the library defined by
the compilation unit | |
8142 * with the given source. | |
8143 * | |
8144 * @param librarySource the source of the library's defining compilation unit | |
8145 * @param unit the compilation unit that defines the library | |
8146 * @return the library object that was created | |
8147 * @throws AnalysisException if the library source is not valid | |
8148 */ | |
8149 Library _createLibraryWithUnit(Source librarySource, CompilationUnit unit) { | |
8150 Library library = | |
8151 new Library(analysisContext, _errorListener, librarySource); | |
8152 library.setDefiningCompilationUnit(unit); | |
8153 _libraryMap[librarySource] = library; | |
8154 return library; | |
8155 } | |
8156 | |
8157 /** | |
8158 * Return an array containing the lexical identifiers associated with the node
s in the given list. | |
8159 * | |
8160 * @param names the AST nodes representing the identifiers | |
8161 * @return the lexical identifiers associated with the nodes in the list | |
8162 */ | |
8163 List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) { | |
8164 int count = names.length; | |
8165 List<String> identifiers = new List<String>(count); | |
8166 for (int i = 0; i < count; i++) { | |
8167 identifiers[i] = names[i].name; | |
8168 } | |
8169 return identifiers; | |
8170 } | |
8171 | |
8172 /** | |
8173 * Compute a value for all of the constants in the libraries being analyzed. | |
8174 */ | |
8175 void _performConstantEvaluation() { | |
8176 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
8177 ConstantValueComputer computer = new ConstantValueComputer( | |
8178 analysisContext, _typeProvider, analysisContext.declaredVariables); | |
8179 for (Library library in _librariesInCycles) { | |
8180 for (Source source in library.compilationUnitSources) { | |
8181 try { | |
8182 CompilationUnit unit = library.getAST(source); | |
8183 if (unit != null) { | |
8184 computer.add(unit, source, library.librarySource); | |
8185 } | |
8186 } on AnalysisException catch (exception, stackTrace) { | |
8187 AnalysisEngine.instance.logger.logError( | |
8188 "Internal Error: Could not access AST for ${source.fullName} dur
ing constant evaluation", | |
8189 new CaughtException(exception, stackTrace)); | |
8190 } | |
8191 } | |
8192 } | |
8193 computer.computeValues(); | |
8194 // As a temporary workaround for issue 21572, run ConstantVerifier now. | |
8195 // TODO(paulberry): remove this workaround once issue 21572 is fixed. | |
8196 for (Library library in _librariesInCycles) { | |
8197 for (Source source in library.compilationUnitSources) { | |
8198 try { | |
8199 CompilationUnit unit = library.getAST(source); | |
8200 ErrorReporter errorReporter = | |
8201 new ErrorReporter(_errorListener, source); | |
8202 ConstantVerifier constantVerifier = new ConstantVerifier( | |
8203 errorReporter, library.libraryElement, _typeProvider, | |
8204 analysisContext.declaredVariables); | |
8205 unit.accept(constantVerifier); | |
8206 } on AnalysisException catch (exception, stackTrace) { | |
8207 AnalysisEngine.instance.logger.logError( | |
8208 "Internal Error: Could not access AST for ${source.fullName} " | |
8209 "during constant verification", | |
8210 new CaughtException(exception, stackTrace)); | |
8211 } | |
8212 } | |
8213 } | |
8214 }); | |
8215 } | |
8216 | |
8217 /** | |
8218 * Resolve the identifiers and perform type analysis in the given library. | |
8219 * | |
8220 * @param library the library to be resolved | |
8221 * @throws AnalysisException if any of the identifiers could not be resolved o
r if the types in | |
8222 * the library cannot be analyzed | |
8223 */ | |
8224 void _resolveReferencesAndTypesInLibrary(Library library) { | |
8225 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
8226 for (Source source in library.compilationUnitSources) { | |
8227 CompilationUnit ast = library.getAST(source); | |
8228 ast.accept(new VariableResolverVisitor(library.libraryElement, source, | |
8229 _typeProvider, library.errorListener, | |
8230 nameScope: library.libraryScope)); | |
8231 ResolverVisitorFactory visitorFactory = | |
8232 analysisContext.resolverVisitorFactory; | |
8233 ResolverVisitor visitor = visitorFactory != null | |
8234 ? visitorFactory(library, source, _typeProvider) | |
8235 : new ResolverVisitor(library.libraryElement, source, _typeProvider, | |
8236 library.errorListener, | |
8237 nameScope: library.libraryScope, | |
8238 inheritanceManager: library.inheritanceManager); | |
8239 ast.accept(visitor); | |
8240 } | |
8241 }); | |
8242 } | |
8243 | |
8244 /** | |
8245 * Return the result of resolving the URI of the given URI-based directive aga
inst the URI of the | |
8246 * given library, or `null` if the URI is not valid. | |
8247 * | |
8248 * @param librarySource the source representing the library containing the dir
ective | |
8249 * @param directive the directive which URI should be resolved | |
8250 * @return the result of resolving the URI against the URI of the library | |
8251 */ | |
8252 Source _resolveSource(Source librarySource, UriBasedDirective directive) { | |
8253 StringLiteral uriLiteral = directive.uri; | |
8254 if (uriLiteral is StringInterpolation) { | |
8255 return null; | |
8256 } | |
8257 String uriContent = uriLiteral.stringValue.trim(); | |
8258 if (uriContent == null || uriContent.isEmpty) { | |
8259 return null; | |
8260 } | |
8261 uriContent = Uri.encodeFull(uriContent); | |
8262 return analysisContext.sourceFactory.resolveUri(librarySource, uriContent); | |
8263 } | |
8264 } | |
8265 | |
8266 /** | |
8267 * Instances of the class `LibraryResolver` are used to resolve one or more mutu
ally dependent | |
8268 * libraries within a single context. | |
8269 */ | |
8270 class LibraryResolver2 { | |
8271 /** | |
8272 * The analysis context in which the libraries are being analyzed. | |
8273 */ | |
8274 final InternalAnalysisContext analysisContext; | |
8275 | |
8276 /** | |
8277 * The listener to which analysis errors will be reported, this error listener
is either | |
8278 * references [recordingErrorListener], or it unions the passed | |
8279 * [AnalysisErrorListener] with the [recordingErrorListener]. | |
8280 */ | |
8281 RecordingErrorListener _errorListener; | |
8282 | |
8283 /** | |
8284 * A source object representing the core library (dart:core). | |
8285 */ | |
8286 Source _coreLibrarySource; | |
8287 | |
8288 /** | |
8289 * A source object representing the async library (dart:async). | |
8290 */ | |
8291 Source _asyncLibrarySource; | |
8292 | |
8293 /** | |
8294 * The object representing the core library. | |
8295 */ | |
8296 ResolvableLibrary _coreLibrary; | |
8297 | |
8298 /** | |
8299 * The object representing the async library. | |
8300 */ | |
8301 ResolvableLibrary _asyncLibrary; | |
8302 | |
8303 /** | |
8304 * The object used to access the types from the core library. | |
8305 */ | |
8306 TypeProvider _typeProvider; | |
8307 | |
8308 /** | |
8309 * A table mapping library sources to the information being maintained for tho
se libraries. | |
8310 */ | |
8311 HashMap<Source, ResolvableLibrary> _libraryMap = | |
8312 new HashMap<Source, ResolvableLibrary>(); | |
8313 | |
8314 /** | |
8315 * A collection containing the libraries that are being resolved together. | |
8316 */ | |
8317 List<ResolvableLibrary> _librariesInCycle; | |
8318 | |
8319 /** | |
8320 * Initialize a newly created library resolver to resolve libraries within the
given context. | |
8321 * | |
8322 * @param analysisContext the analysis context in which the library is being a
nalyzed | |
8323 */ | |
8324 LibraryResolver2(this.analysisContext) { | |
8325 this._errorListener = new RecordingErrorListener(); | |
8326 _coreLibrarySource = | |
8327 analysisContext.sourceFactory.forUri(DartSdk.DART_CORE); | |
8328 _asyncLibrarySource = | |
8329 analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC); | |
8330 } | |
8331 | |
8332 /** | |
8333 * Return the listener to which analysis errors will be reported. | |
8334 * | |
8335 * @return the listener to which analysis errors will be reported | |
8336 */ | |
8337 RecordingErrorListener get errorListener => _errorListener; | |
8338 | |
8339 /** | |
8340 * Return an array containing information about all of the libraries that were
resolved. | |
8341 * | |
8342 * @return an array containing the libraries that were resolved | |
8343 */ | |
8344 List<ResolvableLibrary> get resolvedLibraries => _librariesInCycle; | |
8345 | |
8346 /** | |
8347 * Resolve the library specified by the given source in the given context. | |
8348 * | |
8349 * Note that because Dart allows circular imports between libraries, it is pos
sible that more than | |
8350 * one library will need to be resolved. In such cases the error listener can
receive errors from | |
8351 * multiple libraries. | |
8352 * | |
8353 * @param librarySource the source specifying the defining compilation unit of
the library to be | |
8354 * resolved | |
8355 * @param fullAnalysis `true` if a full analysis should be performed | |
8356 * @return the element representing the resolved library | |
8357 * @throws AnalysisException if the library could not be resolved for some rea
son | |
8358 */ | |
8359 LibraryElement resolveLibrary( | |
8360 Source librarySource, List<ResolvableLibrary> librariesInCycle) { | |
8361 // | |
8362 // Build the map of libraries that are known. | |
8363 // | |
8364 this._librariesInCycle = librariesInCycle; | |
8365 _libraryMap = _buildLibraryMap(); | |
8366 ResolvableLibrary targetLibrary = _libraryMap[librarySource]; | |
8367 _coreLibrary = _libraryMap[_coreLibrarySource]; | |
8368 _asyncLibrary = _libraryMap[_asyncLibrarySource]; | |
8369 // | |
8370 // Build the element models representing the libraries being resolved. | |
8371 // This is done in three steps: | |
8372 // | |
8373 // 1. Build the basic element models without making any connections | |
8374 // between elements other than the basic parent/child relationships. | |
8375 // This includes building the elements representing the libraries, but | |
8376 // excludes members defined in enums. | |
8377 // 2. Build the elements for the import and export directives. This | |
8378 // requires that we have the elements built for the referenced | |
8379 // libraries, but because of the possibility of circular references | |
8380 // needs to happen after all of the library elements have been created. | |
8381 // 3. Build the members in enum declarations. | |
8382 // 4. Build the rest of the type model by connecting superclasses, mixins, | |
8383 // and interfaces. This requires that we be able to compute the names | |
8384 // visible in the libraries being resolved, which in turn requires that | |
8385 // we have resolved the import directives. | |
8386 // | |
8387 _buildElementModels(); | |
8388 LibraryElement coreElement = _coreLibrary.libraryElement; | |
8389 if (coreElement == null) { | |
8390 missingCoreLibrary(analysisContext, _coreLibrarySource); | |
8391 } | |
8392 LibraryElement asyncElement = _asyncLibrary.libraryElement; | |
8393 if (asyncElement == null) { | |
8394 missingAsyncLibrary(analysisContext, _asyncLibrarySource); | |
8395 } | |
8396 _buildDirectiveModels(); | |
8397 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); | |
8398 _buildEnumMembers(); | |
8399 _buildTypeHierarchies(); | |
8400 // | |
8401 // Perform resolution and type analysis. | |
8402 // | |
8403 // TODO(brianwilkerson) Decide whether we want to resolve all of the | |
8404 // libraries or whether we want to only resolve the target library. The | |
8405 // advantage to resolving everything is that we have already done part of | |
8406 // the work so we'll avoid duplicated effort. The disadvantage of | |
8407 // resolving everything is that we might do extra work that we don't | |
8408 // really care about. Another possibility is to add a parameter to this | |
8409 // method and punt the decision to the clients. | |
8410 // | |
8411 //if (analyzeAll) { | |
8412 _resolveReferencesAndTypes(); | |
8413 //} else { | |
8414 // resolveReferencesAndTypes(targetLibrary); | |
8415 //} | |
8416 _performConstantEvaluation(); | |
8417 return targetLibrary.libraryElement; | |
8418 } | |
8419 | |
8420 /** | |
8421 * Build the element model representing the combinators declared by the given
directive. | |
8422 * | |
8423 * @param directive the directive that declares the combinators | |
8424 * @return an array containing the import combinators that were built | |
8425 */ | |
8426 List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) { | |
8427 List<NamespaceCombinator> combinators = new List<NamespaceCombinator>(); | |
8428 for (Combinator combinator in directive.combinators) { | |
8429 if (combinator is HideCombinator) { | |
8430 HideElementCombinatorImpl hide = new HideElementCombinatorImpl(); | |
8431 hide.hiddenNames = _getIdentifiers(combinator.hiddenNames); | |
8432 combinators.add(hide); | |
8433 } else { | |
8434 ShowElementCombinatorImpl show = new ShowElementCombinatorImpl(); | |
8435 show.offset = combinator.offset; | |
8436 show.end = combinator.end; | |
8437 show.shownNames = | |
8438 _getIdentifiers((combinator as ShowCombinator).shownNames); | |
8439 combinators.add(show); | |
8440 } | |
8441 } | |
8442 return combinators; | |
8443 } | |
8444 | |
8445 /** | |
8446 * Every library now has a corresponding [LibraryElement], so it is now possib
le to resolve | |
8447 * the import and export directives. | |
8448 * | |
8449 * @throws AnalysisException if the defining compilation unit for any of the l
ibraries could not | |
8450 * be accessed | |
8451 */ | |
8452 void _buildDirectiveModels() { | |
8453 for (ResolvableLibrary library in _librariesInCycle) { | |
8454 HashMap<String, PrefixElementImpl> nameToPrefixMap = | |
8455 new HashMap<String, PrefixElementImpl>(); | |
8456 List<ImportElement> imports = new List<ImportElement>(); | |
8457 List<ExportElement> exports = new List<ExportElement>(); | |
8458 for (Directive directive in library.definingCompilationUnit.directives) { | |
8459 if (directive is ImportDirective) { | |
8460 ImportDirective importDirective = directive; | |
8461 String uriContent = importDirective.uriContent; | |
8462 if (DartUriResolver.isDartExtUri(uriContent)) { | |
8463 library.libraryElement.hasExtUri = true; | |
8464 } | |
8465 Source importedSource = importDirective.source; | |
8466 if (importedSource != null && | |
8467 analysisContext.exists(importedSource)) { | |
8468 // The imported source will be null if the URI in the import | |
8469 // directive was invalid. | |
8470 ResolvableLibrary importedLibrary = _libraryMap[importedSource]; | |
8471 if (importedLibrary != null) { | |
8472 ImportElementImpl importElement = | |
8473 new ImportElementImpl(directive.offset); | |
8474 StringLiteral uriLiteral = importDirective.uri; | |
8475 if (uriLiteral != null) { | |
8476 importElement.uriOffset = uriLiteral.offset; | |
8477 importElement.uriEnd = uriLiteral.end; | |
8478 } | |
8479 importElement.uri = uriContent; | |
8480 importElement.deferred = importDirective.deferredKeyword != null; | |
8481 importElement.combinators = _buildCombinators(importDirective); | |
8482 LibraryElement importedLibraryElement = | |
8483 importedLibrary.libraryElement; | |
8484 if (importedLibraryElement != null) { | |
8485 importElement.importedLibrary = importedLibraryElement; | |
8486 } | |
8487 SimpleIdentifier prefixNode = directive.prefix; | |
8488 if (prefixNode != null) { | |
8489 importElement.prefixOffset = prefixNode.offset; | |
8490 String prefixName = prefixNode.name; | |
8491 PrefixElementImpl prefix = nameToPrefixMap[prefixName]; | |
8492 if (prefix == null) { | |
8493 prefix = new PrefixElementImpl.forNode(prefixNode); | |
8494 nameToPrefixMap[prefixName] = prefix; | |
8495 } | |
8496 importElement.prefix = prefix; | |
8497 prefixNode.staticElement = prefix; | |
8498 } | |
8499 directive.element = importElement; | |
8500 imports.add(importElement); | |
8501 if (analysisContext.computeKindOf(importedSource) != | |
8502 SourceKind.LIBRARY) { | |
8503 ErrorCode errorCode = (importElement.isDeferred | |
8504 ? StaticWarningCode.IMPORT_OF_NON_LIBRARY | |
8505 : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY); | |
8506 _errorListener.onError(new AnalysisError(library.librarySource, | |
8507 uriLiteral.offset, uriLiteral.length, errorCode, | |
8508 [uriLiteral.toSource()])); | |
8509 } | |
8510 } | |
8511 } | |
8512 } else if (directive is ExportDirective) { | |
8513 ExportDirective exportDirective = directive; | |
8514 Source exportedSource = exportDirective.source; | |
8515 if (exportedSource != null && | |
8516 analysisContext.exists(exportedSource)) { | |
8517 // The exported source will be null if the URI in the export | |
8518 // directive was invalid. | |
8519 ResolvableLibrary exportedLibrary = _libraryMap[exportedSource]; | |
8520 if (exportedLibrary != null) { | |
8521 ExportElementImpl exportElement = | |
8522 new ExportElementImpl(directive.offset); | |
8523 StringLiteral uriLiteral = exportDirective.uri; | |
8524 if (uriLiteral != null) { | |
8525 exportElement.uriOffset = uriLiteral.offset; | |
8526 exportElement.uriEnd = uriLiteral.end; | |
8527 } | |
8528 exportElement.uri = exportDirective.uriContent; | |
8529 exportElement.combinators = _buildCombinators(exportDirective); | |
8530 LibraryElement exportedLibraryElement = | |
8531 exportedLibrary.libraryElement; | |
8532 if (exportedLibraryElement != null) { | |
8533 exportElement.exportedLibrary = exportedLibraryElement; | |
8534 } | |
8535 directive.element = exportElement; | |
8536 exports.add(exportElement); | |
8537 if (analysisContext.computeKindOf(exportedSource) != | |
8538 SourceKind.LIBRARY) { | |
8539 _errorListener.onError(new AnalysisError(library.librarySource, | |
8540 uriLiteral.offset, uriLiteral.length, | |
8541 CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY, | |
8542 [uriLiteral.toSource()])); | |
8543 } | |
8544 } | |
8545 } | |
8546 } | |
8547 } | |
8548 Source librarySource = library.librarySource; | |
8549 if (!library.explicitlyImportsCore && | |
8550 _coreLibrarySource != librarySource) { | |
8551 ImportElementImpl importElement = new ImportElementImpl(-1); | |
8552 importElement.importedLibrary = _coreLibrary.libraryElement; | |
8553 importElement.synthetic = true; | |
8554 imports.add(importElement); | |
8555 } | |
8556 LibraryElementImpl libraryElement = library.libraryElement; | |
8557 libraryElement.imports = imports; | |
8558 libraryElement.exports = exports; | |
8559 if (libraryElement.entryPoint == null) { | |
8560 Namespace namespace = new NamespaceBuilder() | |
8561 .createExportNamespaceForLibrary(libraryElement); | |
8562 Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME); | |
8563 if (element is FunctionElement) { | |
8564 libraryElement.entryPoint = element; | |
8565 } | |
8566 } | |
8567 } | |
8568 } | |
8569 | |
8570 /** | |
8571 * Build element models for all of the libraries in the current cycle. | |
8572 * | |
8573 * @throws AnalysisException if any of the element models cannot be built | |
8574 */ | |
8575 void _buildElementModels() { | |
8576 for (ResolvableLibrary library in _librariesInCycle) { | |
8577 LibraryElementBuilder builder = | |
8578 new LibraryElementBuilder(analysisContext, errorListener); | |
8579 builder.buildLibrary2(library); | |
8580 } | |
8581 } | |
8582 | |
8583 /** | |
8584 * Build the members in enum declarations. This cannot be done while building
the rest of the | |
8585 * element model because it depends on being able to access core types, which
cannot happen until | |
8586 * the rest of the element model has been built (when resolving the core libra
ry). | |
8587 * | |
8588 * @throws AnalysisException if any of the enum members could not be built | |
8589 */ | |
8590 void _buildEnumMembers() { | |
8591 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
8592 for (ResolvableLibrary library in _librariesInCycle) { | |
8593 for (Source source in library.compilationUnitSources) { | |
8594 EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider); | |
8595 library.getAST(source).accept(builder); | |
8596 } | |
8597 } | |
8598 }); | |
8599 } | |
8600 | |
8601 HashMap<Source, ResolvableLibrary> _buildLibraryMap() { | |
8602 HashMap<Source, ResolvableLibrary> libraryMap = | |
8603 new HashMap<Source, ResolvableLibrary>(); | |
8604 int libraryCount = _librariesInCycle.length; | |
8605 for (int i = 0; i < libraryCount; i++) { | |
8606 ResolvableLibrary library = _librariesInCycle[i]; | |
8607 library.errorListener = _errorListener; | |
8608 libraryMap[library.librarySource] = library; | |
8609 List<ResolvableLibrary> dependencies = library.importsAndExports; | |
8610 int dependencyCount = dependencies.length; | |
8611 for (int j = 0; j < dependencyCount; j++) { | |
8612 ResolvableLibrary dependency = dependencies[j]; | |
8613 //dependency.setErrorListener(errorListener); | |
8614 libraryMap[dependency.librarySource] = dependency; | |
8615 } | |
8616 } | |
8617 return libraryMap; | |
8618 } | |
8619 | |
8620 /** | |
8621 * Resolve the type hierarchy across all of the types declared in the librarie
s in the current | |
8622 * cycle. | |
8623 * | |
8624 * @throws AnalysisException if any of the type hierarchies could not be resol
ved | |
8625 */ | |
8626 void _buildTypeHierarchies() { | |
8627 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
8628 for (ResolvableLibrary library in _librariesInCycle) { | |
8629 for (ResolvableCompilationUnit unit | |
8630 in library.resolvableCompilationUnits) { | |
8631 Source source = unit.source; | |
8632 CompilationUnit ast = unit.compilationUnit; | |
8633 TypeResolverVisitor visitor = new TypeResolverVisitor( | |
8634 library.libraryElement, source, _typeProvider, | |
8635 library.libraryScope.errorListener, | |
8636 nameScope: library.libraryScope); | |
8637 ast.accept(visitor); | |
8638 } | |
8639 } | |
8640 }); | |
8641 } | |
8642 | |
8643 /** | |
8644 * Return an array containing the lexical identifiers associated with the node
s in the given list. | |
8645 * | |
8646 * @param names the AST nodes representing the identifiers | |
8647 * @return the lexical identifiers associated with the nodes in the list | |
8648 */ | |
8649 List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) { | |
8650 int count = names.length; | |
8651 List<String> identifiers = new List<String>(count); | |
8652 for (int i = 0; i < count; i++) { | |
8653 identifiers[i] = names[i].name; | |
8654 } | |
8655 return identifiers; | |
8656 } | |
8657 | |
8658 /** | |
8659 * Compute a value for all of the constants in the libraries being analyzed. | |
8660 */ | |
8661 void _performConstantEvaluation() { | |
8662 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
8663 ConstantValueComputer computer = new ConstantValueComputer( | |
8664 analysisContext, _typeProvider, analysisContext.declaredVariables); | |
8665 for (ResolvableLibrary library in _librariesInCycle) { | |
8666 for (ResolvableCompilationUnit unit | |
8667 in library.resolvableCompilationUnits) { | |
8668 CompilationUnit ast = unit.compilationUnit; | |
8669 if (ast != null) { | |
8670 computer.add(ast, unit.source, library.librarySource); | |
8671 } | |
8672 } | |
8673 } | |
8674 computer.computeValues(); | |
8675 // As a temporary workaround for issue 21572, run ConstantVerifier now. | |
8676 // TODO(paulberry): remove this workaround once issue 21572 is fixed. | |
8677 for (ResolvableLibrary library in _librariesInCycle) { | |
8678 for (ResolvableCompilationUnit unit | |
8679 in library.resolvableCompilationUnits) { | |
8680 CompilationUnit ast = unit.compilationUnit; | |
8681 ErrorReporter errorReporter = | |
8682 new ErrorReporter(_errorListener, unit.source); | |
8683 ConstantVerifier constantVerifier = new ConstantVerifier( | |
8684 errorReporter, library.libraryElement, _typeProvider, | |
8685 analysisContext.declaredVariables); | |
8686 ast.accept(constantVerifier); | |
8687 } | |
8688 } | |
8689 }); | |
8690 } | |
8691 | |
8692 /** | |
8693 * Resolve the identifiers and perform type analysis in the libraries in the c
urrent cycle. | |
8694 * | |
8695 * @throws AnalysisException if any of the identifiers could not be resolved o
r if any of the | |
8696 * libraries could not have their types analyzed | |
8697 */ | |
8698 void _resolveReferencesAndTypes() { | |
8699 for (ResolvableLibrary library in _librariesInCycle) { | |
8700 _resolveReferencesAndTypesInLibrary(library); | |
8701 } | |
8702 } | |
8703 | |
8704 /** | |
8705 * Resolve the identifiers and perform type analysis in the given library. | |
8706 * | |
8707 * @param library the library to be resolved | |
8708 * @throws AnalysisException if any of the identifiers could not be resolved o
r if the types in | |
8709 * the library cannot be analyzed | |
8710 */ | |
8711 void _resolveReferencesAndTypesInLibrary(ResolvableLibrary library) { | |
8712 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
8713 for (ResolvableCompilationUnit unit | |
8714 in library.resolvableCompilationUnits) { | |
8715 Source source = unit.source; | |
8716 CompilationUnit ast = unit.compilationUnit; | |
8717 ast.accept(new VariableResolverVisitor(library.libraryElement, source, | |
8718 _typeProvider, library.libraryScope.errorListener, | |
8719 nameScope: library.libraryScope)); | |
8720 ResolverVisitor visitor = new ResolverVisitor(library.libraryElement, | |
8721 source, _typeProvider, library._libraryScope.errorListener, | |
8722 nameScope: library._libraryScope, | |
8723 inheritanceManager: library.inheritanceManager); | |
8724 ast.accept(visitor); | |
8725 } | |
8726 }); | |
8727 } | |
8728 | |
8729 /** | |
8730 * Report that the async library could not be resolved in the given | |
8731 * [analysisContext] and throw an exception. [asyncLibrarySource] is the sour
ce | |
8732 * representing the async library. | |
8733 */ | |
8734 static void missingAsyncLibrary( | |
8735 AnalysisContext analysisContext, Source asyncLibrarySource) { | |
8736 throw new AnalysisException("Could not resolve dart:async"); | |
8737 } | |
8738 | |
8739 /** | |
8740 * Report that the core library could not be resolved in the given analysis co
ntext and throw an | |
8741 * exception. | |
8742 * | |
8743 * @param analysisContext the analysis context in which the failure occurred | |
8744 * @param coreLibrarySource the source representing the core library | |
8745 * @throws AnalysisException always | |
8746 */ | |
8747 static void missingCoreLibrary( | |
8748 AnalysisContext analysisContext, Source coreLibrarySource) { | |
8749 throw new AnalysisException("Could not resolve dart:core"); | |
8750 } | |
8751 } | |
8752 | |
8753 /** | |
8754 * Instances of the class `TypeAliasInfo` hold information about a [TypeAlias]. | |
8755 */ | |
8756 class LibraryResolver2_TypeAliasInfo { | |
8757 final ResolvableLibrary _library; | |
8758 | |
8759 final Source _source; | |
8760 | |
8761 final FunctionTypeAlias _typeAlias; | |
8762 | |
8763 /** | |
8764 * Initialize a newly created information holder with the given information. | |
8765 * | |
8766 * @param library the library containing the type alias | |
8767 * @param source the source of the file containing the type alias | |
8768 * @param typeAlias the type alias being remembered | |
8769 */ | |
8770 LibraryResolver2_TypeAliasInfo(this._library, this._source, this._typeAlias); | |
8771 } | |
8772 | |
8773 /** | |
8774 * Instances of the class `TypeAliasInfo` hold information about a [TypeAlias]. | |
8775 */ | |
8776 class LibraryResolver_TypeAliasInfo { | |
8777 final Library _library; | |
8778 | |
8779 final Source _source; | |
8780 | |
8781 final FunctionTypeAlias _typeAlias; | |
8782 | |
8783 /** | |
8784 * Initialize a newly created information holder with the given information. | |
8785 * | |
8786 * @param library the library containing the type alias | |
8787 * @param source the source of the file containing the type alias | |
8788 * @param typeAlias the type alias being remembered | |
8789 */ | |
8790 LibraryResolver_TypeAliasInfo(this._library, this._source, this._typeAlias); | |
8791 } | |
8792 | |
8793 /** | |
8794 * Instances of the class `LibraryScope` implement a scope containing all of the
names defined | |
8795 * in a given library. | |
8796 */ | |
8797 class LibraryScope extends EnclosedScope { | |
8798 /** | |
8799 * Initialize a newly created scope representing the names defined in the give
n library. | |
8800 * | |
8801 * @param definingLibrary the element representing the library represented by
this scope | |
8802 * @param errorListener the listener that is to be informed when an error is e
ncountered | |
8803 */ | |
8804 LibraryScope( | |
8805 LibraryElement definingLibrary, AnalysisErrorListener errorListener) | |
8806 : super(new LibraryImportScope(definingLibrary, errorListener)) { | |
8807 _defineTopLevelNames(definingLibrary); | |
8808 } | |
8809 | |
8810 @override | |
8811 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { | |
8812 if (existing is PrefixElement) { | |
8813 // TODO(scheglov) consider providing actual 'nameOffset' from the | |
8814 // synthetic accessor | |
8815 int offset = duplicate.nameOffset; | |
8816 if (duplicate is PropertyAccessorElement) { | |
8817 PropertyAccessorElement accessor = duplicate; | |
8818 if (accessor.isSynthetic) { | |
8819 offset = accessor.variable.nameOffset; | |
8820 } | |
8821 } | |
8822 return new AnalysisError(duplicate.source, offset, | |
8823 duplicate.displayName.length, | |
8824 CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER, | |
8825 [existing.displayName]); | |
8826 } | |
8827 return super.getErrorForDuplicate(existing, duplicate); | |
8828 } | |
8829 | |
8830 /** | |
8831 * Add to this scope all of the public top-level names that are defined in the
given compilation | |
8832 * unit. | |
8833 * | |
8834 * @param compilationUnit the compilation unit defining the top-level names to
be added to this | |
8835 * scope | |
8836 */ | |
8837 void _defineLocalNames(CompilationUnitElement compilationUnit) { | |
8838 for (PropertyAccessorElement element in compilationUnit.accessors) { | |
8839 define(element); | |
8840 } | |
8841 for (ClassElement element in compilationUnit.enums) { | |
8842 define(element); | |
8843 } | |
8844 for (FunctionElement element in compilationUnit.functions) { | |
8845 define(element); | |
8846 } | |
8847 for (FunctionTypeAliasElement element | |
8848 in compilationUnit.functionTypeAliases) { | |
8849 define(element); | |
8850 } | |
8851 for (ClassElement element in compilationUnit.types) { | |
8852 define(element); | |
8853 } | |
8854 } | |
8855 | |
8856 /** | |
8857 * Add to this scope all of the names that are explicitly defined in the given
library. | |
8858 * | |
8859 * @param definingLibrary the element representing the library that defines th
e names in this | |
8860 * scope | |
8861 */ | |
8862 void _defineTopLevelNames(LibraryElement definingLibrary) { | |
8863 for (PrefixElement prefix in definingLibrary.prefixes) { | |
8864 define(prefix); | |
8865 } | |
8866 _defineLocalNames(definingLibrary.definingCompilationUnit); | |
8867 for (CompilationUnitElement compilationUnit in definingLibrary.parts) { | |
8868 _defineLocalNames(compilationUnit); | |
8869 } | |
8870 } | |
8871 } | |
8872 | |
8873 /** | |
8874 * This class is used to replace uses of `HashMap<String, ExecutableElement>` | |
8875 * which are not as performant as this class. | |
8876 */ | |
8877 class MemberMap { | |
8878 /** | |
8879 * The current size of this map. | |
8880 */ | |
8881 int _size = 0; | |
8882 | |
8883 /** | |
8884 * The array of keys. | |
8885 */ | |
8886 List<String> _keys; | |
8887 | |
8888 /** | |
8889 * The array of ExecutableElement values. | |
8890 */ | |
8891 List<ExecutableElement> _values; | |
8892 | |
8893 /** | |
8894 * Initialize a newly created member map to have the given [initialCapacity]. | |
8895 * The map will grow if needed. | |
8896 */ | |
8897 MemberMap([int initialCapacity = 10]) { | |
8898 _initArrays(initialCapacity); | |
8899 } | |
8900 | |
8901 /** | |
8902 * This constructor takes an initial capacity of the map. | |
8903 * | |
8904 * @param initialCapacity the initial capacity | |
8905 */ | |
8906 @deprecated // Use new MemberMap(initialCapacity) | |
8907 MemberMap.con1(int initialCapacity) { | |
8908 _initArrays(initialCapacity); | |
8909 } | |
8910 | |
8911 /** | |
8912 * Copy constructor. | |
8913 */ | |
8914 @deprecated // Use new MemberMap.from(memberMap) | |
8915 MemberMap.con2(MemberMap memberMap) { | |
8916 _initArrays(memberMap._size + 5); | |
8917 for (int i = 0; i < memberMap._size; i++) { | |
8918 _keys[i] = memberMap._keys[i]; | |
8919 _values[i] = memberMap._values[i]; | |
8920 } | |
8921 _size = memberMap._size; | |
8922 } | |
8923 | |
8924 /** | |
8925 * Initialize a newly created member map to contain the same members as the | |
8926 * given [memberMap]. | |
8927 */ | |
8928 MemberMap.from(MemberMap memberMap) { | |
8929 _initArrays(memberMap._size + 5); | |
8930 for (int i = 0; i < memberMap._size; i++) { | |
8931 _keys[i] = memberMap._keys[i]; | |
8932 _values[i] = memberMap._values[i]; | |
8933 } | |
8934 _size = memberMap._size; | |
8935 } | |
8936 | |
8937 /** | |
8938 * The size of the map. | |
8939 * | |
8940 * @return the size of the map. | |
8941 */ | |
8942 int get size => _size; | |
8943 | |
8944 /** | |
8945 * Given some key, return the ExecutableElement value from the map, if the key
does not exist in | |
8946 * the map, `null` is returned. | |
8947 * | |
8948 * @param key some key to look up in the map | |
8949 * @return the associated ExecutableElement value from the map, if the key doe
s not exist in the | |
8950 * map, `null` is returned | |
8951 */ | |
8952 ExecutableElement get(String key) { | |
8953 for (int i = 0; i < _size; i++) { | |
8954 if (_keys[i] != null && _keys[i] == key) { | |
8955 return _values[i]; | |
8956 } | |
8957 } | |
8958 return null; | |
8959 } | |
8960 | |
8961 /** | |
8962 * Get and return the key at the specified location. If the key/value pair has
been removed from | |
8963 * the set, then `null` is returned. | |
8964 * | |
8965 * @param i some non-zero value less than size | |
8966 * @return the key at the passed index | |
8967 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than | |
8968 * zero or greater than or equal to the capacity of the arrays | |
8969 */ | |
8970 String getKey(int i) => _keys[i]; | |
8971 | |
8972 /** | |
8973 * Get and return the ExecutableElement at the specified location. If the key/
value pair has been | |
8974 * removed from the set, then then `null` is returned. | |
8975 * | |
8976 * @param i some non-zero value less than size | |
8977 * @return the key at the passed index | |
8978 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than | |
8979 * zero or greater than or equal to the capacity of the arrays | |
8980 */ | |
8981 ExecutableElement getValue(int i) => _values[i]; | |
8982 | |
8983 /** | |
8984 * Given some key/value pair, store the pair in the map. If the key exists alr
eady, then the new | |
8985 * value overrides the old value. | |
8986 * | |
8987 * @param key the key to store in the map | |
8988 * @param value the ExecutableElement value to store in the map | |
8989 */ | |
8990 void put(String key, ExecutableElement value) { | |
8991 // If we already have a value with this key, override the value | |
8992 for (int i = 0; i < _size; i++) { | |
8993 if (_keys[i] != null && _keys[i] == key) { | |
8994 _values[i] = value; | |
8995 return; | |
8996 } | |
8997 } | |
8998 // If needed, double the size of our arrays and copy values over in both | |
8999 // arrays | |
9000 if (_size == _keys.length) { | |
9001 int newArrayLength = _size * 2; | |
9002 List<String> keys_new_array = new List<String>(newArrayLength); | |
9003 List<ExecutableElement> values_new_array = | |
9004 new List<ExecutableElement>(newArrayLength); | |
9005 for (int i = 0; i < _size; i++) { | |
9006 keys_new_array[i] = _keys[i]; | |
9007 } | |
9008 for (int i = 0; i < _size; i++) { | |
9009 values_new_array[i] = _values[i]; | |
9010 } | |
9011 _keys = keys_new_array; | |
9012 _values = values_new_array; | |
9013 } | |
9014 // Put new value at end of array | |
9015 _keys[_size] = key; | |
9016 _values[_size] = value; | |
9017 _size++; | |
9018 } | |
9019 | |
9020 /** | |
9021 * Given some [String] key, this method replaces the associated key and value
pair with | |
9022 * `null`. The size is not decremented with this call, instead it is expected
that the users | |
9023 * check for `null`. | |
9024 * | |
9025 * @param key the key of the key/value pair to remove from the map | |
9026 */ | |
9027 void remove(String key) { | |
9028 for (int i = 0; i < _size; i++) { | |
9029 if (_keys[i] == key) { | |
9030 _keys[i] = null; | |
9031 _values[i] = null; | |
9032 return; | |
9033 } | |
9034 } | |
9035 } | |
9036 | |
9037 /** | |
9038 * Sets the ExecutableElement at the specified location. | |
9039 * | |
9040 * @param i some non-zero value less than size | |
9041 * @param value the ExecutableElement value to store in the map | |
9042 */ | |
9043 void setValue(int i, ExecutableElement value) { | |
9044 _values[i] = value; | |
9045 } | |
9046 | |
9047 /** | |
9048 * Initializes [keys] and [values]. | |
9049 */ | |
9050 void _initArrays(int initialCapacity) { | |
9051 _keys = new List<String>(initialCapacity); | |
9052 _values = new List<ExecutableElement>(initialCapacity); | |
9053 } | |
9054 } | |
9055 | |
9056 /** | |
9057 * Instances of the class `Namespace` implement a mapping of identifiers to the
elements | |
9058 * represented by those identifiers. Namespaces are the building blocks for scop
es. | |
9059 */ | |
9060 class Namespace { | |
9061 /** | |
9062 * An empty namespace. | |
9063 */ | |
9064 static Namespace EMPTY = new Namespace(new HashMap<String, Element>()); | |
9065 | |
9066 /** | |
9067 * A table mapping names that are defined in this namespace to the element rep
resenting the thing | |
9068 * declared with that name. | |
9069 */ | |
9070 final HashMap<String, Element> _definedNames; | |
9071 | |
9072 /** | |
9073 * Initialize a newly created namespace to have the given defined names. | |
9074 * | |
9075 * @param definedNames the mapping from names that are defined in this namespa
ce to the | |
9076 * corresponding elements | |
9077 */ | |
9078 Namespace(this._definedNames); | |
9079 | |
9080 /** | |
9081 * Return a table containing the same mappings as those defined by this namesp
ace. | |
9082 * | |
9083 * @return a table containing the same mappings as those defined by this names
pace | |
9084 */ | |
9085 Map<String, Element> get definedNames => | |
9086 new HashMap<String, Element>.from(_definedNames); | |
9087 | |
9088 /** | |
9089 * Return the element in this namespace that is available to the containing sc
ope using the given | |
9090 * name. | |
9091 * | |
9092 * @param name the name used to reference the | |
9093 * @return the element represented by the given identifier | |
9094 */ | |
9095 Element get(String name) => _definedNames[name]; | |
9096 } | |
9097 | |
9098 /** | |
9099 * Instances of the class `NamespaceBuilder` are used to build a `Namespace`. Na
mespace | |
9100 * builders are thread-safe and re-usable. | |
9101 */ | |
9102 class NamespaceBuilder { | |
9103 /** | |
9104 * Create a namespace representing the export namespace of the given [ExportEl
ement]. | |
9105 * | |
9106 * @param element the export element whose export namespace is to be created | |
9107 * @return the export namespace that was created | |
9108 */ | |
9109 Namespace createExportNamespaceForDirective(ExportElement element) { | |
9110 LibraryElement exportedLibrary = element.exportedLibrary; | |
9111 if (exportedLibrary == null) { | |
9112 // | |
9113 // The exported library will be null if the URI does not reference a valid | |
9114 // library. | |
9115 // | |
9116 return Namespace.EMPTY; | |
9117 } | |
9118 HashMap<String, Element> definedNames = | |
9119 _createExportMapping(exportedLibrary, new HashSet<LibraryElement>()); | |
9120 definedNames = _applyCombinators(definedNames, element.combinators); | |
9121 return new Namespace(definedNames); | |
9122 } | |
9123 | |
9124 /** | |
9125 * Create a namespace representing the export namespace of the given library. | |
9126 * | |
9127 * @param library the library whose export namespace is to be created | |
9128 * @return the export namespace that was created | |
9129 */ | |
9130 Namespace createExportNamespaceForLibrary(LibraryElement library) => | |
9131 new Namespace( | |
9132 _createExportMapping(library, new HashSet<LibraryElement>())); | |
9133 | |
9134 /** | |
9135 * Create a namespace representing the import namespace of the given library. | |
9136 * | |
9137 * @param library the library whose import namespace is to be created | |
9138 * @return the import namespace that was created | |
9139 */ | |
9140 Namespace createImportNamespaceForDirective(ImportElement element) { | |
9141 LibraryElement importedLibrary = element.importedLibrary; | |
9142 if (importedLibrary == null) { | |
9143 // | |
9144 // The imported library will be null if the URI does not reference a valid | |
9145 // library. | |
9146 // | |
9147 return Namespace.EMPTY; | |
9148 } | |
9149 HashMap<String, Element> definedNames = | |
9150 _createExportMapping(importedLibrary, new HashSet<LibraryElement>()); | |
9151 definedNames = _applyCombinators(definedNames, element.combinators); | |
9152 definedNames = _applyPrefix(definedNames, element.prefix); | |
9153 return new Namespace(definedNames); | |
9154 } | |
9155 | |
9156 /** | |
9157 * Create a namespace representing the public namespace of the given library. | |
9158 * | |
9159 * @param library the library whose public namespace is to be created | |
9160 * @return the public namespace that was created | |
9161 */ | |
9162 Namespace createPublicNamespaceForLibrary(LibraryElement library) { | |
9163 HashMap<String, Element> definedNames = new HashMap<String, Element>(); | |
9164 _addPublicNames(definedNames, library.definingCompilationUnit); | |
9165 for (CompilationUnitElement compilationUnit in library.parts) { | |
9166 _addPublicNames(definedNames, compilationUnit); | |
9167 } | |
9168 return new Namespace(definedNames); | |
9169 } | |
9170 | |
9171 /** | |
9172 * Add all of the names in the given namespace to the given mapping table. | |
9173 * | |
9174 * @param definedNames the mapping table to which the names in the given names
pace are to be added | |
9175 * @param namespace the namespace containing the names to be added to this nam
espace | |
9176 */ | |
9177 void _addAllFromNamespace( | |
9178 Map<String, Element> definedNames, Namespace namespace) { | |
9179 if (namespace != null) { | |
9180 definedNames.addAll(namespace.definedNames); | |
9181 } | |
9182 } | |
9183 | |
9184 /** | |
9185 * Add the given element to the given mapping table if it has a publicly visib
le name. | |
9186 * | |
9187 * @param definedNames the mapping table to which the public name is to be add
ed | |
9188 * @param element the element to be added | |
9189 */ | |
9190 void _addIfPublic(Map<String, Element> definedNames, Element element) { | |
9191 String name = element.name; | |
9192 if (name != null && !Scope.isPrivateName(name)) { | |
9193 definedNames[name] = element; | |
9194 } | |
9195 } | |
9196 | |
9197 /** | |
9198 * Add to the given mapping table all of the public top-level names that are d
efined in the given | |
9199 * compilation unit. | |
9200 * | |
9201 * @param definedNames the mapping table to which the public names are to be a
dded | |
9202 * @param compilationUnit the compilation unit defining the top-level names to
be added to this | |
9203 * namespace | |
9204 */ | |
9205 void _addPublicNames(Map<String, Element> definedNames, | |
9206 CompilationUnitElement compilationUnit) { | |
9207 for (PropertyAccessorElement element in compilationUnit.accessors) { | |
9208 _addIfPublic(definedNames, element); | |
9209 } | |
9210 for (ClassElement element in compilationUnit.enums) { | |
9211 _addIfPublic(definedNames, element); | |
9212 } | |
9213 for (FunctionElement element in compilationUnit.functions) { | |
9214 _addIfPublic(definedNames, element); | |
9215 } | |
9216 for (FunctionTypeAliasElement element | |
9217 in compilationUnit.functionTypeAliases) { | |
9218 _addIfPublic(definedNames, element); | |
9219 } | |
9220 for (ClassElement element in compilationUnit.types) { | |
9221 _addIfPublic(definedNames, element); | |
9222 } | |
9223 } | |
9224 | |
9225 /** | |
9226 * Apply the given combinators to all of the names in the given mapping table. | |
9227 * | |
9228 * @param definedNames the mapping table to which the namespace operations are
to be applied | |
9229 * @param combinators the combinators to be applied | |
9230 */ | |
9231 HashMap<String, Element> _applyCombinators( | |
9232 HashMap<String, Element> definedNames, | |
9233 List<NamespaceCombinator> combinators) { | |
9234 for (NamespaceCombinator combinator in combinators) { | |
9235 if (combinator is HideElementCombinator) { | |
9236 _hide(definedNames, combinator.hiddenNames); | |
9237 } else if (combinator is ShowElementCombinator) { | |
9238 definedNames = _show(definedNames, combinator.shownNames); | |
9239 } else { | |
9240 // Internal error. | |
9241 AnalysisEngine.instance.logger | |
9242 .logError("Unknown type of combinator: ${combinator.runtimeType}"); | |
9243 } | |
9244 } | |
9245 return definedNames; | |
9246 } | |
9247 | |
9248 /** | |
9249 * Apply the given prefix to all of the names in the table of defined names. | |
9250 * | |
9251 * @param definedNames the names that were defined before this operation | |
9252 * @param prefixElement the element defining the prefix to be added to the nam
es | |
9253 */ | |
9254 HashMap<String, Element> _applyPrefix( | |
9255 HashMap<String, Element> definedNames, PrefixElement prefixElement) { | |
9256 if (prefixElement != null) { | |
9257 String prefix = prefixElement.name; | |
9258 HashMap<String, Element> newNames = new HashMap<String, Element>(); | |
9259 definedNames.forEach((String name, Element element) { | |
9260 newNames["$prefix.$name"] = element; | |
9261 }); | |
9262 return newNames; | |
9263 } else { | |
9264 return definedNames; | |
9265 } | |
9266 } | |
9267 | |
9268 /** | |
9269 * Create a mapping table representing the export namespace of the given libra
ry. | |
9270 * | |
9271 * @param library the library whose public namespace is to be created | |
9272 * @param visitedElements a set of libraries that do not need to be visited wh
en processing the | |
9273 * export directives of the given library because all of the names de
fined by them will | |
9274 * be added by another library | |
9275 * @return the mapping table that was created | |
9276 */ | |
9277 HashMap<String, Element> _createExportMapping( | |
9278 LibraryElement library, HashSet<LibraryElement> visitedElements) { | |
9279 // Check if the export namespace has been already computed. | |
9280 { | |
9281 Namespace exportNamespace = library.exportNamespace; | |
9282 if (exportNamespace != null) { | |
9283 return exportNamespace.definedNames; | |
9284 } | |
9285 } | |
9286 // TODO(scheglov) Remove this after switching to the new task model. | |
9287 visitedElements.add(library); | |
9288 try { | |
9289 HashMap<String, Element> definedNames = new HashMap<String, Element>(); | |
9290 for (ExportElement element in library.exports) { | |
9291 LibraryElement exportedLibrary = element.exportedLibrary; | |
9292 if (exportedLibrary != null && | |
9293 !visitedElements.contains(exportedLibrary)) { | |
9294 // | |
9295 // The exported library will be null if the URI does not reference a | |
9296 // valid library. | |
9297 // | |
9298 HashMap<String, Element> exportedNames = | |
9299 _createExportMapping(exportedLibrary, visitedElements); | |
9300 exportedNames = _applyCombinators(exportedNames, element.combinators); | |
9301 definedNames.addAll(exportedNames); | |
9302 } | |
9303 } | |
9304 _addAllFromNamespace(definedNames, | |
9305 (library.context as InternalAnalysisContext) | |
9306 .getPublicNamespace(library)); | |
9307 return definedNames; | |
9308 } finally { | |
9309 visitedElements.remove(library); | |
9310 } | |
9311 } | |
9312 | |
9313 /** | |
9314 * Hide all of the given names by removing them from the given collection of d
efined names. | |
9315 * | |
9316 * @param definedNames the names that were defined before this operation | |
9317 * @param hiddenNames the names to be hidden | |
9318 */ | |
9319 void _hide(HashMap<String, Element> definedNames, List<String> hiddenNames) { | |
9320 for (String name in hiddenNames) { | |
9321 definedNames.remove(name); | |
9322 definedNames.remove("$name="); | |
9323 } | |
9324 } | |
9325 | |
9326 /** | |
9327 * Show only the given names by removing all other names from the given collec
tion of defined | |
9328 * names. | |
9329 * | |
9330 * @param definedNames the names that were defined before this operation | |
9331 * @param shownNames the names to be shown | |
9332 */ | |
9333 HashMap<String, Element> _show( | |
9334 HashMap<String, Element> definedNames, List<String> shownNames) { | |
9335 HashMap<String, Element> newNames = new HashMap<String, Element>(); | |
9336 for (String name in shownNames) { | |
9337 Element element = definedNames[name]; | |
9338 if (element != null) { | |
9339 newNames[name] = element; | |
9340 } | |
9341 String setterName = "$name="; | |
9342 element = definedNames[setterName]; | |
9343 if (element != null) { | |
9344 newNames[setterName] = element; | |
9345 } | |
9346 } | |
9347 return newNames; | |
9348 } | |
9349 } | |
9350 | |
9351 /** | |
9352 * Instances of the class `OverrideVerifier` visit all of the declarations in a
compilation | |
9353 * unit to verify that if they have an override annotation it is being used corr
ectly. | |
9354 */ | |
9355 class OverrideVerifier extends RecursiveAstVisitor<Object> { | |
9356 /** | |
9357 * The error reporter used to report errors. | |
9358 */ | |
9359 final ErrorReporter _errorReporter; | |
9360 | |
9361 /** | |
9362 * The inheritance manager used to find overridden methods. | |
9363 */ | |
9364 final InheritanceManager _manager; | |
9365 | |
9366 /** | |
9367 * Initialize a newly created verifier to look for inappropriate uses of the o
verride annotation. | |
9368 * | |
9369 * @param errorReporter the error reporter used to report errors | |
9370 * @param manager the inheritance manager used to find overridden methods | |
9371 */ | |
9372 OverrideVerifier(this._errorReporter, this._manager); | |
9373 | |
9374 @override | |
9375 Object visitMethodDeclaration(MethodDeclaration node) { | |
9376 ExecutableElement element = node.element; | |
9377 if (_isOverride(element)) { | |
9378 if (_getOverriddenMember(element) == null) { | |
9379 if (element is MethodElement) { | |
9380 _errorReporter.reportErrorForNode( | |
9381 HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.name); | |
9382 } else if (element is PropertyAccessorElement) { | |
9383 if (element.isGetter) { | |
9384 _errorReporter.reportErrorForNode( | |
9385 HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER, node.name); | |
9386 } else { | |
9387 _errorReporter.reportErrorForNode( | |
9388 HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER, node.name); | |
9389 } | |
9390 } | |
9391 } | |
9392 } | |
9393 return super.visitMethodDeclaration(node); | |
9394 } | |
9395 | |
9396 /** | |
9397 * Return the member that overrides the given member. | |
9398 * | |
9399 * @param member the member that overrides the returned member | |
9400 * @return the member that overrides the given member | |
9401 */ | |
9402 ExecutableElement _getOverriddenMember(ExecutableElement member) { | |
9403 LibraryElement library = member.library; | |
9404 if (library == null) { | |
9405 return null; | |
9406 } | |
9407 ClassElement classElement = | |
9408 member.getAncestor((element) => element is ClassElement); | |
9409 if (classElement == null) { | |
9410 return null; | |
9411 } | |
9412 return _manager.lookupInheritance(classElement, member.name); | |
9413 } | |
9414 | |
9415 /** | |
9416 * Return `true` if the given element has an override annotation associated wi
th it. | |
9417 * | |
9418 * @param element the element being tested | |
9419 * @return `true` if the element has an override annotation associated with it | |
9420 */ | |
9421 bool _isOverride(Element element) => element != null && element.isOverride; | |
9422 } | |
9423 | |
9424 /** | |
9425 * Instances of the class `PubVerifier` traverse an AST structure looking for de
viations from | |
9426 * pub best practices. | |
9427 */ | |
9428 class PubVerifier extends RecursiveAstVisitor<Object> { | |
9429 // static String _PUBSPEC_YAML = "pubspec.yaml"; | |
9430 | |
9431 /** | |
9432 * The analysis context containing the sources to be analyzed | |
9433 */ | |
9434 final AnalysisContext _context; | |
9435 | |
9436 /** | |
9437 * The error reporter by which errors will be reported. | |
9438 */ | |
9439 final ErrorReporter _errorReporter; | |
9440 | |
9441 PubVerifier(this._context, this._errorReporter); | |
9442 | |
9443 @override | |
9444 Object visitImportDirective(ImportDirective directive) { | |
9445 return null; | |
9446 } | |
9447 | |
9448 // /** | |
9449 // * This verifies that the passed file import directive is not contained in a
source inside a | |
9450 // * package "lib" directory hierarchy referencing a source outside that packa
ge "lib" directory | |
9451 // * hierarchy. | |
9452 // * | |
9453 // * @param uriLiteral the import URL (not `null`) | |
9454 // * @param path the file path being verified (not `null`) | |
9455 // * @return `true` if and only if an error code is generated on the passed no
de | |
9456 // * See [PubSuggestionCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE]. | |
9457 // */ | |
9458 // bool | |
9459 // _checkForFileImportInsideLibReferencesFileOutside(StringLiteral uriLiter
al, | |
9460 // String path) { | |
9461 // Source source = _getSource(uriLiteral); | |
9462 // String fullName = _getSourceFullName(source); | |
9463 // if (fullName != null) { | |
9464 // int pathIndex = 0; | |
9465 // int fullNameIndex = fullName.length; | |
9466 // while (pathIndex < path.length && | |
9467 // StringUtilities.startsWith3(path, pathIndex, 0x2E, 0x2E, 0x2F)) { | |
9468 // fullNameIndex = JavaString.lastIndexOf(fullName, '/', fullNameIndex); | |
9469 // if (fullNameIndex < 4) { | |
9470 // return false; | |
9471 // } | |
9472 // // Check for "/lib" at a specified place in the fullName | |
9473 // if (StringUtilities.startsWith4( | |
9474 // fullName, | |
9475 // fullNameIndex - 4, | |
9476 // 0x2F, | |
9477 // 0x6C, | |
9478 // 0x69, | |
9479 // 0x62)) { | |
9480 // String relativePubspecPath = | |
9481 // path.substring(0, pathIndex + 3) + | |
9482 // _PUBSPEC_YAML; | |
9483 // Source pubspecSource = | |
9484 // _context.sourceFactory.resolveUri(source, relativePubspecPath); | |
9485 // if (_context.exists(pubspecSource)) { | |
9486 // // Files inside the lib directory hierarchy should not reference | |
9487 // // files outside | |
9488 // _errorReporter.reportErrorForNode( | |
9489 // HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE, | |
9490 // uriLiteral); | |
9491 // } | |
9492 // return true; | |
9493 // } | |
9494 // pathIndex += 3; | |
9495 // } | |
9496 // } | |
9497 // return false; | |
9498 // } | |
9499 | |
9500 // /** | |
9501 // * This verifies that the passed file import directive is not contained in a
source outside a | |
9502 // * package "lib" directory hierarchy referencing a source inside that packag
e "lib" directory | |
9503 // * hierarchy. | |
9504 // * | |
9505 // * @param uriLiteral the import URL (not `null`) | |
9506 // * @param path the file path being verified (not `null`) | |
9507 // * @return `true` if and only if an error code is generated on the passed no
de | |
9508 // * See [PubSuggestionCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE]. | |
9509 // */ | |
9510 // bool | |
9511 // _checkForFileImportOutsideLibReferencesFileInside(StringLiteral uriLiter
al, | |
9512 // String path) { | |
9513 // if (StringUtilities.startsWith4(path, 0, 0x6C, 0x69, 0x62, 0x2F)) { | |
9514 // if (_checkForFileImportOutsideLibReferencesFileInsideAtIndex( | |
9515 // uriLiteral, | |
9516 // path, | |
9517 // 0)) { | |
9518 // return true; | |
9519 // } | |
9520 // } | |
9521 // int pathIndex = | |
9522 // StringUtilities.indexOf5(path, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F); | |
9523 // while (pathIndex != -1) { | |
9524 // if (_checkForFileImportOutsideLibReferencesFileInsideAtIndex( | |
9525 // uriLiteral, | |
9526 // path, | |
9527 // pathIndex + 1)) { | |
9528 // return true; | |
9529 // } | |
9530 // pathIndex = | |
9531 // StringUtilities.indexOf5(path, pathIndex + 4, 0x2F, 0x6C, 0x69, 0x62
, 0x2F); | |
9532 // } | |
9533 // return false; | |
9534 // } | |
9535 | |
9536 // bool | |
9537 // _checkForFileImportOutsideLibReferencesFileInsideAtIndex(StringLiteral u
riLiteral, | |
9538 // String path, int pathIndex) { | |
9539 // Source source = _getSource(uriLiteral); | |
9540 // String relativePubspecPath = path.substring(0, pathIndex) + _PUBSPEC_YAML; | |
9541 // Source pubspecSource = | |
9542 // _context.sourceFactory.resolveUri(source, relativePubspecPath); | |
9543 // if (!_context.exists(pubspecSource)) { | |
9544 // return false; | |
9545 // } | |
9546 // String fullName = _getSourceFullName(source); | |
9547 // if (fullName != null) { | |
9548 // if (StringUtilities.indexOf5(fullName, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F)
< | |
9549 // 0) { | |
9550 // // Files outside the lib directory hierarchy should not reference file
s | |
9551 // // inside ... use package: url instead | |
9552 // _errorReporter.reportErrorForNode( | |
9553 // HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE, | |
9554 // uriLiteral); | |
9555 // return true; | |
9556 // } | |
9557 // } | |
9558 // return false; | |
9559 // } | |
9560 | |
9561 // /** | |
9562 // * This verifies that the passed package import directive does not contain "
.." | |
9563 // * | |
9564 // * @param uriLiteral the import URL (not `null`) | |
9565 // * @param path the path to be validated (not `null`) | |
9566 // * @return `true` if and only if an error code is generated on the passed no
de | |
9567 // * See [PubSuggestionCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT]. | |
9568 // */ | |
9569 // bool _checkForPackageImportContainsDotDot(StringLiteral uriLiteral, | |
9570 // String path) { | |
9571 // if (StringUtilities.startsWith3(path, 0, 0x2E, 0x2E, 0x2F) || | |
9572 // StringUtilities.indexOf4(path, 0, 0x2F, 0x2E, 0x2E, 0x2F) >= 0) { | |
9573 // // Package import should not to contain ".." | |
9574 // _errorReporter.reportErrorForNode( | |
9575 // HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT, | |
9576 // uriLiteral); | |
9577 // return true; | |
9578 // } | |
9579 // return false; | |
9580 // } | |
9581 | |
9582 // /** | |
9583 // * Answer the source associated with the compilation unit containing the giv
en AST node. | |
9584 // * | |
9585 // * @param node the node (not `null`) | |
9586 // * @return the source or `null` if it could not be determined | |
9587 // */ | |
9588 // Source _getSource(AstNode node) { | |
9589 // Source source = null; | |
9590 // CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit)
; | |
9591 // if (unit != null) { | |
9592 // CompilationUnitElement element = unit.element; | |
9593 // if (element != null) { | |
9594 // source = element.source; | |
9595 // } | |
9596 // } | |
9597 // return source; | |
9598 // } | |
9599 | |
9600 // /** | |
9601 // * Answer the full name of the given source. The returned value will have al
l | |
9602 // * [File.separatorChar] replace by '/'. | |
9603 // * | |
9604 // * @param source the source | |
9605 // * @return the full name or `null` if it could not be determined | |
9606 // */ | |
9607 // String _getSourceFullName(Source source) { | |
9608 // if (source != null) { | |
9609 // String fullName = source.fullName; | |
9610 // if (fullName != null) { | |
9611 // return fullName.replaceAll(r'\', '/'); | |
9612 // } | |
9613 // } | |
9614 // return null; | |
9615 // } | |
9616 } | |
9617 | |
9618 /** | |
9619 * Kind of the redirecting constructor. | |
9620 */ | |
9621 class RedirectingConstructorKind extends Enum<RedirectingConstructorKind> { | |
9622 static const RedirectingConstructorKind CONST = | |
9623 const RedirectingConstructorKind('CONST', 0); | |
9624 | |
9625 static const RedirectingConstructorKind NORMAL = | |
9626 const RedirectingConstructorKind('NORMAL', 1); | |
9627 | |
9628 static const List<RedirectingConstructorKind> values = const [CONST, NORMAL]; | |
9629 | |
9630 const RedirectingConstructorKind(String name, int ordinal) | |
9631 : super(name, ordinal); | |
9632 } | |
9633 | |
9634 /** | |
9635 * A `ResolvableLibrary` represents a single library during the resolution of | |
9636 * some (possibly different) library. They are not intended to be used except | |
9637 * during the resolution process. | |
9638 */ | |
9639 class ResolvableLibrary { | |
9640 /** | |
9641 * An empty array that can be used to initialize lists of libraries. | |
9642 */ | |
9643 static List<ResolvableLibrary> _EMPTY_ARRAY = new List<ResolvableLibrary>(0); | |
9644 | |
9645 /** | |
9646 * The next artificial hash code. | |
9647 */ | |
9648 static int _NEXT_HASH_CODE = 0; | |
9649 | |
9650 /** | |
9651 * The artifitial hash code for this object. | |
9652 */ | |
9653 final int _hashCode = _nextHashCode(); | |
9654 | |
9655 /** | |
9656 * The source specifying the defining compilation unit of this library. | |
9657 */ | |
9658 final Source librarySource; | |
9659 | |
9660 /** | |
9661 * A list containing all of the libraries that are imported into this library. | |
9662 */ | |
9663 List<ResolvableLibrary> _importedLibraries = _EMPTY_ARRAY; | |
9664 | |
9665 /** | |
9666 * A flag indicating whether this library explicitly imports core. | |
9667 */ | |
9668 bool explicitlyImportsCore = false; | |
9669 | |
9670 /** | |
9671 * An array containing all of the libraries that are exported from this librar
y. | |
9672 */ | |
9673 List<ResolvableLibrary> _exportedLibraries = _EMPTY_ARRAY; | |
9674 | |
9675 /** | |
9676 * An array containing the compilation units that comprise this library. The | |
9677 * defining compilation unit is always first. | |
9678 */ | |
9679 List<ResolvableCompilationUnit> _compilationUnits; | |
9680 | |
9681 /** | |
9682 * The library element representing this library. | |
9683 */ | |
9684 LibraryElementImpl _libraryElement; | |
9685 | |
9686 /** | |
9687 * The listener to which analysis errors will be reported. | |
9688 */ | |
9689 AnalysisErrorListener _errorListener; | |
9690 | |
9691 /** | |
9692 * The inheritance manager which is used for member lookups in this library. | |
9693 */ | |
9694 InheritanceManager _inheritanceManager; | |
9695 | |
9696 /** | |
9697 * The library scope used when resolving elements within this library's compil
ation units. | |
9698 */ | |
9699 LibraryScope _libraryScope; | |
9700 | |
9701 /** | |
9702 * Initialize a newly created data holder that can maintain the data associate
d with a library. | |
9703 * | |
9704 * @param librarySource the source specifying the defining compilation unit of
this library | |
9705 * @param errorListener the listener to which analysis errors will be reported | |
9706 */ | |
9707 ResolvableLibrary(this.librarySource); | |
9708 | |
9709 /** | |
9710 * Return an array of the [CompilationUnit]s that make up the library. The fir
st unit is | |
9711 * always the defining unit. | |
9712 * | |
9713 * @return an array of the [CompilationUnit]s that make up the library. The fi
rst unit is | |
9714 * always the defining unit | |
9715 */ | |
9716 List<CompilationUnit> get compilationUnits { | |
9717 int count = _compilationUnits.length; | |
9718 List<CompilationUnit> units = new List<CompilationUnit>(count); | |
9719 for (int i = 0; i < count; i++) { | |
9720 units[i] = _compilationUnits[i].compilationUnit; | |
9721 } | |
9722 return units; | |
9723 } | |
9724 | |
9725 /** | |
9726 * Return an array containing the sources for the compilation units in this li
brary, including the | |
9727 * defining compilation unit. | |
9728 * | |
9729 * @return the sources for the compilation units in this library | |
9730 */ | |
9731 List<Source> get compilationUnitSources { | |
9732 int count = _compilationUnits.length; | |
9733 List<Source> sources = new List<Source>(count); | |
9734 for (int i = 0; i < count; i++) { | |
9735 sources[i] = _compilationUnits[i].source; | |
9736 } | |
9737 return sources; | |
9738 } | |
9739 | |
9740 /** | |
9741 * Return the AST structure associated with the defining compilation unit for
this library. | |
9742 * | |
9743 * @return the AST structure associated with the defining compilation unit for
this library | |
9744 * @throws AnalysisException if an AST structure could not be created for the
defining compilation | |
9745 * unit | |
9746 */ | |
9747 CompilationUnit get definingCompilationUnit => | |
9748 _compilationUnits[0].compilationUnit; | |
9749 | |
9750 /** | |
9751 * Set the listener to which analysis errors will be reported to be the given
listener. | |
9752 * | |
9753 * @param errorListener the listener to which analysis errors will be reported | |
9754 */ | |
9755 void set errorListener(AnalysisErrorListener errorListener) { | |
9756 this._errorListener = errorListener; | |
9757 } | |
9758 | |
9759 /** | |
9760 * Set the libraries that are exported by this library to be those in the give
n array. | |
9761 * | |
9762 * @param exportedLibraries the libraries that are exported by this library | |
9763 */ | |
9764 void set exportedLibraries(List<ResolvableLibrary> exportedLibraries) { | |
9765 this._exportedLibraries = exportedLibraries; | |
9766 } | |
9767 | |
9768 /** | |
9769 * Return an array containing the libraries that are exported from this librar
y. | |
9770 * | |
9771 * @return an array containing the libraries that are exported from this libra
ry | |
9772 */ | |
9773 List<ResolvableLibrary> get exports => _exportedLibraries; | |
9774 | |
9775 @override | |
9776 int get hashCode => _hashCode; | |
9777 | |
9778 /** | |
9779 * Set the libraries that are imported into this library to be those in the gi
ven array. | |
9780 * | |
9781 * @param importedLibraries the libraries that are imported into this library | |
9782 */ | |
9783 void set importedLibraries(List<ResolvableLibrary> importedLibraries) { | |
9784 this._importedLibraries = importedLibraries; | |
9785 } | |
9786 | |
9787 /** | |
9788 * Return an array containing the libraries that are imported into this librar
y. | |
9789 * | |
9790 * @return an array containing the libraries that are imported into this libra
ry | |
9791 */ | |
9792 List<ResolvableLibrary> get imports => _importedLibraries; | |
9793 | |
9794 /** | |
9795 * Return an array containing the libraries that are either imported or export
ed from this | |
9796 * library. | |
9797 * | |
9798 * @return the libraries that are either imported or exported from this librar
y | |
9799 */ | |
9800 List<ResolvableLibrary> get importsAndExports { | |
9801 HashSet<ResolvableLibrary> libraries = new HashSet<ResolvableLibrary>(); | |
9802 for (ResolvableLibrary library in _importedLibraries) { | |
9803 libraries.add(library); | |
9804 } | |
9805 for (ResolvableLibrary library in _exportedLibraries) { | |
9806 libraries.add(library); | |
9807 } | |
9808 return new List.from(libraries); | |
9809 } | |
9810 | |
9811 /** | |
9812 * Return the inheritance manager for this library. | |
9813 * | |
9814 * @return the inheritance manager for this library | |
9815 */ | |
9816 InheritanceManager get inheritanceManager { | |
9817 if (_inheritanceManager == null) { | |
9818 return _inheritanceManager = new InheritanceManager(_libraryElement); | |
9819 } | |
9820 return _inheritanceManager; | |
9821 } | |
9822 | |
9823 /** | |
9824 * Return the library element representing this library, creating it if necess
ary. | |
9825 * | |
9826 * @return the library element representing this library | |
9827 */ | |
9828 LibraryElementImpl get libraryElement => _libraryElement; | |
9829 | |
9830 /** | |
9831 * Set the library element representing this library to the given library elem
ent. | |
9832 * | |
9833 * @param libraryElement the library element representing this library | |
9834 */ | |
9835 void set libraryElement(LibraryElementImpl libraryElement) { | |
9836 this._libraryElement = libraryElement; | |
9837 if (_inheritanceManager != null) { | |
9838 _inheritanceManager.libraryElement = libraryElement; | |
9839 } | |
9840 } | |
9841 | |
9842 /** | |
9843 * Return the library scope used when resolving elements within this library's
compilation units. | |
9844 * | |
9845 * @return the library scope used when resolving elements within this library'
s compilation units | |
9846 */ | |
9847 LibraryScope get libraryScope { | |
9848 if (_libraryScope == null) { | |
9849 _libraryScope = new LibraryScope(_libraryElement, _errorListener); | |
9850 } | |
9851 return _libraryScope; | |
9852 } | |
9853 | |
9854 /** | |
9855 * Return an array containing the compilation units that comprise this library
. The defining | |
9856 * compilation unit is always first. | |
9857 * | |
9858 * @return the compilation units that comprise this library | |
9859 */ | |
9860 List<ResolvableCompilationUnit> get resolvableCompilationUnits => | |
9861 _compilationUnits; | |
9862 | |
9863 /** | |
9864 * Set the compilation unit in this library to the given compilation units. Th
e defining | |
9865 * compilation unit must be the first element of the array. | |
9866 * | |
9867 * @param units the compilation units in this library | |
9868 */ | |
9869 void set resolvableCompilationUnits(List<ResolvableCompilationUnit> units) { | |
9870 _compilationUnits = units; | |
9871 } | |
9872 | |
9873 /** | |
9874 * Return the AST structure associated with the given source, or `null` if the
source does | |
9875 * not represent a compilation unit that is included in this library. | |
9876 * | |
9877 * @param source the source representing the compilation unit whose AST is to
be returned | |
9878 * @return the AST structure associated with the given source | |
9879 * @throws AnalysisException if an AST structure could not be created for the
compilation unit | |
9880 */ | |
9881 CompilationUnit getAST(Source source) { | |
9882 int count = _compilationUnits.length; | |
9883 for (int i = 0; i < count; i++) { | |
9884 if (_compilationUnits[i].source == source) { | |
9885 return _compilationUnits[i].compilationUnit; | |
9886 } | |
9887 } | |
9888 return null; | |
9889 } | |
9890 | |
9891 @override | |
9892 String toString() => librarySource.shortName; | |
9893 | |
9894 static int _nextHashCode() { | |
9895 int next = (_NEXT_HASH_CODE + 1) & 0xFFFFFF; | |
9896 _NEXT_HASH_CODE = next; | |
9897 return next; | |
9898 } | |
9899 } | |
9900 | |
9901 /** | |
9902 * The enumeration `ResolverErrorCode` defines the error codes used for errors | |
9903 * detected by the resolver. The convention for this class is for the name of | |
9904 * the error code to indicate the problem that caused the error to be generated | |
9905 * and for the error message to explain what is wrong and, when appropriate, how | |
9906 * the problem can be corrected. | |
9907 */ | |
9908 class ResolverErrorCode extends ErrorCode { | |
9909 static const ResolverErrorCode BREAK_LABEL_ON_SWITCH_MEMBER = | |
9910 const ResolverErrorCode('BREAK_LABEL_ON_SWITCH_MEMBER', | |
9911 "Break label resolves to case or default statement"); | |
9912 | |
9913 static const ResolverErrorCode CONTINUE_LABEL_ON_SWITCH = | |
9914 const ResolverErrorCode('CONTINUE_LABEL_ON_SWITCH', | |
9915 "A continue label resolves to switch, must be loop or switch member"); | |
9916 | |
9917 static const ResolverErrorCode MISSING_LIBRARY_DIRECTIVE_WITH_PART = | |
9918 const ResolverErrorCode('MISSING_LIBRARY_DIRECTIVE_WITH_PART', | |
9919 "Libraries that have parts must have a library directive"); | |
9920 | |
9921 /** | |
9922 * Initialize a newly created error code to have the given [name]. The message | |
9923 * associated with the error will be created from the given [message] | |
9924 * template. The correction associated with the error will be created from the | |
9925 * given [correction] template. | |
9926 */ | |
9927 const ResolverErrorCode(String name, String message, [String correction]) | |
9928 : super(name, message, correction); | |
9929 | |
9930 @override | |
9931 ErrorSeverity get errorSeverity => type.severity; | |
9932 | |
9933 @override | |
9934 ErrorType get type => ErrorType.COMPILE_TIME_ERROR; | |
9935 } | |
9936 | |
9937 /** | |
9938 * Instances of the class `ResolverVisitor` are used to resolve the nodes within
a single | |
9939 * compilation unit. | |
9940 */ | |
9941 class ResolverVisitor extends ScopedVisitor { | |
9942 /** | |
9943 * The manager for the inheritance mappings. | |
9944 */ | |
9945 InheritanceManager _inheritanceManager; | |
9946 | |
9947 /** | |
9948 * The object used to resolve the element associated with the current node. | |
9949 */ | |
9950 ElementResolver elementResolver; | |
9951 | |
9952 /** | |
9953 * The object used to compute the type associated with the current node. | |
9954 */ | |
9955 StaticTypeAnalyzer typeAnalyzer; | |
9956 | |
9957 /** | |
9958 * The class element representing the class containing the current node, | |
9959 * or `null` if the current node is not contained in a class. | |
9960 */ | |
9961 ClassElement enclosingClass = null; | |
9962 | |
9963 /** | |
9964 * The class declaration representing the class containing the current node, o
r `null` if | |
9965 * the current node is not contained in a class. | |
9966 */ | |
9967 ClassDeclaration _enclosingClassDeclaration = null; | |
9968 | |
9969 /** | |
9970 * The function type alias representing the function type containing the curre
nt node, or | |
9971 * `null` if the current node is not contained in a function type alias. | |
9972 */ | |
9973 FunctionTypeAlias _enclosingFunctionTypeAlias = null; | |
9974 | |
9975 /** | |
9976 * The element representing the function containing the current node, or `null
` if the | |
9977 * current node is not contained in a function. | |
9978 */ | |
9979 ExecutableElement _enclosingFunction = null; | |
9980 | |
9981 /** | |
9982 * The [Comment] before a [FunctionDeclaration] or a [MethodDeclaration] that | |
9983 * cannot be resolved where we visited it, because it should be resolved in th
e scope of the body. | |
9984 */ | |
9985 Comment _commentBeforeFunction = null; | |
9986 | |
9987 /** | |
9988 * The object keeping track of which elements have had their types overridden. | |
9989 */ | |
9990 TypeOverrideManager _overrideManager = new TypeOverrideManager(); | |
9991 | |
9992 /** | |
9993 * The object keeping track of which elements have had their types promoted. | |
9994 */ | |
9995 TypePromotionManager _promoteManager = new TypePromotionManager(); | |
9996 | |
9997 /** | |
9998 * A comment before a function should be resolved in the context of the | |
9999 * function. But when we incrementally resolve a comment, we don't want to | |
10000 * resolve the whole function. | |
10001 * | |
10002 * So, this flag is set to `true`, when just context of the function should | |
10003 * be built and the comment resolved. | |
10004 */ | |
10005 bool resolveOnlyCommentInFunctionBody = false; | |
10006 | |
10007 /** | |
10008 * Initialize a newly created visitor to resolve the nodes in an AST node. | |
10009 * | |
10010 * [definingLibrary] is the element for the library containing the node being | |
10011 * visited. | |
10012 * [source] is the source representing the compilation unit containing the | |
10013 * node being visited. | |
10014 * [typeProvider] the object used to access the types from the core library. | |
10015 * [errorListener] the error listener that will be informed of any errors | |
10016 * that are found during resolution. | |
10017 * [nameScope] is the scope used to resolve identifiers in the node that will | |
10018 * first be visited. If `null` or unspecified, a new [LibraryScope] will be | |
10019 * created based on [definingLibrary] and [typeProvider]. | |
10020 * [inheritanceManager] is used to perform inheritance lookups. If `null` or | |
10021 * unspecified, a new [InheritanceManager] will be created based on | |
10022 * [definingLibrary]. | |
10023 * [typeAnalyzerFactory] is used to create the type analyzer. If `null` or | |
10024 * unspecified, a type analyzer of type [StaticTypeAnalyzer] will be created. | |
10025 */ | |
10026 ResolverVisitor(LibraryElement definingLibrary, Source source, | |
10027 TypeProvider typeProvider, AnalysisErrorListener errorListener, | |
10028 {Scope nameScope, InheritanceManager inheritanceManager, | |
10029 StaticTypeAnalyzerFactory typeAnalyzerFactory}) | |
10030 : super(definingLibrary, source, typeProvider, errorListener, | |
10031 nameScope: nameScope) { | |
10032 if (inheritanceManager == null) { | |
10033 this._inheritanceManager = new InheritanceManager(definingLibrary); | |
10034 } else { | |
10035 this._inheritanceManager = inheritanceManager; | |
10036 } | |
10037 this.elementResolver = new ElementResolver(this); | |
10038 if (typeAnalyzerFactory == null) { | |
10039 this.typeAnalyzer = new StaticTypeAnalyzer(this); | |
10040 } else { | |
10041 this.typeAnalyzer = typeAnalyzerFactory(this); | |
10042 } | |
10043 } | |
10044 | |
10045 /** | |
10046 * Initialize a newly created visitor to resolve the nodes in a compilation un
it. | |
10047 * | |
10048 * @param library the library containing the compilation unit being resolved | |
10049 * @param source the source representing the compilation unit being visited | |
10050 * @param typeProvider the object used to access the types from the core libra
ry | |
10051 * | |
10052 * Deprecated. Please use unnamed constructor instead. | |
10053 */ | |
10054 @deprecated | |
10055 ResolverVisitor.con1( | |
10056 Library library, Source source, TypeProvider typeProvider, | |
10057 {StaticTypeAnalyzerFactory typeAnalyzerFactory}) | |
10058 : this( | |
10059 library.libraryElement, source, typeProvider, library.errorListener, | |
10060 nameScope: library.libraryScope, | |
10061 inheritanceManager: library.inheritanceManager, | |
10062 typeAnalyzerFactory: typeAnalyzerFactory); | |
10063 | |
10064 /** | |
10065 * Return the element representing the function containing the current node, o
r `null` if | |
10066 * the current node is not contained in a function. | |
10067 * | |
10068 * @return the element representing the function containing the current node | |
10069 */ | |
10070 ExecutableElement get enclosingFunction => _enclosingFunction; | |
10071 | |
10072 /** | |
10073 * Return the object keeping track of which elements have had their types over
ridden. | |
10074 * | |
10075 * @return the object keeping track of which elements have had their types ove
rridden | |
10076 */ | |
10077 TypeOverrideManager get overrideManager => _overrideManager; | |
10078 | |
10079 /** | |
10080 * Return the object keeping track of which elements have had their types prom
oted. | |
10081 * | |
10082 * @return the object keeping track of which elements have had their types pro
moted | |
10083 */ | |
10084 TypePromotionManager get promoteManager => _promoteManager; | |
10085 | |
10086 /** | |
10087 * Return the propagated element associated with the given expression whose ty
pe can be | |
10088 * overridden, or `null` if there is no element whose type can be overridden. | |
10089 * | |
10090 * @param expression the expression with which the element is associated | |
10091 * @return the element associated with the given expression | |
10092 */ | |
10093 VariableElement getOverridablePropagatedElement(Expression expression) { | |
10094 Element element = null; | |
10095 if (expression is SimpleIdentifier) { | |
10096 element = expression.propagatedElement; | |
10097 } else if (expression is PrefixedIdentifier) { | |
10098 element = expression.propagatedElement; | |
10099 } else if (expression is PropertyAccess) { | |
10100 element = expression.propertyName.propagatedElement; | |
10101 } | |
10102 if (element is VariableElement) { | |
10103 return element; | |
10104 } | |
10105 return null; | |
10106 } | |
10107 | |
10108 /** | |
10109 * Return the static element associated with the given expression whose type c
an be overridden, or | |
10110 * `null` if there is no element whose type can be overridden. | |
10111 * | |
10112 * @param expression the expression with which the element is associated | |
10113 * @return the element associated with the given expression | |
10114 */ | |
10115 VariableElement getOverridableStaticElement(Expression expression) { | |
10116 Element element = null; | |
10117 if (expression is SimpleIdentifier) { | |
10118 element = expression.staticElement; | |
10119 } else if (expression is PrefixedIdentifier) { | |
10120 element = expression.staticElement; | |
10121 } else if (expression is PropertyAccess) { | |
10122 element = expression.propertyName.staticElement; | |
10123 } | |
10124 if (element is VariableElement) { | |
10125 return element; | |
10126 } | |
10127 return null; | |
10128 } | |
10129 | |
10130 /** | |
10131 * Return the static element associated with the given expression whose type c
an be promoted, or | |
10132 * `null` if there is no element whose type can be promoted. | |
10133 * | |
10134 * @param expression the expression with which the element is associated | |
10135 * @return the element associated with the given expression | |
10136 */ | |
10137 VariableElement getPromotionStaticElement(Expression expression) { | |
10138 while (expression is ParenthesizedExpression) { | |
10139 expression = (expression as ParenthesizedExpression).expression; | |
10140 } | |
10141 if (expression is! SimpleIdentifier) { | |
10142 return null; | |
10143 } | |
10144 SimpleIdentifier identifier = expression as SimpleIdentifier; | |
10145 Element element = identifier.staticElement; | |
10146 if (element is! VariableElement) { | |
10147 return null; | |
10148 } | |
10149 ElementKind kind = element.kind; | |
10150 if (kind == ElementKind.LOCAL_VARIABLE) { | |
10151 return element as VariableElement; | |
10152 } | |
10153 if (kind == ElementKind.PARAMETER) { | |
10154 return element as VariableElement; | |
10155 } | |
10156 return null; | |
10157 } | |
10158 | |
10159 /** | |
10160 * Prepares this [ResolverVisitor] to using it for incremental resolution. | |
10161 */ | |
10162 void initForIncrementalResolution() { | |
10163 _overrideManager.enterScope(); | |
10164 } | |
10165 | |
10166 /** | |
10167 * If it is appropriate to do so, override the current type of the static and
propagated elements | |
10168 * associated with the given expression with the given type. Generally speakin
g, it is appropriate | |
10169 * if the given type is more specific than the current type. | |
10170 * | |
10171 * @param expression the expression used to access the static and propagated e
lements whose types | |
10172 * might be overridden | |
10173 * @param potentialType the potential type of the elements | |
10174 * @param allowPrecisionLoss see @{code overrideVariable} docs | |
10175 */ | |
10176 void overrideExpression(Expression expression, DartType potentialType, | |
10177 bool allowPrecisionLoss, bool setExpressionType) { | |
10178 VariableElement element = getOverridableStaticElement(expression); | |
10179 if (element != null) { | |
10180 DartType newBestType = | |
10181 overrideVariable(element, potentialType, allowPrecisionLoss); | |
10182 if (setExpressionType) { | |
10183 recordPropagatedTypeIfBetter(expression, newBestType); | |
10184 } | |
10185 } | |
10186 element = getOverridablePropagatedElement(expression); | |
10187 if (element != null) { | |
10188 overrideVariable(element, potentialType, allowPrecisionLoss); | |
10189 } | |
10190 } | |
10191 | |
10192 /** | |
10193 * If it is appropriate to do so, override the current type of the given eleme
nt with the given | |
10194 * type. | |
10195 * | |
10196 * @param element the element whose type might be overridden | |
10197 * @param potentialType the potential type of the element | |
10198 * @param allowPrecisionLoss true if `potentialType` is allowed to be less pre
cise than the | |
10199 * current best type | |
10200 * | |
10201 * Return a new better [DartType], or `null` if [potentialType] is not better | |
10202 * than the current [element] type. | |
10203 */ | |
10204 DartType overrideVariable(VariableElement element, DartType potentialType, | |
10205 bool allowPrecisionLoss) { | |
10206 if (potentialType == null || potentialType.isBottom) { | |
10207 return null; | |
10208 } | |
10209 DartType currentType = _overrideManager.getBestType(element); | |
10210 | |
10211 if (potentialType == currentType) { | |
10212 return null; | |
10213 } | |
10214 | |
10215 // If we aren't allowing precision loss then the third and fourth conditions | |
10216 // check that we aren't losing precision. | |
10217 // | |
10218 // Let [C] be the current type and [P] be the potential type. When we | |
10219 // aren't allowing precision loss -- which is the case for is-checks -- we | |
10220 // check that [! (C << P)] or [P << C]. The second check, that [P << C], is | |
10221 // analogous to part of the Dart Language Spec rule for type promotion under | |
10222 // is-checks (in the analogy [T] is [P] and [S] is [C]): | |
10223 // | |
10224 // An is-expression of the form [v is T] shows that [v] has type [T] iff | |
10225 // [T] is more specific than the type [S] of the expression [v] and both | |
10226 // [T != dynamic] and [S != dynamic]. | |
10227 // | |
10228 // It also covers an important case that is not applicable in the spec: | |
10229 // for union types, we want an is-check to promote from an union type to | |
10230 // (a subtype of) any of its members. | |
10231 // | |
10232 // The first check, that [! (C << P)], covers the case where [P] and [C] are | |
10233 // unrelated types; This case is not addressed in the spec for static types. | |
10234 if (currentType == null || | |
10235 allowPrecisionLoss || | |
10236 !currentType.isMoreSpecificThan(potentialType) || | |
10237 potentialType.isMoreSpecificThan(currentType)) { | |
10238 // TODO(scheglov) type propagation for instance/top-level fields | |
10239 // was disabled because it depends on the order or visiting. | |
10240 // If both field and its client are in the same unit, and we visit | |
10241 // the client before the field, then propagated type is not set yet. | |
10242 // if (element is PropertyInducingElement) { | |
10243 // PropertyInducingElement variable = element; | |
10244 // if (!variable.isConst && !variable.isFinal) { | |
10245 // return; | |
10246 // } | |
10247 // (variable as PropertyInducingElementImpl).propagatedType = | |
10248 // potentialType; | |
10249 // } | |
10250 _overrideManager.setType(element, potentialType); | |
10251 return potentialType; | |
10252 } | |
10253 return null; | |
10254 } | |
10255 | |
10256 /** | |
10257 * If the given [type] is valid, strongly more specific than the | |
10258 * existing static type of the given [expression], record it as a propagated | |
10259 * type of the given [expression]. Otherwise, reset it to `null`. | |
10260 * | |
10261 * If [hasOldPropagatedType] is `true` then the existing propagated type | |
10262 * should also is checked. | |
10263 */ | |
10264 void recordPropagatedTypeIfBetter(Expression expression, DartType type, | |
10265 [bool hasOldPropagatedType = false]) { | |
10266 // Ensure that propagated type invalid. | |
10267 if (type == null || type.isDynamic || type.isBottom) { | |
10268 if (!hasOldPropagatedType) { | |
10269 expression.propagatedType = null; | |
10270 } | |
10271 return; | |
10272 } | |
10273 // Ensure that propagated type is more specific than the static type. | |
10274 DartType staticType = expression.staticType; | |
10275 if (type == staticType || !type.isMoreSpecificThan(staticType)) { | |
10276 expression.propagatedType = null; | |
10277 return; | |
10278 } | |
10279 // Ensure that the new propagated type is more specific than the old one. | |
10280 if (hasOldPropagatedType) { | |
10281 DartType oldPropagatedType = expression.propagatedType; | |
10282 if (oldPropagatedType != null && | |
10283 !type.isMoreSpecificThan(oldPropagatedType)) { | |
10284 return; | |
10285 } | |
10286 } | |
10287 // OK | |
10288 expression.propagatedType = type; | |
10289 } | |
10290 | |
10291 @override | |
10292 Object visitAnnotation(Annotation node) { | |
10293 AstNode parent = node.parent; | |
10294 if (identical(parent, _enclosingClassDeclaration) || | |
10295 identical(parent, _enclosingFunctionTypeAlias)) { | |
10296 return null; | |
10297 } | |
10298 return super.visitAnnotation(node); | |
10299 } | |
10300 | |
10301 @override | |
10302 Object visitAsExpression(AsExpression node) { | |
10303 super.visitAsExpression(node); | |
10304 // Since an as-statement doesn't actually change the type, we don't | |
10305 // let it affect the propagated type when it would result in a loss | |
10306 // of precision. | |
10307 overrideExpression(node.expression, node.type.type, false, false); | |
10308 return null; | |
10309 } | |
10310 | |
10311 @override | |
10312 Object visitAssertStatement(AssertStatement node) { | |
10313 super.visitAssertStatement(node); | |
10314 _propagateTrueState(node.condition); | |
10315 return null; | |
10316 } | |
10317 | |
10318 @override | |
10319 Object visitBinaryExpression(BinaryExpression node) { | |
10320 sc.TokenType operatorType = node.operator.type; | |
10321 Expression leftOperand = node.leftOperand; | |
10322 Expression rightOperand = node.rightOperand; | |
10323 if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) { | |
10324 safelyVisit(leftOperand); | |
10325 if (rightOperand != null) { | |
10326 _overrideManager.enterScope(); | |
10327 try { | |
10328 _promoteManager.enterScope(); | |
10329 try { | |
10330 _propagateTrueState(leftOperand); | |
10331 // Type promotion. | |
10332 _promoteTypes(leftOperand); | |
10333 _clearTypePromotionsIfPotentiallyMutatedIn(leftOperand); | |
10334 _clearTypePromotionsIfPotentiallyMutatedIn(rightOperand); | |
10335 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( | |
10336 rightOperand); | |
10337 // Visit right operand. | |
10338 rightOperand.accept(this); | |
10339 } finally { | |
10340 _promoteManager.exitScope(); | |
10341 } | |
10342 } finally { | |
10343 _overrideManager.exitScope(); | |
10344 } | |
10345 } | |
10346 } else if (operatorType == sc.TokenType.BAR_BAR) { | |
10347 safelyVisit(leftOperand); | |
10348 if (rightOperand != null) { | |
10349 _overrideManager.enterScope(); | |
10350 try { | |
10351 _propagateFalseState(leftOperand); | |
10352 rightOperand.accept(this); | |
10353 } finally { | |
10354 _overrideManager.exitScope(); | |
10355 } | |
10356 } | |
10357 } else { | |
10358 safelyVisit(leftOperand); | |
10359 safelyVisit(rightOperand); | |
10360 } | |
10361 node.accept(elementResolver); | |
10362 node.accept(typeAnalyzer); | |
10363 return null; | |
10364 } | |
10365 | |
10366 @override | |
10367 Object visitBlockFunctionBody(BlockFunctionBody node) { | |
10368 safelyVisit(_commentBeforeFunction); | |
10369 _overrideManager.enterScope(); | |
10370 try { | |
10371 super.visitBlockFunctionBody(node); | |
10372 } finally { | |
10373 _overrideManager.exitScope(); | |
10374 } | |
10375 return null; | |
10376 } | |
10377 | |
10378 @override | |
10379 Object visitBreakStatement(BreakStatement node) { | |
10380 // | |
10381 // We do not visit the label because it needs to be visited in the context | |
10382 // of the statement. | |
10383 // | |
10384 node.accept(elementResolver); | |
10385 node.accept(typeAnalyzer); | |
10386 return null; | |
10387 } | |
10388 | |
10389 @override | |
10390 Object visitClassDeclaration(ClassDeclaration node) { | |
10391 // | |
10392 // Resolve the metadata in the library scope. | |
10393 // | |
10394 if (node.metadata != null) { | |
10395 node.metadata.accept(this); | |
10396 } | |
10397 _enclosingClassDeclaration = node; | |
10398 // | |
10399 // Continue the class resolution. | |
10400 // | |
10401 ClassElement outerType = enclosingClass; | |
10402 try { | |
10403 enclosingClass = node.element; | |
10404 typeAnalyzer.thisType = | |
10405 enclosingClass == null ? null : enclosingClass.type; | |
10406 super.visitClassDeclaration(node); | |
10407 node.accept(elementResolver); | |
10408 node.accept(typeAnalyzer); | |
10409 } finally { | |
10410 typeAnalyzer.thisType = outerType == null ? null : outerType.type; | |
10411 enclosingClass = outerType; | |
10412 _enclosingClassDeclaration = null; | |
10413 } | |
10414 return null; | |
10415 } | |
10416 | |
10417 /** | |
10418 * Implementation of this method should be synchronized with | |
10419 * [visitClassDeclaration]. | |
10420 */ | |
10421 visitClassDeclarationIncrementally(ClassDeclaration node) { | |
10422 // | |
10423 // Resolve the metadata in the library scope. | |
10424 // | |
10425 if (node.metadata != null) { | |
10426 node.metadata.accept(this); | |
10427 } | |
10428 _enclosingClassDeclaration = node; | |
10429 // | |
10430 // Continue the class resolution. | |
10431 // | |
10432 enclosingClass = node.element; | |
10433 typeAnalyzer.thisType = enclosingClass == null ? null : enclosingClass.type; | |
10434 node.accept(elementResolver); | |
10435 node.accept(typeAnalyzer); | |
10436 } | |
10437 | |
10438 @override | |
10439 Object visitComment(Comment node) { | |
10440 if (node.parent is FunctionDeclaration || | |
10441 node.parent is ConstructorDeclaration || | |
10442 node.parent is MethodDeclaration) { | |
10443 if (!identical(node, _commentBeforeFunction)) { | |
10444 _commentBeforeFunction = node; | |
10445 return null; | |
10446 } | |
10447 } | |
10448 super.visitComment(node); | |
10449 _commentBeforeFunction = null; | |
10450 return null; | |
10451 } | |
10452 | |
10453 @override | |
10454 Object visitCommentReference(CommentReference node) { | |
10455 // | |
10456 // We do not visit the identifier because it needs to be visited in the | |
10457 // context of the reference. | |
10458 // | |
10459 node.accept(elementResolver); | |
10460 node.accept(typeAnalyzer); | |
10461 return null; | |
10462 } | |
10463 | |
10464 @override | |
10465 Object visitCompilationUnit(CompilationUnit node) { | |
10466 // | |
10467 // TODO(brianwilkerson) The goal of the code below is to visit the | |
10468 // declarations in such an order that we can infer type information for | |
10469 // top-level variables before we visit references to them. This is better | |
10470 // than making no effort, but still doesn't completely satisfy that goal | |
10471 // (consider for example "final var a = b; final var b = 0;"; we'll infer a | |
10472 // type of 'int' for 'b', but not for 'a' because of the order of the | |
10473 // visits). Ideally we would create a dependency graph, but that would | |
10474 // require references to be resolved, which they are not. | |
10475 // | |
10476 _overrideManager.enterScope(); | |
10477 try { | |
10478 NodeList<Directive> directives = node.directives; | |
10479 int directiveCount = directives.length; | |
10480 for (int i = 0; i < directiveCount; i++) { | |
10481 directives[i].accept(this); | |
10482 } | |
10483 NodeList<CompilationUnitMember> declarations = node.declarations; | |
10484 int declarationCount = declarations.length; | |
10485 for (int i = 0; i < declarationCount; i++) { | |
10486 CompilationUnitMember declaration = declarations[i]; | |
10487 if (declaration is! ClassDeclaration) { | |
10488 declaration.accept(this); | |
10489 } | |
10490 } | |
10491 for (int i = 0; i < declarationCount; i++) { | |
10492 CompilationUnitMember declaration = declarations[i]; | |
10493 if (declaration is ClassDeclaration) { | |
10494 declaration.accept(this); | |
10495 } | |
10496 } | |
10497 } finally { | |
10498 _overrideManager.exitScope(); | |
10499 } | |
10500 node.accept(elementResolver); | |
10501 node.accept(typeAnalyzer); | |
10502 return null; | |
10503 } | |
10504 | |
10505 @override | |
10506 Object visitConditionalExpression(ConditionalExpression node) { | |
10507 Expression condition = node.condition; | |
10508 safelyVisit(condition); | |
10509 Expression thenExpression = node.thenExpression; | |
10510 if (thenExpression != null) { | |
10511 _overrideManager.enterScope(); | |
10512 try { | |
10513 _promoteManager.enterScope(); | |
10514 try { | |
10515 _propagateTrueState(condition); | |
10516 // Type promotion. | |
10517 _promoteTypes(condition); | |
10518 _clearTypePromotionsIfPotentiallyMutatedIn(thenExpression); | |
10519 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( | |
10520 thenExpression); | |
10521 // Visit "then" expression. | |
10522 thenExpression.accept(this); | |
10523 } finally { | |
10524 _promoteManager.exitScope(); | |
10525 } | |
10526 } finally { | |
10527 _overrideManager.exitScope(); | |
10528 } | |
10529 } | |
10530 Expression elseExpression = node.elseExpression; | |
10531 if (elseExpression != null) { | |
10532 _overrideManager.enterScope(); | |
10533 try { | |
10534 _propagateFalseState(condition); | |
10535 elseExpression.accept(this); | |
10536 } finally { | |
10537 _overrideManager.exitScope(); | |
10538 } | |
10539 } | |
10540 node.accept(elementResolver); | |
10541 node.accept(typeAnalyzer); | |
10542 bool thenIsAbrupt = _isAbruptTerminationExpression(thenExpression); | |
10543 bool elseIsAbrupt = _isAbruptTerminationExpression(elseExpression); | |
10544 if (elseIsAbrupt && !thenIsAbrupt) { | |
10545 _propagateTrueState(condition); | |
10546 _propagateState(thenExpression); | |
10547 } else if (thenIsAbrupt && !elseIsAbrupt) { | |
10548 _propagateFalseState(condition); | |
10549 _propagateState(elseExpression); | |
10550 } | |
10551 return null; | |
10552 } | |
10553 | |
10554 @override | |
10555 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
10556 ExecutableElement outerFunction = _enclosingFunction; | |
10557 try { | |
10558 _enclosingFunction = node.element; | |
10559 super.visitConstructorDeclaration(node); | |
10560 } finally { | |
10561 _enclosingFunction = outerFunction; | |
10562 } | |
10563 ConstructorElementImpl constructor = node.element; | |
10564 constructor.constantInitializers = | |
10565 new ConstantAstCloner().cloneNodeList(node.initializers); | |
10566 return null; | |
10567 } | |
10568 | |
10569 @override | |
10570 Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | |
10571 // | |
10572 // We visit the expression, but do not visit the field name because it needs | |
10573 // to be visited in the context of the constructor field initializer node. | |
10574 // | |
10575 safelyVisit(node.expression); | |
10576 node.accept(elementResolver); | |
10577 node.accept(typeAnalyzer); | |
10578 return null; | |
10579 } | |
10580 | |
10581 @override | |
10582 Object visitConstructorName(ConstructorName node) { | |
10583 // | |
10584 // We do not visit either the type name, because it won't be visited anyway, | |
10585 // or the name, because it needs to be visited in the context of the | |
10586 // constructor name. | |
10587 // | |
10588 node.accept(elementResolver); | |
10589 node.accept(typeAnalyzer); | |
10590 return null; | |
10591 } | |
10592 | |
10593 @override | |
10594 Object visitContinueStatement(ContinueStatement node) { | |
10595 // | |
10596 // We do not visit the label because it needs to be visited in the context | |
10597 // of the statement. | |
10598 // | |
10599 node.accept(elementResolver); | |
10600 node.accept(typeAnalyzer); | |
10601 return null; | |
10602 } | |
10603 | |
10604 @override | |
10605 Object visitDefaultFormalParameter(DefaultFormalParameter node) { | |
10606 super.visitDefaultFormalParameter(node); | |
10607 FormalParameterList parent = node.parent; | |
10608 AstNode grandparent = parent.parent; | |
10609 if (grandparent is ConstructorDeclaration && | |
10610 grandparent.constKeyword != null) { | |
10611 // For const constructors, we need to clone the ASTs for default formal | |
10612 // parameters, so that we can use them during constant evaluation. | |
10613 ParameterElement element = node.element; | |
10614 (element as ConstVariableElement).constantInitializer = | |
10615 new ConstantAstCloner().cloneNode(node.defaultValue); | |
10616 } | |
10617 return null; | |
10618 } | |
10619 | |
10620 @override | |
10621 Object visitDoStatement(DoStatement node) { | |
10622 _overrideManager.enterScope(); | |
10623 try { | |
10624 super.visitDoStatement(node); | |
10625 } finally { | |
10626 _overrideManager.exitScope(); | |
10627 } | |
10628 // TODO(brianwilkerson) If the loop can only be exited because the condition | |
10629 // is false, then propagateFalseState(node.getCondition()); | |
10630 return null; | |
10631 } | |
10632 | |
10633 @override | |
10634 Object visitEmptyFunctionBody(EmptyFunctionBody node) { | |
10635 safelyVisit(_commentBeforeFunction); | |
10636 if (resolveOnlyCommentInFunctionBody) { | |
10637 return null; | |
10638 } | |
10639 return super.visitEmptyFunctionBody(node); | |
10640 } | |
10641 | |
10642 @override | |
10643 Object visitEnumDeclaration(EnumDeclaration node) { | |
10644 // | |
10645 // Resolve the metadata in the library scope | |
10646 // and associate the annotations with the element. | |
10647 // | |
10648 if (node.metadata != null) { | |
10649 node.metadata.accept(this); | |
10650 ElementResolver.setMetadata(node.element, node); | |
10651 } | |
10652 // | |
10653 // There is nothing else to do because everything else was resolved by the | |
10654 // element builder. | |
10655 // | |
10656 return null; | |
10657 } | |
10658 | |
10659 @override | |
10660 Object visitExpressionFunctionBody(ExpressionFunctionBody node) { | |
10661 safelyVisit(_commentBeforeFunction); | |
10662 if (resolveOnlyCommentInFunctionBody) { | |
10663 return null; | |
10664 } | |
10665 _overrideManager.enterScope(); | |
10666 try { | |
10667 super.visitExpressionFunctionBody(node); | |
10668 } finally { | |
10669 _overrideManager.exitScope(); | |
10670 } | |
10671 return null; | |
10672 } | |
10673 | |
10674 @override | |
10675 Object visitFieldDeclaration(FieldDeclaration node) { | |
10676 _overrideManager.enterScope(); | |
10677 try { | |
10678 super.visitFieldDeclaration(node); | |
10679 } finally { | |
10680 Map<VariableElement, DartType> overrides = | |
10681 _overrideManager.captureOverrides(node.fields); | |
10682 _overrideManager.exitScope(); | |
10683 _overrideManager.applyOverrides(overrides); | |
10684 } | |
10685 return null; | |
10686 } | |
10687 | |
10688 @override | |
10689 Object visitForEachStatement(ForEachStatement node) { | |
10690 _overrideManager.enterScope(); | |
10691 try { | |
10692 super.visitForEachStatement(node); | |
10693 } finally { | |
10694 _overrideManager.exitScope(); | |
10695 } | |
10696 return null; | |
10697 } | |
10698 | |
10699 @override | |
10700 void visitForEachStatementInScope(ForEachStatement node) { | |
10701 // | |
10702 // We visit the iterator before the loop variable because the loop variable | |
10703 // cannot be in scope while visiting the iterator. | |
10704 // | |
10705 Expression iterable = node.iterable; | |
10706 safelyVisit(iterable); | |
10707 DeclaredIdentifier loopVariable = node.loopVariable; | |
10708 SimpleIdentifier identifier = node.identifier; | |
10709 safelyVisit(loopVariable); | |
10710 safelyVisit(identifier); | |
10711 Statement body = node.body; | |
10712 if (body != null) { | |
10713 _overrideManager.enterScope(); | |
10714 try { | |
10715 if (loopVariable != null && iterable != null) { | |
10716 LocalVariableElement loopElement = loopVariable.element; | |
10717 if (loopElement != null) { | |
10718 DartType iteratorElementType = _getIteratorElementType(iterable); | |
10719 overrideVariable(loopElement, iteratorElementType, true); | |
10720 _recordPropagatedType(loopVariable.identifier, iteratorElementType); | |
10721 } | |
10722 } else if (identifier != null && iterable != null) { | |
10723 Element identifierElement = identifier.staticElement; | |
10724 if (identifierElement is VariableElement) { | |
10725 DartType iteratorElementType = _getIteratorElementType(iterable); | |
10726 overrideVariable(identifierElement, iteratorElementType, true); | |
10727 _recordPropagatedType(identifier, iteratorElementType); | |
10728 } | |
10729 } | |
10730 visitStatementInScope(body); | |
10731 } finally { | |
10732 _overrideManager.exitScope(); | |
10733 } | |
10734 } | |
10735 node.accept(elementResolver); | |
10736 node.accept(typeAnalyzer); | |
10737 } | |
10738 | |
10739 @override | |
10740 Object visitForStatement(ForStatement node) { | |
10741 _overrideManager.enterScope(); | |
10742 try { | |
10743 super.visitForStatement(node); | |
10744 } finally { | |
10745 _overrideManager.exitScope(); | |
10746 } | |
10747 return null; | |
10748 } | |
10749 | |
10750 @override | |
10751 void visitForStatementInScope(ForStatement node) { | |
10752 safelyVisit(node.variables); | |
10753 safelyVisit(node.initialization); | |
10754 safelyVisit(node.condition); | |
10755 _overrideManager.enterScope(); | |
10756 try { | |
10757 _propagateTrueState(node.condition); | |
10758 visitStatementInScope(node.body); | |
10759 node.updaters.accept(this); | |
10760 } finally { | |
10761 _overrideManager.exitScope(); | |
10762 } | |
10763 // TODO(brianwilkerson) If the loop can only be exited because the condition | |
10764 // is false, then propagateFalseState(condition); | |
10765 } | |
10766 | |
10767 @override | |
10768 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
10769 ExecutableElement outerFunction = _enclosingFunction; | |
10770 try { | |
10771 SimpleIdentifier functionName = node.name; | |
10772 _enclosingFunction = functionName.staticElement as ExecutableElement; | |
10773 super.visitFunctionDeclaration(node); | |
10774 } finally { | |
10775 _enclosingFunction = outerFunction; | |
10776 } | |
10777 return null; | |
10778 } | |
10779 | |
10780 @override | |
10781 Object visitFunctionExpression(FunctionExpression node) { | |
10782 ExecutableElement outerFunction = _enclosingFunction; | |
10783 try { | |
10784 _enclosingFunction = node.element; | |
10785 _overrideManager.enterScope(); | |
10786 try { | |
10787 super.visitFunctionExpression(node); | |
10788 } finally { | |
10789 _overrideManager.exitScope(); | |
10790 } | |
10791 } finally { | |
10792 _enclosingFunction = outerFunction; | |
10793 } | |
10794 return null; | |
10795 } | |
10796 | |
10797 @override | |
10798 Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | |
10799 safelyVisit(node.function); | |
10800 node.accept(elementResolver); | |
10801 _inferFunctionExpressionsParametersTypes(node.argumentList); | |
10802 safelyVisit(node.argumentList); | |
10803 node.accept(typeAnalyzer); | |
10804 return null; | |
10805 } | |
10806 | |
10807 @override | |
10808 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
10809 // Resolve the metadata in the library scope. | |
10810 if (node.metadata != null) { | |
10811 node.metadata.accept(this); | |
10812 } | |
10813 FunctionTypeAlias outerAlias = _enclosingFunctionTypeAlias; | |
10814 _enclosingFunctionTypeAlias = node; | |
10815 try { | |
10816 super.visitFunctionTypeAlias(node); | |
10817 } finally { | |
10818 _enclosingFunctionTypeAlias = outerAlias; | |
10819 } | |
10820 return null; | |
10821 } | |
10822 | |
10823 @override | |
10824 Object visitHideCombinator(HideCombinator node) => null; | |
10825 | |
10826 @override | |
10827 Object visitIfStatement(IfStatement node) { | |
10828 Expression condition = node.condition; | |
10829 safelyVisit(condition); | |
10830 Map<VariableElement, DartType> thenOverrides = | |
10831 new HashMap<VariableElement, DartType>(); | |
10832 Statement thenStatement = node.thenStatement; | |
10833 if (thenStatement != null) { | |
10834 _overrideManager.enterScope(); | |
10835 try { | |
10836 _promoteManager.enterScope(); | |
10837 try { | |
10838 _propagateTrueState(condition); | |
10839 // Type promotion. | |
10840 _promoteTypes(condition); | |
10841 _clearTypePromotionsIfPotentiallyMutatedIn(thenStatement); | |
10842 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( | |
10843 thenStatement); | |
10844 // Visit "then". | |
10845 visitStatementInScope(thenStatement); | |
10846 } finally { | |
10847 _promoteManager.exitScope(); | |
10848 } | |
10849 } finally { | |
10850 thenOverrides = _overrideManager.captureLocalOverrides(); | |
10851 _overrideManager.exitScope(); | |
10852 } | |
10853 } | |
10854 Map<VariableElement, DartType> elseOverrides = | |
10855 new HashMap<VariableElement, DartType>(); | |
10856 Statement elseStatement = node.elseStatement; | |
10857 if (elseStatement != null) { | |
10858 _overrideManager.enterScope(); | |
10859 try { | |
10860 _propagateFalseState(condition); | |
10861 visitStatementInScope(elseStatement); | |
10862 } finally { | |
10863 elseOverrides = _overrideManager.captureLocalOverrides(); | |
10864 _overrideManager.exitScope(); | |
10865 } | |
10866 } | |
10867 node.accept(elementResolver); | |
10868 node.accept(typeAnalyzer); | |
10869 // Join overrides. | |
10870 bool thenIsAbrupt = _isAbruptTerminationStatement(thenStatement); | |
10871 bool elseIsAbrupt = _isAbruptTerminationStatement(elseStatement); | |
10872 if (elseIsAbrupt && !thenIsAbrupt) { | |
10873 _propagateTrueState(condition); | |
10874 _overrideManager.applyOverrides(thenOverrides); | |
10875 } else if (thenIsAbrupt && !elseIsAbrupt) { | |
10876 _propagateFalseState(condition); | |
10877 _overrideManager.applyOverrides(elseOverrides); | |
10878 } else if (!thenIsAbrupt && !elseIsAbrupt) { | |
10879 List<Map<VariableElement, DartType>> perBranchOverrides = | |
10880 new List<Map<VariableElement, DartType>>(); | |
10881 perBranchOverrides.add(thenOverrides); | |
10882 perBranchOverrides.add(elseOverrides); | |
10883 _overrideManager.mergeOverrides(perBranchOverrides); | |
10884 } | |
10885 return null; | |
10886 } | |
10887 | |
10888 @override | |
10889 Object visitLabel(Label node) => null; | |
10890 | |
10891 @override | |
10892 Object visitLibraryIdentifier(LibraryIdentifier node) => null; | |
10893 | |
10894 @override | |
10895 Object visitMethodDeclaration(MethodDeclaration node) { | |
10896 ExecutableElement outerFunction = _enclosingFunction; | |
10897 try { | |
10898 _enclosingFunction = node.element; | |
10899 super.visitMethodDeclaration(node); | |
10900 } finally { | |
10901 _enclosingFunction = outerFunction; | |
10902 } | |
10903 return null; | |
10904 } | |
10905 | |
10906 @override | |
10907 Object visitMethodInvocation(MethodInvocation node) { | |
10908 // | |
10909 // We visit the target and argument list, but do not visit the method name | |
10910 // because it needs to be visited in the context of the invocation. | |
10911 // | |
10912 safelyVisit(node.target); | |
10913 node.accept(elementResolver); | |
10914 _inferFunctionExpressionsParametersTypes(node.argumentList); | |
10915 safelyVisit(node.argumentList); | |
10916 node.accept(typeAnalyzer); | |
10917 return null; | |
10918 } | |
10919 | |
10920 @override | |
10921 Object visitNode(AstNode node) { | |
10922 node.visitChildren(this); | |
10923 node.accept(elementResolver); | |
10924 node.accept(typeAnalyzer); | |
10925 return null; | |
10926 } | |
10927 | |
10928 @override | |
10929 Object visitPrefixedIdentifier(PrefixedIdentifier node) { | |
10930 // | |
10931 // We visit the prefix, but do not visit the identifier because it needs to | |
10932 // be visited in the context of the prefix. | |
10933 // | |
10934 safelyVisit(node.prefix); | |
10935 node.accept(elementResolver); | |
10936 node.accept(typeAnalyzer); | |
10937 return null; | |
10938 } | |
10939 | |
10940 @override | |
10941 Object visitPropertyAccess(PropertyAccess node) { | |
10942 // | |
10943 // We visit the target, but do not visit the property name because it needs | |
10944 // to be visited in the context of the property access node. | |
10945 // | |
10946 safelyVisit(node.target); | |
10947 node.accept(elementResolver); | |
10948 node.accept(typeAnalyzer); | |
10949 return null; | |
10950 } | |
10951 | |
10952 @override | |
10953 Object visitRedirectingConstructorInvocation( | |
10954 RedirectingConstructorInvocation node) { | |
10955 // | |
10956 // We visit the argument list, but do not visit the optional identifier | |
10957 // because it needs to be visited in the context of the constructor | |
10958 // invocation. | |
10959 // | |
10960 safelyVisit(node.argumentList); | |
10961 node.accept(elementResolver); | |
10962 node.accept(typeAnalyzer); | |
10963 return null; | |
10964 } | |
10965 | |
10966 @override | |
10967 Object visitShowCombinator(ShowCombinator node) => null; | |
10968 | |
10969 @override | |
10970 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
10971 // | |
10972 // We visit the argument list, but do not visit the optional identifier | |
10973 // because it needs to be visited in the context of the constructor | |
10974 // invocation. | |
10975 // | |
10976 safelyVisit(node.argumentList); | |
10977 node.accept(elementResolver); | |
10978 node.accept(typeAnalyzer); | |
10979 return null; | |
10980 } | |
10981 | |
10982 @override | |
10983 Object visitSwitchCase(SwitchCase node) { | |
10984 _overrideManager.enterScope(); | |
10985 try { | |
10986 super.visitSwitchCase(node); | |
10987 } finally { | |
10988 _overrideManager.exitScope(); | |
10989 } | |
10990 return null; | |
10991 } | |
10992 | |
10993 @override | |
10994 Object visitSwitchDefault(SwitchDefault node) { | |
10995 _overrideManager.enterScope(); | |
10996 try { | |
10997 super.visitSwitchDefault(node); | |
10998 } finally { | |
10999 _overrideManager.exitScope(); | |
11000 } | |
11001 return null; | |
11002 } | |
11003 | |
11004 @override | |
11005 Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
11006 _overrideManager.enterScope(); | |
11007 try { | |
11008 super.visitTopLevelVariableDeclaration(node); | |
11009 } finally { | |
11010 Map<VariableElement, DartType> overrides = | |
11011 _overrideManager.captureOverrides(node.variables); | |
11012 _overrideManager.exitScope(); | |
11013 _overrideManager.applyOverrides(overrides); | |
11014 } | |
11015 return null; | |
11016 } | |
11017 | |
11018 @override | |
11019 Object visitTypeName(TypeName node) => null; | |
11020 | |
11021 @override | |
11022 Object visitVariableDeclaration(VariableDeclaration node) { | |
11023 super.visitVariableDeclaration(node); | |
11024 VariableElement element = node.element; | |
11025 // Note: in addition to cloning the initializers for const variables, we | |
11026 // have to clone the initializers for non-static final fields (because if | |
11027 // they occur in a class with a const constructor, they will be needed to | |
11028 // evaluate the const constructor). | |
11029 if ((element.isConst || | |
11030 (element is FieldElement && | |
11031 element.isFinal && | |
11032 !element.isStatic)) && | |
11033 node.initializer != null) { | |
11034 (element as ConstVariableElement).constantInitializer = | |
11035 new ConstantAstCloner().cloneNode(node.initializer); | |
11036 } | |
11037 return null; | |
11038 } | |
11039 | |
11040 @override | |
11041 Object visitWhileStatement(WhileStatement node) { | |
11042 // Note: since we don't call the base class, we have to maintain | |
11043 // _implicitLabelScope ourselves. | |
11044 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
11045 try { | |
11046 _implicitLabelScope = _implicitLabelScope.nest(node); | |
11047 Expression condition = node.condition; | |
11048 safelyVisit(condition); | |
11049 Statement body = node.body; | |
11050 if (body != null) { | |
11051 _overrideManager.enterScope(); | |
11052 try { | |
11053 _propagateTrueState(condition); | |
11054 visitStatementInScope(body); | |
11055 } finally { | |
11056 _overrideManager.exitScope(); | |
11057 } | |
11058 } | |
11059 } finally { | |
11060 _implicitLabelScope = outerImplicitScope; | |
11061 } | |
11062 // TODO(brianwilkerson) If the loop can only be exited because the condition | |
11063 // is false, then propagateFalseState(condition); | |
11064 node.accept(elementResolver); | |
11065 node.accept(typeAnalyzer); | |
11066 return null; | |
11067 } | |
11068 | |
11069 /** | |
11070 * Checks each promoted variable in the current scope for compliance with the
following | |
11071 * specification statement: | |
11072 * | |
11073 * If the variable <i>v</i> is accessed by a closure in <i>s<sub>1</sub></i> t
hen the variable | |
11074 * <i>v</i> is not potentially mutated anywhere in the scope of <i>v</i>. | |
11075 */ | |
11076 void _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( | |
11077 AstNode target) { | |
11078 for (Element element in _promoteManager.promotedElements) { | |
11079 if ((element as VariableElementImpl).isPotentiallyMutatedInScope) { | |
11080 if (_isVariableAccessedInClosure(element, target)) { | |
11081 _promoteManager.setType(element, null); | |
11082 } | |
11083 } | |
11084 } | |
11085 } | |
11086 | |
11087 /** | |
11088 * Checks each promoted variable in the current scope for compliance with the
following | |
11089 * specification statement: | |
11090 * | |
11091 * <i>v</i> is not potentially mutated in <i>s<sub>1</sub></i> or within a clo
sure. | |
11092 */ | |
11093 void _clearTypePromotionsIfPotentiallyMutatedIn(AstNode target) { | |
11094 for (Element element in _promoteManager.promotedElements) { | |
11095 if (_isVariablePotentiallyMutatedIn(element, target)) { | |
11096 _promoteManager.setType(element, null); | |
11097 } | |
11098 } | |
11099 } | |
11100 | |
11101 /** | |
11102 * The given expression is the expression used to compute the iterator for a f
or-each statement. | |
11103 * Attempt to compute the type of objects that will be assigned to the loop va
riable and return | |
11104 * that type. Return `null` if the type could not be determined. | |
11105 * | |
11106 * @param iterator the iterator for a for-each statement | |
11107 * @return the type of objects that will be assigned to the loop variable | |
11108 */ | |
11109 DartType _getIteratorElementType(Expression iteratorExpression) { | |
11110 DartType expressionType = iteratorExpression.bestType; | |
11111 if (expressionType is InterfaceType) { | |
11112 InterfaceType interfaceType = expressionType; | |
11113 FunctionType iteratorFunction = | |
11114 _inheritanceManager.lookupMemberType(interfaceType, "iterator"); | |
11115 if (iteratorFunction == null) { | |
11116 // TODO(brianwilkerson) Should we report this error? | |
11117 return null; | |
11118 } | |
11119 DartType iteratorType = iteratorFunction.returnType; | |
11120 if (iteratorType is InterfaceType) { | |
11121 InterfaceType iteratorInterfaceType = iteratorType; | |
11122 FunctionType currentFunction = _inheritanceManager.lookupMemberType( | |
11123 iteratorInterfaceType, "current"); | |
11124 if (currentFunction == null) { | |
11125 // TODO(brianwilkerson) Should we report this error? | |
11126 return null; | |
11127 } | |
11128 return currentFunction.returnType; | |
11129 } | |
11130 } | |
11131 return null; | |
11132 } | |
11133 | |
11134 /** | |
11135 * If given "mayBeClosure" is [FunctionExpression] without explicit parameters
types and its | |
11136 * required type is [FunctionType], then infer parameters types from [Function
Type]. | |
11137 */ | |
11138 void _inferFunctionExpressionParametersTypes( | |
11139 Expression mayBeClosure, DartType mayByFunctionType) { | |
11140 // prepare closure | |
11141 if (mayBeClosure is! FunctionExpression) { | |
11142 return; | |
11143 } | |
11144 FunctionExpression closure = mayBeClosure as FunctionExpression; | |
11145 // prepare expected closure type | |
11146 if (mayByFunctionType is! FunctionType) { | |
11147 return; | |
11148 } | |
11149 FunctionType expectedClosureType = mayByFunctionType as FunctionType; | |
11150 // If the expectedClosureType is not more specific than the static type, | |
11151 // return. | |
11152 DartType staticClosureType = | |
11153 (closure.element != null ? closure.element.type : null) as DartType; | |
11154 if (staticClosureType != null && | |
11155 !expectedClosureType.isMoreSpecificThan(staticClosureType)) { | |
11156 return; | |
11157 } | |
11158 // set propagated type for the closure | |
11159 closure.propagatedType = expectedClosureType; | |
11160 // set inferred types for parameters | |
11161 NodeList<FormalParameter> parameters = closure.parameters.parameters; | |
11162 List<ParameterElement> expectedParameters = expectedClosureType.parameters; | |
11163 for (int i = 0; | |
11164 i < parameters.length && i < expectedParameters.length; | |
11165 i++) { | |
11166 FormalParameter parameter = parameters[i]; | |
11167 ParameterElement element = parameter.element; | |
11168 DartType currentType = _overrideManager.getBestType(element); | |
11169 // may be override the type | |
11170 DartType expectedType = expectedParameters[i].type; | |
11171 if (currentType == null || expectedType.isMoreSpecificThan(currentType)) { | |
11172 _overrideManager.setType(element, expectedType); | |
11173 } | |
11174 } | |
11175 } | |
11176 | |
11177 /** | |
11178 * Try to infer types of parameters of the [FunctionExpression] arguments. | |
11179 */ | |
11180 void _inferFunctionExpressionsParametersTypes(ArgumentList argumentList) { | |
11181 for (Expression argument in argumentList.arguments) { | |
11182 ParameterElement parameter = argument.propagatedParameterElement; | |
11183 if (parameter == null) { | |
11184 parameter = argument.staticParameterElement; | |
11185 } | |
11186 if (parameter != null) { | |
11187 _inferFunctionExpressionParametersTypes(argument, parameter.type); | |
11188 } | |
11189 } | |
11190 } | |
11191 | |
11192 /** | |
11193 * Return `true` if the given expression terminates abruptly (that is, if any
expression | |
11194 * following the given expression will not be reached). | |
11195 * | |
11196 * @param expression the expression being tested | |
11197 * @return `true` if the given expression terminates abruptly | |
11198 */ | |
11199 bool _isAbruptTerminationExpression(Expression expression) { | |
11200 // TODO(brianwilkerson) This needs to be significantly improved. Ideally we | |
11201 // would eventually turn this into a method on Expression that returns a | |
11202 // termination indication (normal, abrupt with no exception, abrupt with an | |
11203 // exception). | |
11204 while (expression is ParenthesizedExpression) { | |
11205 expression = (expression as ParenthesizedExpression).expression; | |
11206 } | |
11207 return expression is ThrowExpression || expression is RethrowExpression; | |
11208 } | |
11209 | |
11210 /** | |
11211 * Return `true` if the given statement terminates abruptly (that is, if any s
tatement | |
11212 * following the given statement will not be reached). | |
11213 * | |
11214 * @param statement the statement being tested | |
11215 * @return `true` if the given statement terminates abruptly | |
11216 */ | |
11217 bool _isAbruptTerminationStatement(Statement statement) { | |
11218 // TODO(brianwilkerson) This needs to be significantly improved. Ideally we | |
11219 // would eventually turn this into a method on Statement that returns a | |
11220 // termination indication (normal, abrupt with no exception, abrupt with an | |
11221 // exception). | |
11222 // | |
11223 // collinsn: it is unsound to assume that [break] and [continue] are | |
11224 // "abrupt". See: https://code.google.com/p/dart/issues/detail?id=19929#c4 | |
11225 // (tests are included in TypePropagationTest.java). | |
11226 // In general, the difficulty is loopy control flow. | |
11227 // | |
11228 // In the presence of exceptions things become much more complicated, but | |
11229 // while we only use this to propagate at [if]-statement join points, | |
11230 // checking for [return] may work well enough in the common case. | |
11231 if (statement is ReturnStatement) { | |
11232 return true; | |
11233 } else if (statement is ExpressionStatement) { | |
11234 return _isAbruptTerminationExpression(statement.expression); | |
11235 } else if (statement is Block) { | |
11236 NodeList<Statement> statements = statement.statements; | |
11237 int size = statements.length; | |
11238 if (size == 0) { | |
11239 return false; | |
11240 } | |
11241 | |
11242 // This last-statement-is-return heuristic is unsound for adversarial | |
11243 // code, but probably works well in the common case: | |
11244 // | |
11245 // var x = 123; | |
11246 // var c = true; | |
11247 // L: if (c) { | |
11248 // x = "hello"; | |
11249 // c = false; | |
11250 // break L; | |
11251 // return; | |
11252 // } | |
11253 // print(x); | |
11254 // | |
11255 // Unsound to assume that [x = "hello";] never executed after the | |
11256 // if-statement. Of course, a dead-code analysis could point out that | |
11257 // [return] here is dead. | |
11258 return _isAbruptTerminationStatement(statements[size - 1]); | |
11259 } | |
11260 return false; | |
11261 } | |
11262 | |
11263 /** | |
11264 * Return `true` if the given variable is accessed within a closure in the giv
en | |
11265 * [AstNode] and also mutated somewhere in variable scope. This information is
only | |
11266 * available for local variables (including parameters). | |
11267 * | |
11268 * @param variable the variable to check | |
11269 * @param target the [AstNode] to check within | |
11270 * @return `true` if this variable is potentially mutated somewhere in the giv
en ASTNode | |
11271 */ | |
11272 bool _isVariableAccessedInClosure(Element variable, AstNode target) { | |
11273 _ResolverVisitor_isVariableAccessedInClosure visitor = | |
11274 new _ResolverVisitor_isVariableAccessedInClosure(variable); | |
11275 target.accept(visitor); | |
11276 return visitor.result; | |
11277 } | |
11278 | |
11279 /** | |
11280 * Return `true` if the given variable is potentially mutated somewhere in the
given | |
11281 * [AstNode]. This information is only available for local variables (includin
g parameters). | |
11282 * | |
11283 * @param variable the variable to check | |
11284 * @param target the [AstNode] to check within | |
11285 * @return `true` if this variable is potentially mutated somewhere in the giv
en ASTNode | |
11286 */ | |
11287 bool _isVariablePotentiallyMutatedIn(Element variable, AstNode target) { | |
11288 _ResolverVisitor_isVariablePotentiallyMutatedIn visitor = | |
11289 new _ResolverVisitor_isVariablePotentiallyMutatedIn(variable); | |
11290 target.accept(visitor); | |
11291 return visitor.result; | |
11292 } | |
11293 | |
11294 /** | |
11295 * If it is appropriate to do so, promotes the current type of the static elem
ent associated with | |
11296 * the given expression with the given type. Generally speaking, it is appropr
iate if the given | |
11297 * type is more specific than the current type. | |
11298 * | |
11299 * @param expression the expression used to access the static element whose ty
pes might be | |
11300 * promoted | |
11301 * @param potentialType the potential type of the elements | |
11302 */ | |
11303 void _promote(Expression expression, DartType potentialType) { | |
11304 VariableElement element = getPromotionStaticElement(expression); | |
11305 if (element != null) { | |
11306 // may be mutated somewhere in closure | |
11307 if (element.isPotentiallyMutatedInClosure) { | |
11308 return; | |
11309 } | |
11310 // prepare current variable type | |
11311 DartType type = _promoteManager.getType(element); | |
11312 if (type == null) { | |
11313 type = expression.staticType; | |
11314 } | |
11315 // Declared type should not be "dynamic". | |
11316 if (type == null || type.isDynamic) { | |
11317 return; | |
11318 } | |
11319 // Promoted type should not be "dynamic". | |
11320 if (potentialType == null || potentialType.isDynamic) { | |
11321 return; | |
11322 } | |
11323 // Promoted type should be more specific than declared. | |
11324 if (!potentialType.isMoreSpecificThan(type)) { | |
11325 return; | |
11326 } | |
11327 // Do promote type of variable. | |
11328 _promoteManager.setType(element, potentialType); | |
11329 } | |
11330 } | |
11331 | |
11332 /** | |
11333 * Promotes type information using given condition. | |
11334 */ | |
11335 void _promoteTypes(Expression condition) { | |
11336 if (condition is BinaryExpression) { | |
11337 BinaryExpression binary = condition; | |
11338 if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) { | |
11339 Expression left = binary.leftOperand; | |
11340 Expression right = binary.rightOperand; | |
11341 _promoteTypes(left); | |
11342 _promoteTypes(right); | |
11343 _clearTypePromotionsIfPotentiallyMutatedIn(right); | |
11344 } | |
11345 } else if (condition is IsExpression) { | |
11346 IsExpression is2 = condition; | |
11347 if (is2.notOperator == null) { | |
11348 _promote(is2.expression, is2.type.type); | |
11349 } | |
11350 } else if (condition is ParenthesizedExpression) { | |
11351 _promoteTypes(condition.expression); | |
11352 } | |
11353 } | |
11354 | |
11355 /** | |
11356 * Propagate any type information that results from knowing that the given con
dition will have | |
11357 * been evaluated to 'false'. | |
11358 * | |
11359 * @param condition the condition that will have evaluated to 'false' | |
11360 */ | |
11361 void _propagateFalseState(Expression condition) { | |
11362 if (condition is BinaryExpression) { | |
11363 BinaryExpression binary = condition; | |
11364 if (binary.operator.type == sc.TokenType.BAR_BAR) { | |
11365 _propagateFalseState(binary.leftOperand); | |
11366 _propagateFalseState(binary.rightOperand); | |
11367 } | |
11368 } else if (condition is IsExpression) { | |
11369 IsExpression is2 = condition; | |
11370 if (is2.notOperator != null) { | |
11371 // Since an is-statement doesn't actually change the type, we don't | |
11372 // let it affect the propagated type when it would result in a loss | |
11373 // of precision. | |
11374 overrideExpression(is2.expression, is2.type.type, false, false); | |
11375 } | |
11376 } else if (condition is PrefixExpression) { | |
11377 PrefixExpression prefix = condition; | |
11378 if (prefix.operator.type == sc.TokenType.BANG) { | |
11379 _propagateTrueState(prefix.operand); | |
11380 } | |
11381 } else if (condition is ParenthesizedExpression) { | |
11382 _propagateFalseState(condition.expression); | |
11383 } | |
11384 } | |
11385 | |
11386 /** | |
11387 * Propagate any type information that results from knowing that the given exp
ression will have | |
11388 * been evaluated without altering the flow of execution. | |
11389 * | |
11390 * @param expression the expression that will have been evaluated | |
11391 */ | |
11392 void _propagateState(Expression expression) { | |
11393 // TODO(brianwilkerson) Implement this. | |
11394 } | |
11395 | |
11396 /** | |
11397 * Propagate any type information that results from knowing that the given con
dition will have | |
11398 * been evaluated to 'true'. | |
11399 * | |
11400 * @param condition the condition that will have evaluated to 'true' | |
11401 */ | |
11402 void _propagateTrueState(Expression condition) { | |
11403 if (condition is BinaryExpression) { | |
11404 BinaryExpression binary = condition; | |
11405 if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) { | |
11406 _propagateTrueState(binary.leftOperand); | |
11407 _propagateTrueState(binary.rightOperand); | |
11408 } | |
11409 } else if (condition is IsExpression) { | |
11410 IsExpression is2 = condition; | |
11411 if (is2.notOperator == null) { | |
11412 // Since an is-statement doesn't actually change the type, we don't | |
11413 // let it affect the propagated type when it would result in a loss | |
11414 // of precision. | |
11415 overrideExpression(is2.expression, is2.type.type, false, false); | |
11416 } | |
11417 } else if (condition is PrefixExpression) { | |
11418 PrefixExpression prefix = condition; | |
11419 if (prefix.operator.type == sc.TokenType.BANG) { | |
11420 _propagateFalseState(prefix.operand); | |
11421 } | |
11422 } else if (condition is ParenthesizedExpression) { | |
11423 _propagateTrueState(condition.expression); | |
11424 } | |
11425 } | |
11426 | |
11427 /** | |
11428 * Record that the propagated type of the given node is the given type. | |
11429 * | |
11430 * @param expression the node whose type is to be recorded | |
11431 * @param type the propagated type of the node | |
11432 */ | |
11433 void _recordPropagatedType(Expression expression, DartType type) { | |
11434 if (type != null && !type.isDynamic) { | |
11435 expression.propagatedType = type; | |
11436 } | |
11437 } | |
11438 } | |
11439 | |
11440 /** | |
11441 * The abstract class `Scope` defines the behavior common to name scopes used by
the resolver | |
11442 * to determine which names are visible at any given point in the code. | |
11443 */ | |
11444 abstract class Scope { | |
11445 /** | |
11446 * The prefix used to mark an identifier as being private to its library. | |
11447 */ | |
11448 static int PRIVATE_NAME_PREFIX = 0x5F; | |
11449 | |
11450 /** | |
11451 * The suffix added to the declared name of a setter when looking up the sette
r. Used to | |
11452 * disambiguate between a getter and a setter that have the same name. | |
11453 */ | |
11454 static String SETTER_SUFFIX = "="; | |
11455 | |
11456 /** | |
11457 * The name used to look up the method used to implement the unary minus opera
tor. Used to | |
11458 * disambiguate between the unary and binary operators. | |
11459 */ | |
11460 static String UNARY_MINUS = "unary-"; | |
11461 | |
11462 /** | |
11463 * A table mapping names that are defined in this scope to the element represe
nting the thing | |
11464 * declared with that name. | |
11465 */ | |
11466 HashMap<String, Element> _definedNames = new HashMap<String, Element>(); | |
11467 | |
11468 /** | |
11469 * A flag indicating whether there are any names defined in this scope. | |
11470 */ | |
11471 bool _hasName = false; | |
11472 | |
11473 /** | |
11474 * Return the scope in which this scope is lexically enclosed. | |
11475 * | |
11476 * @return the scope in which this scope is lexically enclosed | |
11477 */ | |
11478 Scope get enclosingScope => null; | |
11479 | |
11480 /** | |
11481 * Return the listener that is to be informed when an error is encountered. | |
11482 * | |
11483 * @return the listener that is to be informed when an error is encountered | |
11484 */ | |
11485 AnalysisErrorListener get errorListener; | |
11486 | |
11487 /** | |
11488 * Add the given element to this scope. If there is already an element with th
e given name defined | |
11489 * in this scope, then an error will be generated and the original element wil
l continue to be | |
11490 * mapped to the name. If there is an element with the given name in an enclos
ing scope, then a | |
11491 * warning will be generated but the given element will hide the inherited ele
ment. | |
11492 * | |
11493 * @param element the element to be added to this scope | |
11494 */ | |
11495 void define(Element element) { | |
11496 String name = _getName(element); | |
11497 if (name != null && !name.isEmpty) { | |
11498 if (_definedNames.containsKey(name)) { | |
11499 errorListener | |
11500 .onError(getErrorForDuplicate(_definedNames[name], element)); | |
11501 } else { | |
11502 _definedNames[name] = element; | |
11503 _hasName = true; | |
11504 } | |
11505 } | |
11506 } | |
11507 | |
11508 /** | |
11509 * Add the given element to this scope without checking for duplication or hid
ing. | |
11510 * | |
11511 * @param name the name of the element to be added | |
11512 * @param element the element to be added to this scope | |
11513 */ | |
11514 void defineNameWithoutChecking(String name, Element element) { | |
11515 _definedNames[name] = element; | |
11516 _hasName = true; | |
11517 } | |
11518 | |
11519 /** | |
11520 * Add the given element to this scope without checking for duplication or hid
ing. | |
11521 * | |
11522 * @param element the element to be added to this scope | |
11523 */ | |
11524 void defineWithoutChecking(Element element) { | |
11525 _definedNames[_getName(element)] = element; | |
11526 _hasName = true; | |
11527 } | |
11528 | |
11529 /** | |
11530 * Return the error code to be used when reporting that a name being defined l
ocally conflicts | |
11531 * with another element of the same name in the local scope. | |
11532 * | |
11533 * @param existing the first element to be declared with the conflicting name | |
11534 * @param duplicate another element declared with the conflicting name | |
11535 * @return the error code used to report duplicate names within a scope | |
11536 */ | |
11537 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { | |
11538 // TODO(brianwilkerson) Customize the error message based on the types of | |
11539 // elements that share the same name. | |
11540 // TODO(jwren) There are 4 error codes for duplicate, but only 1 is being | |
11541 // generated. | |
11542 Source source = duplicate.source; | |
11543 return new AnalysisError(source, duplicate.nameOffset, | |
11544 duplicate.displayName.length, CompileTimeErrorCode.DUPLICATE_DEFINITION, | |
11545 [existing.displayName]); | |
11546 } | |
11547 | |
11548 /** | |
11549 * Return the source that contains the given identifier, or the source associa
ted with this scope | |
11550 * if the source containing the identifier could not be determined. | |
11551 * | |
11552 * @param identifier the identifier whose source is to be returned | |
11553 * @return the source that contains the given identifier | |
11554 */ | |
11555 Source getSource(AstNode node) { | |
11556 CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit); | |
11557 if (unit != null) { | |
11558 CompilationUnitElement unitElement = unit.element; | |
11559 if (unitElement != null) { | |
11560 return unitElement.source; | |
11561 } | |
11562 } | |
11563 return null; | |
11564 } | |
11565 | |
11566 /** | |
11567 * Return the element with which the given name is associated, or `null` if th
e name is not | |
11568 * defined within this scope. | |
11569 * | |
11570 * @param identifier the identifier node to lookup element for, used to report
correct kind of a | |
11571 * problem and associate problem with | |
11572 * @param name the name associated with the element to be returned | |
11573 * @param referencingLibrary the library that contains the reference to the na
me, used to | |
11574 * implement library-level privacy | |
11575 * @return the element with which the given name is associated | |
11576 */ | |
11577 Element internalLookup( | |
11578 Identifier identifier, String name, LibraryElement referencingLibrary); | |
11579 | |
11580 /** | |
11581 * Return the element with which the given name is associated, or `null` if th
e name is not | |
11582 * defined within this scope. This method only returns elements that are direc
tly defined within | |
11583 * this scope, not elements that are defined in an enclosing scope. | |
11584 * | |
11585 * @param name the name associated with the element to be returned | |
11586 * @param referencingLibrary the library that contains the reference to the na
me, used to | |
11587 * implement library-level privacy | |
11588 * @return the element with which the given name is associated | |
11589 */ | |
11590 Element localLookup(String name, LibraryElement referencingLibrary) { | |
11591 if (_hasName) { | |
11592 return _definedNames[name]; | |
11593 } | |
11594 return null; | |
11595 } | |
11596 | |
11597 /** | |
11598 * Return the element with which the given identifier is associated, or `null`
if the name | |
11599 * is not defined within this scope. | |
11600 * | |
11601 * @param identifier the identifier associated with the element to be returned | |
11602 * @param referencingLibrary the library that contains the reference to the na
me, used to | |
11603 * implement library-level privacy | |
11604 * @return the element with which the given identifier is associated | |
11605 */ | |
11606 Element lookup(Identifier identifier, LibraryElement referencingLibrary) => | |
11607 internalLookup(identifier, identifier.name, referencingLibrary); | |
11608 | |
11609 /** | |
11610 * Return the name that will be used to look up the given element. | |
11611 * | |
11612 * @param element the element whose look-up name is to be returned | |
11613 * @return the name that will be used to look up the given element | |
11614 */ | |
11615 String _getName(Element element) { | |
11616 if (element is MethodElement) { | |
11617 MethodElement method = element; | |
11618 if (method.name == "-" && method.parameters.length == 0) { | |
11619 return UNARY_MINUS; | |
11620 } | |
11621 } | |
11622 return element.name; | |
11623 } | |
11624 | |
11625 /** | |
11626 * Return `true` if the given name is a library-private name. | |
11627 * | |
11628 * @param name the name being tested | |
11629 * @return `true` if the given name is a library-private name | |
11630 */ | |
11631 static bool isPrivateName(String name) => | |
11632 name != null && StringUtilities.startsWithChar(name, PRIVATE_NAME_PREFIX); | |
11633 } | |
11634 | |
11635 /** | |
11636 * The abstract class `ScopedVisitor` maintains name and label scopes as an AST
structure is | |
11637 * being visited. | |
11638 */ | |
11639 abstract class ScopedVisitor extends UnifyingAstVisitor<Object> { | |
11640 /** | |
11641 * The element for the library containing the compilation unit being visited. | |
11642 */ | |
11643 LibraryElement _definingLibrary; | |
11644 | |
11645 /** | |
11646 * The source representing the compilation unit being visited. | |
11647 */ | |
11648 final Source source; | |
11649 | |
11650 /** | |
11651 * The error listener that will be informed of any errors that are found durin
g resolution. | |
11652 */ | |
11653 AnalysisErrorListener _errorListener; | |
11654 | |
11655 /** | |
11656 * The scope used to resolve identifiers. | |
11657 */ | |
11658 Scope nameScope; | |
11659 | |
11660 /** | |
11661 * The object used to access the types from the core library. | |
11662 */ | |
11663 final TypeProvider typeProvider; | |
11664 | |
11665 /** | |
11666 * The scope used to resolve unlabeled `break` and `continue` statements. | |
11667 */ | |
11668 ImplicitLabelScope _implicitLabelScope = ImplicitLabelScope.ROOT; | |
11669 | |
11670 /** | |
11671 * The scope used to resolve labels for `break` and `continue` statements, or | |
11672 * `null` if no labels have been defined in the current context. | |
11673 */ | |
11674 LabelScope labelScope; | |
11675 | |
11676 /** | |
11677 * The class containing the AST nodes being visited, | |
11678 * or `null` if we are not in the scope of a class. | |
11679 */ | |
11680 ClassElement enclosingClass; | |
11681 | |
11682 /** | |
11683 * Initialize a newly created visitor to resolve the nodes in a compilation | |
11684 * unit. | |
11685 * | |
11686 * [definingLibrary] is the element for the library containing the | |
11687 * compilation unit being visited. | |
11688 * [source] is the source representing the compilation unit being visited. | |
11689 * [typeProvider] is the object used to access the types from the core | |
11690 * library. | |
11691 * [errorListener] is the error listener that will be informed of any errors | |
11692 * that are found during resolution. | |
11693 * [nameScope] is the scope used to resolve identifiers in the node that will | |
11694 * first be visited. If `null` or unspecified, a new [LibraryScope] will be | |
11695 * created based on [definingLibrary] and [typeProvider]. | |
11696 */ | |
11697 ScopedVisitor(LibraryElement definingLibrary, this.source, this.typeProvider, | |
11698 AnalysisErrorListener errorListener, {Scope nameScope}) { | |
11699 this._definingLibrary = definingLibrary; | |
11700 this._errorListener = errorListener; | |
11701 if (nameScope == null) { | |
11702 this.nameScope = new LibraryScope(definingLibrary, errorListener); | |
11703 } else { | |
11704 this.nameScope = nameScope; | |
11705 } | |
11706 } | |
11707 | |
11708 /** | |
11709 * Return the library element for the library containing the compilation unit
being resolved. | |
11710 * | |
11711 * @return the library element for the library containing the compilation unit
being resolved | |
11712 */ | |
11713 LibraryElement get definingLibrary => _definingLibrary; | |
11714 | |
11715 /** | |
11716 * Return the implicit label scope in which the current node is being | |
11717 * resolved. | |
11718 */ | |
11719 ImplicitLabelScope get implicitLabelScope => _implicitLabelScope; | |
11720 | |
11721 /** | |
11722 * Replaces the current [Scope] with the enclosing [Scope]. | |
11723 * | |
11724 * @return the enclosing [Scope]. | |
11725 */ | |
11726 Scope popNameScope() { | |
11727 nameScope = nameScope.enclosingScope; | |
11728 return nameScope; | |
11729 } | |
11730 | |
11731 /** | |
11732 * Pushes a new [Scope] into the visitor. | |
11733 * | |
11734 * @return the new [Scope]. | |
11735 */ | |
11736 Scope pushNameScope() { | |
11737 Scope newScope = new EnclosedScope(nameScope); | |
11738 nameScope = newScope; | |
11739 return nameScope; | |
11740 } | |
11741 | |
11742 /** | |
11743 * Report an error with the given error code and arguments. | |
11744 * | |
11745 * @param errorCode the error code of the error to be reported | |
11746 * @param node the node specifying the location of the error | |
11747 * @param arguments the arguments to the error, used to compose the error mess
age | |
11748 */ | |
11749 void reportErrorForNode(ErrorCode errorCode, AstNode node, | |
11750 [List<Object> arguments]) { | |
11751 _errorListener.onError(new AnalysisError( | |
11752 source, node.offset, node.length, errorCode, arguments)); | |
11753 } | |
11754 | |
11755 /** | |
11756 * Report an error with the given error code and arguments. | |
11757 * | |
11758 * @param errorCode the error code of the error to be reported | |
11759 * @param offset the offset of the location of the error | |
11760 * @param length the length of the location of the error | |
11761 * @param arguments the arguments to the error, used to compose the error mess
age | |
11762 */ | |
11763 void reportErrorForOffset(ErrorCode errorCode, int offset, int length, | |
11764 [List<Object> arguments]) { | |
11765 _errorListener.onError( | |
11766 new AnalysisError(source, offset, length, errorCode, arguments)); | |
11767 } | |
11768 | |
11769 /** | |
11770 * Report an error with the given error code and arguments. | |
11771 * | |
11772 * @param errorCode the error code of the error to be reported | |
11773 * @param token the token specifying the location of the error | |
11774 * @param arguments the arguments to the error, used to compose the error mess
age | |
11775 */ | |
11776 void reportErrorForToken(ErrorCode errorCode, sc.Token token, | |
11777 [List<Object> arguments]) { | |
11778 _errorListener.onError(new AnalysisError( | |
11779 source, token.offset, token.length, errorCode, arguments)); | |
11780 } | |
11781 | |
11782 /** | |
11783 * Visit the given AST node if it is not null. | |
11784 * | |
11785 * @param node the node to be visited | |
11786 */ | |
11787 void safelyVisit(AstNode node) { | |
11788 if (node != null) { | |
11789 node.accept(this); | |
11790 } | |
11791 } | |
11792 | |
11793 @override | |
11794 Object visitBlock(Block node) { | |
11795 Scope outerScope = nameScope; | |
11796 try { | |
11797 EnclosedScope enclosedScope = new EnclosedScope(nameScope); | |
11798 _hideNamesDefinedInBlock(enclosedScope, node); | |
11799 nameScope = enclosedScope; | |
11800 super.visitBlock(node); | |
11801 } finally { | |
11802 nameScope = outerScope; | |
11803 } | |
11804 return null; | |
11805 } | |
11806 | |
11807 @override | |
11808 Object visitBlockFunctionBody(BlockFunctionBody node) { | |
11809 ImplicitLabelScope implicitOuterScope = _implicitLabelScope; | |
11810 try { | |
11811 _implicitLabelScope = ImplicitLabelScope.ROOT; | |
11812 super.visitBlockFunctionBody(node); | |
11813 } finally { | |
11814 _implicitLabelScope = implicitOuterScope; | |
11815 } | |
11816 return null; | |
11817 } | |
11818 | |
11819 @override | |
11820 Object visitCatchClause(CatchClause node) { | |
11821 SimpleIdentifier exception = node.exceptionParameter; | |
11822 if (exception != null) { | |
11823 Scope outerScope = nameScope; | |
11824 try { | |
11825 nameScope = new EnclosedScope(nameScope); | |
11826 nameScope.define(exception.staticElement); | |
11827 SimpleIdentifier stackTrace = node.stackTraceParameter; | |
11828 if (stackTrace != null) { | |
11829 nameScope.define(stackTrace.staticElement); | |
11830 } | |
11831 super.visitCatchClause(node); | |
11832 } finally { | |
11833 nameScope = outerScope; | |
11834 } | |
11835 } else { | |
11836 super.visitCatchClause(node); | |
11837 } | |
11838 return null; | |
11839 } | |
11840 | |
11841 @override | |
11842 Object visitClassDeclaration(ClassDeclaration node) { | |
11843 ClassElement classElement = node.element; | |
11844 Scope outerScope = nameScope; | |
11845 try { | |
11846 if (classElement == null) { | |
11847 AnalysisEngine.instance.logger.logInformation( | |
11848 "Missing element for class declaration ${node.name.name} in ${defini
ngLibrary.source.fullName}", | |
11849 new CaughtException(new AnalysisException(), null)); | |
11850 super.visitClassDeclaration(node); | |
11851 } else { | |
11852 ClassElement outerClass = enclosingClass; | |
11853 try { | |
11854 enclosingClass = node.element; | |
11855 nameScope = new TypeParameterScope(nameScope, classElement); | |
11856 visitClassDeclarationInScope(node); | |
11857 nameScope = new ClassScope(nameScope, classElement); | |
11858 visitClassMembersInScope(node); | |
11859 } finally { | |
11860 enclosingClass = outerClass; | |
11861 } | |
11862 } | |
11863 } finally { | |
11864 nameScope = outerScope; | |
11865 } | |
11866 return null; | |
11867 } | |
11868 | |
11869 void visitClassDeclarationInScope(ClassDeclaration node) { | |
11870 safelyVisit(node.name); | |
11871 safelyVisit(node.typeParameters); | |
11872 safelyVisit(node.extendsClause); | |
11873 safelyVisit(node.withClause); | |
11874 safelyVisit(node.implementsClause); | |
11875 safelyVisit(node.nativeClause); | |
11876 } | |
11877 | |
11878 void visitClassMembersInScope(ClassDeclaration node) { | |
11879 safelyVisit(node.documentationComment); | |
11880 node.metadata.accept(this); | |
11881 node.members.accept(this); | |
11882 } | |
11883 | |
11884 @override | |
11885 Object visitClassTypeAlias(ClassTypeAlias node) { | |
11886 Scope outerScope = nameScope; | |
11887 try { | |
11888 ClassElement element = node.element; | |
11889 nameScope = | |
11890 new ClassScope(new TypeParameterScope(nameScope, element), element); | |
11891 super.visitClassTypeAlias(node); | |
11892 } finally { | |
11893 nameScope = outerScope; | |
11894 } | |
11895 return null; | |
11896 } | |
11897 | |
11898 @override | |
11899 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
11900 ConstructorElement constructorElement = node.element; | |
11901 Scope outerScope = nameScope; | |
11902 try { | |
11903 if (constructorElement == null) { | |
11904 StringBuffer buffer = new StringBuffer(); | |
11905 buffer.write("Missing element for constructor "); | |
11906 buffer.write(node.returnType.name); | |
11907 if (node.name != null) { | |
11908 buffer.write("."); | |
11909 buffer.write(node.name.name); | |
11910 } | |
11911 buffer.write(" in "); | |
11912 buffer.write(definingLibrary.source.fullName); | |
11913 AnalysisEngine.instance.logger.logInformation(buffer.toString(), | |
11914 new CaughtException(new AnalysisException(), null)); | |
11915 } else { | |
11916 nameScope = new FunctionScope(nameScope, constructorElement); | |
11917 } | |
11918 super.visitConstructorDeclaration(node); | |
11919 } finally { | |
11920 nameScope = outerScope; | |
11921 } | |
11922 return null; | |
11923 } | |
11924 | |
11925 @override | |
11926 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
11927 VariableElement element = node.element; | |
11928 if (element != null) { | |
11929 nameScope.define(element); | |
11930 } | |
11931 super.visitDeclaredIdentifier(node); | |
11932 return null; | |
11933 } | |
11934 | |
11935 @override | |
11936 Object visitDoStatement(DoStatement node) { | |
11937 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
11938 try { | |
11939 _implicitLabelScope = _implicitLabelScope.nest(node); | |
11940 visitStatementInScope(node.body); | |
11941 safelyVisit(node.condition); | |
11942 } finally { | |
11943 _implicitLabelScope = outerImplicitScope; | |
11944 } | |
11945 return null; | |
11946 } | |
11947 | |
11948 @override | |
11949 Object visitForEachStatement(ForEachStatement node) { | |
11950 Scope outerNameScope = nameScope; | |
11951 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
11952 try { | |
11953 nameScope = new EnclosedScope(nameScope); | |
11954 _implicitLabelScope = _implicitLabelScope.nest(node); | |
11955 visitForEachStatementInScope(node); | |
11956 } finally { | |
11957 nameScope = outerNameScope; | |
11958 _implicitLabelScope = outerImplicitScope; | |
11959 } | |
11960 return null; | |
11961 } | |
11962 | |
11963 /** | |
11964 * Visit the given statement after it's scope has been created. This replaces
the normal call to | |
11965 * the inherited visit method so that ResolverVisitor can intervene when type
propagation is | |
11966 * enabled. | |
11967 * | |
11968 * @param node the statement to be visited | |
11969 */ | |
11970 void visitForEachStatementInScope(ForEachStatement node) { | |
11971 // | |
11972 // We visit the iterator before the loop variable because the loop variable | |
11973 // cannot be in scope while visiting the iterator. | |
11974 // | |
11975 safelyVisit(node.identifier); | |
11976 safelyVisit(node.iterable); | |
11977 safelyVisit(node.loopVariable); | |
11978 visitStatementInScope(node.body); | |
11979 } | |
11980 | |
11981 @override | |
11982 Object visitFormalParameterList(FormalParameterList node) { | |
11983 super.visitFormalParameterList(node); | |
11984 // We finished resolving function signature, now include formal parameters | |
11985 // scope. Note: we must not do this if the parent is a | |
11986 // FunctionTypedFormalParameter, because in that case we aren't finished | |
11987 // resolving the full function signature, just a part of it. | |
11988 if (nameScope is FunctionScope && | |
11989 node.parent is! FunctionTypedFormalParameter) { | |
11990 (nameScope as FunctionScope).defineParameters(); | |
11991 } | |
11992 if (nameScope is FunctionTypeScope) { | |
11993 (nameScope as FunctionTypeScope).defineParameters(); | |
11994 } | |
11995 return null; | |
11996 } | |
11997 | |
11998 @override | |
11999 Object visitForStatement(ForStatement node) { | |
12000 Scope outerNameScope = nameScope; | |
12001 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
12002 try { | |
12003 nameScope = new EnclosedScope(nameScope); | |
12004 _implicitLabelScope = _implicitLabelScope.nest(node); | |
12005 visitForStatementInScope(node); | |
12006 } finally { | |
12007 nameScope = outerNameScope; | |
12008 _implicitLabelScope = outerImplicitScope; | |
12009 } | |
12010 return null; | |
12011 } | |
12012 | |
12013 /** | |
12014 * Visit the given statement after it's scope has been created. This replaces
the normal call to | |
12015 * the inherited visit method so that ResolverVisitor can intervene when type
propagation is | |
12016 * enabled. | |
12017 * | |
12018 * @param node the statement to be visited | |
12019 */ | |
12020 void visitForStatementInScope(ForStatement node) { | |
12021 safelyVisit(node.variables); | |
12022 safelyVisit(node.initialization); | |
12023 safelyVisit(node.condition); | |
12024 node.updaters.accept(this); | |
12025 visitStatementInScope(node.body); | |
12026 } | |
12027 | |
12028 @override | |
12029 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
12030 ExecutableElement functionElement = node.element; | |
12031 if (functionElement != null && | |
12032 functionElement.enclosingElement is! CompilationUnitElement) { | |
12033 nameScope.define(functionElement); | |
12034 } | |
12035 Scope outerScope = nameScope; | |
12036 try { | |
12037 if (functionElement == null) { | |
12038 AnalysisEngine.instance.logger.logInformation( | |
12039 "Missing element for top-level function ${node.name.name} in ${defin
ingLibrary.source.fullName}", | |
12040 new CaughtException(new AnalysisException(), null)); | |
12041 } else { | |
12042 nameScope = new FunctionScope(nameScope, functionElement); | |
12043 } | |
12044 super.visitFunctionDeclaration(node); | |
12045 } finally { | |
12046 nameScope = outerScope; | |
12047 } | |
12048 return null; | |
12049 } | |
12050 | |
12051 @override | |
12052 Object visitFunctionExpression(FunctionExpression node) { | |
12053 if (node.parent is FunctionDeclaration) { | |
12054 // We have already created a function scope and don't need to do so again. | |
12055 super.visitFunctionExpression(node); | |
12056 } else { | |
12057 Scope outerScope = nameScope; | |
12058 try { | |
12059 ExecutableElement functionElement = node.element; | |
12060 if (functionElement == null) { | |
12061 StringBuffer buffer = new StringBuffer(); | |
12062 buffer.write("Missing element for function "); | |
12063 AstNode parent = node.parent; | |
12064 while (parent != null) { | |
12065 if (parent is Declaration) { | |
12066 Element parentElement = parent.element; | |
12067 buffer.write(parentElement == null | |
12068 ? "<unknown> " | |
12069 : "${parentElement.name} "); | |
12070 } | |
12071 parent = parent.parent; | |
12072 } | |
12073 buffer.write("in "); | |
12074 buffer.write(definingLibrary.source.fullName); | |
12075 AnalysisEngine.instance.logger.logInformation(buffer.toString(), | |
12076 new CaughtException(new AnalysisException(), null)); | |
12077 } else { | |
12078 nameScope = new FunctionScope(nameScope, functionElement); | |
12079 } | |
12080 super.visitFunctionExpression(node); | |
12081 } finally { | |
12082 nameScope = outerScope; | |
12083 } | |
12084 } | |
12085 return null; | |
12086 } | |
12087 | |
12088 @override | |
12089 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
12090 Scope outerScope = nameScope; | |
12091 try { | |
12092 nameScope = new FunctionTypeScope(nameScope, node.element); | |
12093 super.visitFunctionTypeAlias(node); | |
12094 } finally { | |
12095 nameScope = outerScope; | |
12096 } | |
12097 return null; | |
12098 } | |
12099 | |
12100 @override | |
12101 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
12102 Scope outerScope = nameScope; | |
12103 try { | |
12104 ParameterElement parameterElement = node.element; | |
12105 if (parameterElement == null) { | |
12106 AnalysisEngine.instance.logger.logInformation( | |
12107 "Missing element for function typed formal parameter ${node.identifi
er.name} in ${definingLibrary.source.fullName}", | |
12108 new CaughtException(new AnalysisException(), null)); | |
12109 } else { | |
12110 nameScope = new EnclosedScope(nameScope); | |
12111 for (TypeParameterElement typeParameter | |
12112 in parameterElement.typeParameters) { | |
12113 nameScope.define(typeParameter); | |
12114 } | |
12115 } | |
12116 super.visitFunctionTypedFormalParameter(node); | |
12117 } finally { | |
12118 nameScope = outerScope; | |
12119 } | |
12120 return null; | |
12121 } | |
12122 | |
12123 @override | |
12124 Object visitIfStatement(IfStatement node) { | |
12125 safelyVisit(node.condition); | |
12126 visitStatementInScope(node.thenStatement); | |
12127 visitStatementInScope(node.elseStatement); | |
12128 return null; | |
12129 } | |
12130 | |
12131 @override | |
12132 Object visitLabeledStatement(LabeledStatement node) { | |
12133 LabelScope outerScope = _addScopesFor(node.labels, node.unlabeled); | |
12134 try { | |
12135 super.visitLabeledStatement(node); | |
12136 } finally { | |
12137 labelScope = outerScope; | |
12138 } | |
12139 return null; | |
12140 } | |
12141 | |
12142 @override | |
12143 Object visitMethodDeclaration(MethodDeclaration node) { | |
12144 Scope outerScope = nameScope; | |
12145 try { | |
12146 ExecutableElement methodElement = node.element; | |
12147 if (methodElement == null) { | |
12148 AnalysisEngine.instance.logger.logInformation( | |
12149 "Missing element for method ${node.name.name} in ${definingLibrary.s
ource.fullName}", | |
12150 new CaughtException(new AnalysisException(), null)); | |
12151 } else { | |
12152 nameScope = new FunctionScope(nameScope, methodElement); | |
12153 } | |
12154 super.visitMethodDeclaration(node); | |
12155 } finally { | |
12156 nameScope = outerScope; | |
12157 } | |
12158 return null; | |
12159 } | |
12160 | |
12161 /** | |
12162 * Visit the given statement after it's scope has been created. This is used b
y ResolverVisitor to | |
12163 * correctly visit the 'then' and 'else' statements of an 'if' statement. | |
12164 * | |
12165 * @param node the statement to be visited | |
12166 */ | |
12167 void visitStatementInScope(Statement node) { | |
12168 if (node is Block) { | |
12169 // Don't create a scope around a block because the block will create it's | |
12170 // own scope. | |
12171 visitBlock(node); | |
12172 } else if (node != null) { | |
12173 Scope outerNameScope = nameScope; | |
12174 try { | |
12175 nameScope = new EnclosedScope(nameScope); | |
12176 node.accept(this); | |
12177 } finally { | |
12178 nameScope = outerNameScope; | |
12179 } | |
12180 } | |
12181 } | |
12182 | |
12183 @override | |
12184 Object visitSwitchCase(SwitchCase node) { | |
12185 node.expression.accept(this); | |
12186 Scope outerNameScope = nameScope; | |
12187 try { | |
12188 nameScope = new EnclosedScope(nameScope); | |
12189 node.statements.accept(this); | |
12190 } finally { | |
12191 nameScope = outerNameScope; | |
12192 } | |
12193 return null; | |
12194 } | |
12195 | |
12196 @override | |
12197 Object visitSwitchDefault(SwitchDefault node) { | |
12198 Scope outerNameScope = nameScope; | |
12199 try { | |
12200 nameScope = new EnclosedScope(nameScope); | |
12201 node.statements.accept(this); | |
12202 } finally { | |
12203 nameScope = outerNameScope; | |
12204 } | |
12205 return null; | |
12206 } | |
12207 | |
12208 @override | |
12209 Object visitSwitchStatement(SwitchStatement node) { | |
12210 LabelScope outerScope = labelScope; | |
12211 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
12212 try { | |
12213 _implicitLabelScope = _implicitLabelScope.nest(node); | |
12214 for (SwitchMember member in node.members) { | |
12215 for (Label label in member.labels) { | |
12216 SimpleIdentifier labelName = label.label; | |
12217 LabelElement labelElement = labelName.staticElement as LabelElement; | |
12218 labelScope = | |
12219 new LabelScope(labelScope, labelName.name, member, labelElement); | |
12220 } | |
12221 } | |
12222 super.visitSwitchStatement(node); | |
12223 } finally { | |
12224 labelScope = outerScope; | |
12225 _implicitLabelScope = outerImplicitScope; | |
12226 } | |
12227 return null; | |
12228 } | |
12229 | |
12230 @override | |
12231 Object visitVariableDeclaration(VariableDeclaration node) { | |
12232 super.visitVariableDeclaration(node); | |
12233 if (node.parent.parent is! TopLevelVariableDeclaration && | |
12234 node.parent.parent is! FieldDeclaration) { | |
12235 VariableElement element = node.element; | |
12236 if (element != null) { | |
12237 nameScope.define(element); | |
12238 } | |
12239 } | |
12240 return null; | |
12241 } | |
12242 | |
12243 @override | |
12244 Object visitWhileStatement(WhileStatement node) { | |
12245 safelyVisit(node.condition); | |
12246 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
12247 try { | |
12248 _implicitLabelScope = _implicitLabelScope.nest(node); | |
12249 visitStatementInScope(node.body); | |
12250 } finally { | |
12251 _implicitLabelScope = outerImplicitScope; | |
12252 } | |
12253 return null; | |
12254 } | |
12255 | |
12256 /** | |
12257 * Add scopes for each of the given labels. | |
12258 * | |
12259 * @param labels the labels for which new scopes are to be added | |
12260 * @return the scope that was in effect before the new scopes were added | |
12261 */ | |
12262 LabelScope _addScopesFor(NodeList<Label> labels, AstNode node) { | |
12263 LabelScope outerScope = labelScope; | |
12264 for (Label label in labels) { | |
12265 SimpleIdentifier labelNameNode = label.label; | |
12266 String labelName = labelNameNode.name; | |
12267 LabelElement labelElement = labelNameNode.staticElement as LabelElement; | |
12268 labelScope = new LabelScope(labelScope, labelName, node, labelElement); | |
12269 } | |
12270 return outerScope; | |
12271 } | |
12272 | |
12273 /** | |
12274 * Marks the local declarations of the given [Block] hidden in the enclosing s
cope. | |
12275 * According to the scoping rules name is hidden if block defines it, but name
is defined after | |
12276 * its declaration statement. | |
12277 */ | |
12278 void _hideNamesDefinedInBlock(EnclosedScope scope, Block block) { | |
12279 NodeList<Statement> statements = block.statements; | |
12280 int statementCount = statements.length; | |
12281 for (int i = 0; i < statementCount; i++) { | |
12282 Statement statement = statements[i]; | |
12283 if (statement is VariableDeclarationStatement) { | |
12284 VariableDeclarationStatement vds = statement; | |
12285 NodeList<VariableDeclaration> variables = vds.variables.variables; | |
12286 int variableCount = variables.length; | |
12287 for (int j = 0; j < variableCount; j++) { | |
12288 scope.hide(variables[j].element); | |
12289 } | |
12290 } else if (statement is FunctionDeclarationStatement) { | |
12291 FunctionDeclarationStatement fds = statement; | |
12292 scope.hide(fds.functionDeclaration.element); | |
12293 } | |
12294 } | |
12295 } | |
12296 } | |
12297 | |
12298 /** | |
12299 * Instances of this class manage the knowledge of what the set of subtypes are
for a given type. | |
12300 */ | |
12301 class SubtypeManager { | |
12302 /** | |
12303 * A map between [ClassElement]s and a set of [ClassElement]s that are subtype
s of the | |
12304 * key. | |
12305 */ | |
12306 HashMap<ClassElement, HashSet<ClassElement>> _subtypeMap = | |
12307 new HashMap<ClassElement, HashSet<ClassElement>>(); | |
12308 | |
12309 /** | |
12310 * The set of all [LibraryElement]s that have been visited by the manager. Thi
s is used both | |
12311 * to prevent infinite loops in the recursive methods, and also as a marker fo
r the scope of the | |
12312 * libraries visited by this manager. | |
12313 */ | |
12314 HashSet<LibraryElement> _visitedLibraries = new HashSet<LibraryElement>(); | |
12315 | |
12316 /** | |
12317 * Given some [ClassElement], return the set of all subtypes, and subtypes of
subtypes. | |
12318 * | |
12319 * @param classElement the class to recursively return the set of subtypes of | |
12320 */ | |
12321 HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) { | |
12322 // Ensure that we have generated the subtype map for the library | |
12323 _computeSubtypesInLibrary(classElement.library); | |
12324 // use the subtypeMap to compute the set of all subtypes and subtype's | |
12325 // subtypes | |
12326 HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>(); | |
12327 _safelyComputeAllSubtypes( | |
12328 classElement, new HashSet<ClassElement>(), allSubtypes); | |
12329 return allSubtypes; | |
12330 } | |
12331 | |
12332 /** | |
12333 * Given some [LibraryElement], visit all of the types in the library, the pas
sed library, | |
12334 * and any imported libraries, will be in the [visitedLibraries] set. | |
12335 * | |
12336 * @param libraryElement the library to visit, it it hasn't been visited alrea
dy | |
12337 */ | |
12338 void ensureLibraryVisited(LibraryElement libraryElement) { | |
12339 _computeSubtypesInLibrary(libraryElement); | |
12340 } | |
12341 | |
12342 /** | |
12343 * Given some [ClassElement], this method adds all of the pairs combinations o
f itself and | |
12344 * all of its supertypes to the [subtypeMap] map. | |
12345 * | |
12346 * @param classElement the class element | |
12347 */ | |
12348 void _computeSubtypesInClass(ClassElement classElement) { | |
12349 InterfaceType supertypeType = classElement.supertype; | |
12350 if (supertypeType != null) { | |
12351 ClassElement supertypeElement = supertypeType.element; | |
12352 if (supertypeElement != null) { | |
12353 _putInSubtypeMap(supertypeElement, classElement); | |
12354 } | |
12355 } | |
12356 List<InterfaceType> interfaceTypes = classElement.interfaces; | |
12357 for (InterfaceType interfaceType in interfaceTypes) { | |
12358 ClassElement interfaceElement = interfaceType.element; | |
12359 if (interfaceElement != null) { | |
12360 _putInSubtypeMap(interfaceElement, classElement); | |
12361 } | |
12362 } | |
12363 List<InterfaceType> mixinTypes = classElement.mixins; | |
12364 for (InterfaceType mixinType in mixinTypes) { | |
12365 ClassElement mixinElement = mixinType.element; | |
12366 if (mixinElement != null) { | |
12367 _putInSubtypeMap(mixinElement, classElement); | |
12368 } | |
12369 } | |
12370 } | |
12371 | |
12372 /** | |
12373 * Given some [CompilationUnitElement], this method calls | |
12374 * [computeAllSubtypes] on all of the [ClassElement]s in the | |
12375 * compilation unit. | |
12376 * | |
12377 * @param unitElement the compilation unit element | |
12378 */ | |
12379 void _computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) { | |
12380 List<ClassElement> classElements = unitElement.types; | |
12381 for (ClassElement classElement in classElements) { | |
12382 _computeSubtypesInClass(classElement); | |
12383 } | |
12384 } | |
12385 | |
12386 /** | |
12387 * Given some [LibraryElement], this method calls | |
12388 * [computeAllSubtypes] on all of the [ClassElement]s in the | |
12389 * compilation unit, and itself for all imported and exported libraries. All v
isited libraries are | |
12390 * added to the [visitedLibraries] set. | |
12391 * | |
12392 * @param libraryElement the library element | |
12393 */ | |
12394 void _computeSubtypesInLibrary(LibraryElement libraryElement) { | |
12395 if (libraryElement == null || _visitedLibraries.contains(libraryElement)) { | |
12396 return; | |
12397 } | |
12398 _visitedLibraries.add(libraryElement); | |
12399 _computeSubtypesInCompilationUnit(libraryElement.definingCompilationUnit); | |
12400 List<CompilationUnitElement> parts = libraryElement.parts; | |
12401 for (CompilationUnitElement part in parts) { | |
12402 _computeSubtypesInCompilationUnit(part); | |
12403 } | |
12404 List<LibraryElement> imports = libraryElement.importedLibraries; | |
12405 for (LibraryElement importElt in imports) { | |
12406 _computeSubtypesInLibrary(importElt.library); | |
12407 } | |
12408 List<LibraryElement> exports = libraryElement.exportedLibraries; | |
12409 for (LibraryElement exportElt in exports) { | |
12410 _computeSubtypesInLibrary(exportElt.library); | |
12411 } | |
12412 } | |
12413 | |
12414 /** | |
12415 * Add some key/ value pair into the [subtypeMap] map. | |
12416 * | |
12417 * @param supertypeElement the key for the [subtypeMap] map | |
12418 * @param subtypeElement the value for the [subtypeMap] map | |
12419 */ | |
12420 void _putInSubtypeMap( | |
12421 ClassElement supertypeElement, ClassElement subtypeElement) { | |
12422 HashSet<ClassElement> subtypes = _subtypeMap[supertypeElement]; | |
12423 if (subtypes == null) { | |
12424 subtypes = new HashSet<ClassElement>(); | |
12425 _subtypeMap[supertypeElement] = subtypes; | |
12426 } | |
12427 subtypes.add(subtypeElement); | |
12428 } | |
12429 | |
12430 /** | |
12431 * Given some [ClassElement] and a [HashSet<ClassElement>], this method recurs
ively | |
12432 * adds all of the subtypes of the [ClassElement] to the passed array. | |
12433 * | |
12434 * @param classElement the type to compute the set of subtypes of | |
12435 * @param visitedClasses the set of class elements that this method has alread
y recursively seen | |
12436 * @param allSubtypes the computed set of subtypes of the passed class element | |
12437 */ | |
12438 void _safelyComputeAllSubtypes(ClassElement classElement, | |
12439 HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) { | |
12440 if (!visitedClasses.add(classElement)) { | |
12441 // if this class has already been called on this class element | |
12442 return; | |
12443 } | |
12444 HashSet<ClassElement> subtypes = _subtypeMap[classElement]; | |
12445 if (subtypes == null) { | |
12446 return; | |
12447 } | |
12448 for (ClassElement subtype in subtypes) { | |
12449 _safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes); | |
12450 } | |
12451 allSubtypes.addAll(subtypes); | |
12452 } | |
12453 } | |
12454 | |
12455 /** | |
12456 * Instances of the class `ToDoFinder` find to-do comments in Dart code. | |
12457 */ | |
12458 class ToDoFinder { | |
12459 /** | |
12460 * The error reporter by which to-do comments will be reported. | |
12461 */ | |
12462 final ErrorReporter _errorReporter; | |
12463 | |
12464 /** | |
12465 * Initialize a newly created to-do finder to report to-do comments to the giv
en reporter. | |
12466 * | |
12467 * @param errorReporter the error reporter by which to-do comments will be rep
orted | |
12468 */ | |
12469 ToDoFinder(this._errorReporter); | |
12470 | |
12471 /** | |
12472 * Search the comments in the given compilation unit for to-do comments and re
port an error for | |
12473 * each. | |
12474 * | |
12475 * @param unit the compilation unit containing the to-do comments | |
12476 */ | |
12477 void findIn(CompilationUnit unit) { | |
12478 _gatherTodoComments(unit.beginToken); | |
12479 } | |
12480 | |
12481 /** | |
12482 * Search the comment tokens reachable from the given token and create errors
for each to-do | |
12483 * comment. | |
12484 * | |
12485 * @param token the head of the list of tokens being searched | |
12486 */ | |
12487 void _gatherTodoComments(sc.Token token) { | |
12488 while (token != null && token.type != sc.TokenType.EOF) { | |
12489 sc.Token commentToken = token.precedingComments; | |
12490 while (commentToken != null) { | |
12491 if (commentToken.type == sc.TokenType.SINGLE_LINE_COMMENT || | |
12492 commentToken.type == sc.TokenType.MULTI_LINE_COMMENT) { | |
12493 _scrapeTodoComment(commentToken); | |
12494 } | |
12495 commentToken = commentToken.next; | |
12496 } | |
12497 token = token.next; | |
12498 } | |
12499 } | |
12500 | |
12501 /** | |
12502 * Look for user defined tasks in comments and convert them into info level an
alysis issues. | |
12503 * | |
12504 * @param commentToken the comment token to analyze | |
12505 */ | |
12506 void _scrapeTodoComment(sc.Token commentToken) { | |
12507 JavaPatternMatcher matcher = | |
12508 new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme); | |
12509 if (matcher.find()) { | |
12510 int offset = | |
12511 commentToken.offset + matcher.start() + matcher.group(1).length; | |
12512 int length = matcher.group(2).length; | |
12513 _errorReporter.reportErrorForOffset( | |
12514 TodoCode.TODO, offset, length, [matcher.group(2)]); | |
12515 } | |
12516 } | |
12517 } | |
12518 | |
12519 /** | |
12520 * Instances of the class `TypeOverrideManager` manage the ability to override t
he type of an | |
12521 * element within a given context. | |
12522 */ | |
12523 class TypeOverrideManager { | |
12524 /** | |
12525 * The current override scope, or `null` if no scope has been entered. | |
12526 */ | |
12527 TypeOverrideManager_TypeOverrideScope currentScope; | |
12528 | |
12529 /** | |
12530 * Apply a set of overrides that were previously captured. | |
12531 * | |
12532 * @param overrides the overrides to be applied | |
12533 */ | |
12534 void applyOverrides(Map<VariableElement, DartType> overrides) { | |
12535 if (currentScope == null) { | |
12536 throw new IllegalStateException("Cannot apply overrides without a scope"); | |
12537 } | |
12538 currentScope.applyOverrides(overrides); | |
12539 } | |
12540 | |
12541 /** | |
12542 * Return a table mapping the elements whose type is overridden in the current
scope to the | |
12543 * overriding type. | |
12544 * | |
12545 * @return the overrides in the current scope | |
12546 */ | |
12547 Map<VariableElement, DartType> captureLocalOverrides() { | |
12548 if (currentScope == null) { | |
12549 throw new IllegalStateException( | |
12550 "Cannot capture local overrides without a scope"); | |
12551 } | |
12552 return currentScope.captureLocalOverrides(); | |
12553 } | |
12554 | |
12555 /** | |
12556 * Return a map from the elements for the variables in the given list that hav
e their types | |
12557 * overridden to the overriding type. | |
12558 * | |
12559 * @param variableList the list of variables whose overriding types are to be
captured | |
12560 * @return a table mapping elements to their overriding types | |
12561 */ | |
12562 Map<VariableElement, DartType> captureOverrides( | |
12563 VariableDeclarationList variableList) { | |
12564 if (currentScope == null) { | |
12565 throw new IllegalStateException( | |
12566 "Cannot capture overrides without a scope"); | |
12567 } | |
12568 return currentScope.captureOverrides(variableList); | |
12569 } | |
12570 | |
12571 /** | |
12572 * Enter a new override scope. | |
12573 */ | |
12574 void enterScope() { | |
12575 currentScope = new TypeOverrideManager_TypeOverrideScope(currentScope); | |
12576 } | |
12577 | |
12578 /** | |
12579 * Exit the current override scope. | |
12580 */ | |
12581 void exitScope() { | |
12582 if (currentScope == null) { | |
12583 throw new IllegalStateException("No scope to exit"); | |
12584 } | |
12585 currentScope = currentScope._outerScope; | |
12586 } | |
12587 | |
12588 /** | |
12589 * Return the best type information available for the given element. If the ty
pe of the element | |
12590 * has been overridden, then return the overriding type. Otherwise, return the
static type. | |
12591 * | |
12592 * @param element the element for which type information is to be returned | |
12593 * @return the best type information available for the given element | |
12594 */ | |
12595 DartType getBestType(VariableElement element) { | |
12596 DartType bestType = getType(element); | |
12597 return bestType == null ? element.type : bestType; | |
12598 } | |
12599 | |
12600 /** | |
12601 * Return the overridden type of the given element, or `null` if the type of t
he element has | |
12602 * not been overridden. | |
12603 * | |
12604 * @param element the element whose type might have been overridden | |
12605 * @return the overridden type of the given element | |
12606 */ | |
12607 DartType getType(Element element) { | |
12608 if (currentScope == null) { | |
12609 return null; | |
12610 } | |
12611 return currentScope.getType(element); | |
12612 } | |
12613 | |
12614 /** | |
12615 * Update overrides assuming [perBranchOverrides] is the collection of | |
12616 * per-branch overrides for *all* branches flowing into a join point. | |
12617 * | |
12618 * If a variable type in any of branches is not the same as its type before | |
12619 * the branching, then its propagated type is reset to `null`. | |
12620 */ | |
12621 void mergeOverrides(List<Map<VariableElement, DartType>> perBranchOverrides) { | |
12622 for (Map<VariableElement, DartType> branch in perBranchOverrides) { | |
12623 branch.forEach((VariableElement variable, DartType branchType) { | |
12624 DartType currentType = currentScope.getType(variable); | |
12625 if (currentType != branchType) { | |
12626 currentScope.resetType(variable); | |
12627 } | |
12628 }); | |
12629 } | |
12630 } | |
12631 | |
12632 /** | |
12633 * Set the overridden type of the given element to the given type | |
12634 * | |
12635 * @param element the element whose type might have been overridden | |
12636 * @param type the overridden type of the given element | |
12637 */ | |
12638 void setType(VariableElement element, DartType type) { | |
12639 if (currentScope == null) { | |
12640 throw new IllegalStateException("Cannot override without a scope"); | |
12641 } | |
12642 currentScope.setType(element, type); | |
12643 } | |
12644 } | |
12645 | |
12646 /** | |
12647 * Instances of the class `TypeOverrideScope` represent a scope in which the typ
es of | |
12648 * elements can be overridden. | |
12649 */ | |
12650 class TypeOverrideManager_TypeOverrideScope { | |
12651 /** | |
12652 * The outer scope in which types might be overridden. | |
12653 */ | |
12654 final TypeOverrideManager_TypeOverrideScope _outerScope; | |
12655 | |
12656 /** | |
12657 * A table mapping elements to the overridden type of that element. | |
12658 */ | |
12659 Map<VariableElement, DartType> _overridenTypes = | |
12660 new HashMap<VariableElement, DartType>(); | |
12661 | |
12662 /** | |
12663 * Initialize a newly created scope to be an empty child of the given scope. | |
12664 * | |
12665 * @param outerScope the outer scope in which types might be overridden | |
12666 */ | |
12667 TypeOverrideManager_TypeOverrideScope(this._outerScope); | |
12668 | |
12669 /** | |
12670 * Apply a set of overrides that were previously captured. | |
12671 * | |
12672 * @param overrides the overrides to be applied | |
12673 */ | |
12674 void applyOverrides(Map<VariableElement, DartType> overrides) { | |
12675 _overridenTypes.addAll(overrides); | |
12676 } | |
12677 | |
12678 /** | |
12679 * Return a table mapping the elements whose type is overridden in the current
scope to the | |
12680 * overriding type. | |
12681 * | |
12682 * @return the overrides in the current scope | |
12683 */ | |
12684 Map<VariableElement, DartType> captureLocalOverrides() => _overridenTypes; | |
12685 | |
12686 /** | |
12687 * Return a map from the elements for the variables in the given list that hav
e their types | |
12688 * overridden to the overriding type. | |
12689 * | |
12690 * @param variableList the list of variables whose overriding types are to be
captured | |
12691 * @return a table mapping elements to their overriding types | |
12692 */ | |
12693 Map<VariableElement, DartType> captureOverrides( | |
12694 VariableDeclarationList variableList) { | |
12695 Map<VariableElement, DartType> overrides = | |
12696 new HashMap<VariableElement, DartType>(); | |
12697 if (variableList.isConst || variableList.isFinal) { | |
12698 for (VariableDeclaration variable in variableList.variables) { | |
12699 VariableElement element = variable.element; | |
12700 if (element != null) { | |
12701 DartType type = _overridenTypes[element]; | |
12702 if (type != null) { | |
12703 overrides[element] = type; | |
12704 } | |
12705 } | |
12706 } | |
12707 } | |
12708 return overrides; | |
12709 } | |
12710 | |
12711 /** | |
12712 * Return the overridden type of the given element, or `null` if the type of t
he element | |
12713 * has not been overridden. | |
12714 * | |
12715 * @param element the element whose type might have been overridden | |
12716 * @return the overridden type of the given element | |
12717 */ | |
12718 DartType getType(Element element) { | |
12719 if (element is PropertyAccessorElement) { | |
12720 element = (element as PropertyAccessorElement).variable; | |
12721 } | |
12722 DartType type = _overridenTypes[element]; | |
12723 if (_overridenTypes.containsKey(element)) { | |
12724 return type; | |
12725 } | |
12726 if (type != null) { | |
12727 return type; | |
12728 } else if (_outerScope != null) { | |
12729 return _outerScope.getType(element); | |
12730 } | |
12731 return null; | |
12732 } | |
12733 | |
12734 /** | |
12735 * Clears the overridden type of the given [element]. | |
12736 */ | |
12737 void resetType(VariableElement element) { | |
12738 _overridenTypes[element] = null; | |
12739 } | |
12740 | |
12741 /** | |
12742 * Set the overridden type of the given element to the given type | |
12743 * | |
12744 * @param element the element whose type might have been overridden | |
12745 * @param type the overridden type of the given element | |
12746 */ | |
12747 void setType(VariableElement element, DartType type) { | |
12748 _overridenTypes[element] = type; | |
12749 } | |
12750 } | |
12751 | |
12752 /** | |
12753 * Instances of the class `TypeParameterScope` implement the scope defined by th
e type | |
12754 * parameters in a class. | |
12755 */ | |
12756 class TypeParameterScope extends EnclosedScope { | |
12757 /** | |
12758 * Initialize a newly created scope enclosed within another scope. | |
12759 * | |
12760 * @param enclosingScope the scope in which this scope is lexically enclosed | |
12761 * @param typeElement the element representing the type represented by this sc
ope | |
12762 */ | |
12763 TypeParameterScope(Scope enclosingScope, ClassElement typeElement) | |
12764 : super(enclosingScope) { | |
12765 if (typeElement == null) { | |
12766 throw new IllegalArgumentException("class element cannot be null"); | |
12767 } | |
12768 _defineTypeParameters(typeElement); | |
12769 } | |
12770 | |
12771 /** | |
12772 * Define the type parameters for the class. | |
12773 * | |
12774 * @param typeElement the element representing the type represented by this sc
ope | |
12775 */ | |
12776 void _defineTypeParameters(ClassElement typeElement) { | |
12777 for (TypeParameterElement typeParameter in typeElement.typeParameters) { | |
12778 define(typeParameter); | |
12779 } | |
12780 } | |
12781 } | |
12782 | |
12783 /** | |
12784 * Instances of the class `TypePromotionManager` manage the ability to promote t
ypes of local | |
12785 * variables and formal parameters from their declared types based on control fl
ow. | |
12786 */ | |
12787 class TypePromotionManager { | |
12788 /** | |
12789 * The current promotion scope, or `null` if no scope has been entered. | |
12790 */ | |
12791 TypePromotionManager_TypePromoteScope currentScope; | |
12792 | |
12793 /** | |
12794 * Returns the elements with promoted types. | |
12795 */ | |
12796 Iterable<Element> get promotedElements => currentScope.promotedElements; | |
12797 | |
12798 /** | |
12799 * Enter a new promotions scope. | |
12800 */ | |
12801 void enterScope() { | |
12802 currentScope = new TypePromotionManager_TypePromoteScope(currentScope); | |
12803 } | |
12804 | |
12805 /** | |
12806 * Exit the current promotion scope. | |
12807 */ | |
12808 void exitScope() { | |
12809 if (currentScope == null) { | |
12810 throw new IllegalStateException("No scope to exit"); | |
12811 } | |
12812 currentScope = currentScope._outerScope; | |
12813 } | |
12814 | |
12815 /** | |
12816 * Returns static type of the given variable - declared or promoted. | |
12817 * | |
12818 * @return the static type of the given variable - declared or promoted | |
12819 */ | |
12820 DartType getStaticType(VariableElement variable) { | |
12821 DartType staticType = getType(variable); | |
12822 if (staticType == null) { | |
12823 staticType = variable.type; | |
12824 } | |
12825 return staticType; | |
12826 } | |
12827 | |
12828 /** | |
12829 * Return the promoted type of the given element, or `null` if the type of the
element has | |
12830 * not been promoted. | |
12831 * | |
12832 * @param element the element whose type might have been promoted | |
12833 * @return the promoted type of the given element | |
12834 */ | |
12835 DartType getType(Element element) { | |
12836 if (currentScope == null) { | |
12837 return null; | |
12838 } | |
12839 return currentScope.getType(element); | |
12840 } | |
12841 | |
12842 /** | |
12843 * Set the promoted type of the given element to the given type. | |
12844 * | |
12845 * @param element the element whose type might have been promoted | |
12846 * @param type the promoted type of the given element | |
12847 */ | |
12848 void setType(Element element, DartType type) { | |
12849 if (currentScope == null) { | |
12850 throw new IllegalStateException("Cannot promote without a scope"); | |
12851 } | |
12852 currentScope.setType(element, type); | |
12853 } | |
12854 } | |
12855 | |
12856 /** | |
12857 * Instances of the class `TypePromoteScope` represent a scope in which the type
s of | |
12858 * elements can be promoted. | |
12859 */ | |
12860 class TypePromotionManager_TypePromoteScope { | |
12861 /** | |
12862 * The outer scope in which types might be promoter. | |
12863 */ | |
12864 final TypePromotionManager_TypePromoteScope _outerScope; | |
12865 | |
12866 /** | |
12867 * A table mapping elements to the promoted type of that element. | |
12868 */ | |
12869 HashMap<Element, DartType> _promotedTypes = new HashMap<Element, DartType>(); | |
12870 | |
12871 /** | |
12872 * Initialize a newly created scope to be an empty child of the given scope. | |
12873 * | |
12874 * @param outerScope the outer scope in which types might be promoted | |
12875 */ | |
12876 TypePromotionManager_TypePromoteScope(this._outerScope); | |
12877 | |
12878 /** | |
12879 * Returns the elements with promoted types. | |
12880 */ | |
12881 Iterable<Element> get promotedElements => _promotedTypes.keys.toSet(); | |
12882 | |
12883 /** | |
12884 * Return the promoted type of the given element, or `null` if the type of the
element has | |
12885 * not been promoted. | |
12886 * | |
12887 * @param element the element whose type might have been promoted | |
12888 * @return the promoted type of the given element | |
12889 */ | |
12890 DartType getType(Element element) { | |
12891 DartType type = _promotedTypes[element]; | |
12892 if (type == null && element is PropertyAccessorElement) { | |
12893 type = _promotedTypes[element.variable]; | |
12894 } | |
12895 if (type != null) { | |
12896 return type; | |
12897 } else if (_outerScope != null) { | |
12898 return _outerScope.getType(element); | |
12899 } | |
12900 return null; | |
12901 } | |
12902 | |
12903 /** | |
12904 * Set the promoted type of the given element to the given type. | |
12905 * | |
12906 * @param element the element whose type might have been promoted | |
12907 * @param type the promoted type of the given element | |
12908 */ | |
12909 void setType(Element element, DartType type) { | |
12910 _promotedTypes[element] = type; | |
12911 } | |
12912 } | |
12913 | |
12914 /** | |
12915 * The interface `TypeProvider` defines the behavior of objects that provide acc
ess to types | |
12916 * defined by the language. | |
12917 */ | |
12918 abstract class TypeProvider { | |
12919 /** | |
12920 * Return the type representing the built-in type 'bool'. | |
12921 */ | |
12922 InterfaceType get boolType; | |
12923 | |
12924 /** | |
12925 * Return the type representing the type 'bottom'. | |
12926 */ | |
12927 DartType get bottomType; | |
12928 | |
12929 /** | |
12930 * Return the type representing the built-in type 'Deprecated'. | |
12931 */ | |
12932 InterfaceType get deprecatedType; | |
12933 | |
12934 /** | |
12935 * Return the type representing the built-in type 'double'. | |
12936 */ | |
12937 InterfaceType get doubleType; | |
12938 | |
12939 /** | |
12940 * Return the type representing the built-in type 'dynamic'. | |
12941 */ | |
12942 DartType get dynamicType; | |
12943 | |
12944 /** | |
12945 * Return the type representing the built-in type 'Function'. | |
12946 */ | |
12947 InterfaceType get functionType; | |
12948 | |
12949 /** | |
12950 * Return the type representing 'Future<dynamic>'. | |
12951 */ | |
12952 InterfaceType get futureDynamicType; | |
12953 | |
12954 /** | |
12955 * Return the type representing 'Future<Null>'. | |
12956 */ | |
12957 InterfaceType get futureNullType; | |
12958 | |
12959 /** | |
12960 * Return the type representing the built-in type 'Future'. | |
12961 */ | |
12962 InterfaceType get futureType; | |
12963 | |
12964 /** | |
12965 * Return the type representing the built-in type 'int'. | |
12966 */ | |
12967 InterfaceType get intType; | |
12968 | |
12969 /** | |
12970 * Return the type representing the type 'Iterable<dynamic>'. | |
12971 */ | |
12972 InterfaceType get iterableDynamicType; | |
12973 | |
12974 /** | |
12975 * Return the type representing the built-in type 'Iterable'. | |
12976 */ | |
12977 InterfaceType get iterableType; | |
12978 | |
12979 /** | |
12980 * Return the type representing the built-in type 'List'. | |
12981 */ | |
12982 InterfaceType get listType; | |
12983 | |
12984 /** | |
12985 * Return the type representing the built-in type 'Map'. | |
12986 */ | |
12987 InterfaceType get mapType; | |
12988 | |
12989 /** | |
12990 * Return a list containing all of the types that cannot be either extended or | |
12991 * implemented. | |
12992 */ | |
12993 List<InterfaceType> get nonSubtypableTypes; | |
12994 | |
12995 /** | |
12996 * Return a [DartObjectImpl] representing the `null` object. | |
12997 */ | |
12998 DartObjectImpl get nullObject; | |
12999 | |
13000 /** | |
13001 * Return the type representing the built-in type 'Null'. | |
13002 */ | |
13003 InterfaceType get nullType; | |
13004 | |
13005 /** | |
13006 * Return the type representing the built-in type 'num'. | |
13007 */ | |
13008 InterfaceType get numType; | |
13009 | |
13010 /** | |
13011 * Return the type representing the built-in type 'Object'. | |
13012 */ | |
13013 InterfaceType get objectType; | |
13014 | |
13015 /** | |
13016 * Return the type representing the built-in type 'StackTrace'. | |
13017 */ | |
13018 InterfaceType get stackTraceType; | |
13019 | |
13020 /** | |
13021 * Return the type representing 'Stream<dynamic>'. | |
13022 */ | |
13023 InterfaceType get streamDynamicType; | |
13024 | |
13025 /** | |
13026 * Return the type representing the built-in type 'Stream'. | |
13027 */ | |
13028 InterfaceType get streamType; | |
13029 | |
13030 /** | |
13031 * Return the type representing the built-in type 'String'. | |
13032 */ | |
13033 InterfaceType get stringType; | |
13034 | |
13035 /** | |
13036 * Return the type representing the built-in type 'Symbol'. | |
13037 */ | |
13038 InterfaceType get symbolType; | |
13039 | |
13040 /** | |
13041 * Return the type representing the built-in type 'Type'. | |
13042 */ | |
13043 InterfaceType get typeType; | |
13044 | |
13045 /** | |
13046 * Return the type representing typenames that can't be resolved. | |
13047 */ | |
13048 DartType get undefinedType; | |
13049 } | |
13050 | |
13051 /** | |
13052 * Instances of the class `TypeProviderImpl` provide access to types defined by
the language | |
13053 * by looking for those types in the element model for the core library. | |
13054 */ | |
13055 class TypeProviderImpl implements TypeProvider { | |
13056 /** | |
13057 * The type representing the built-in type 'bool'. | |
13058 */ | |
13059 InterfaceType _boolType; | |
13060 | |
13061 /** | |
13062 * The type representing the type 'bottom'. | |
13063 */ | |
13064 DartType _bottomType; | |
13065 | |
13066 /** | |
13067 * The type representing the built-in type 'double'. | |
13068 */ | |
13069 InterfaceType _doubleType; | |
13070 | |
13071 /** | |
13072 * The type representing the built-in type 'Deprecated'. | |
13073 */ | |
13074 InterfaceType _deprecatedType; | |
13075 | |
13076 /** | |
13077 * The type representing the built-in type 'dynamic'. | |
13078 */ | |
13079 DartType _dynamicType; | |
13080 | |
13081 /** | |
13082 * The type representing the built-in type 'Function'. | |
13083 */ | |
13084 InterfaceType _functionType; | |
13085 | |
13086 /** | |
13087 * The type representing 'Future<dynamic>'. | |
13088 */ | |
13089 InterfaceType _futureDynamicType; | |
13090 | |
13091 /** | |
13092 * The type representing 'Future<Null>'. | |
13093 */ | |
13094 InterfaceType _futureNullType; | |
13095 | |
13096 /** | |
13097 * The type representing the built-in type 'Future'. | |
13098 */ | |
13099 InterfaceType _futureType; | |
13100 | |
13101 /** | |
13102 * The type representing the built-in type 'int'. | |
13103 */ | |
13104 InterfaceType _intType; | |
13105 | |
13106 /** | |
13107 * The type representing 'Iterable<dynamic>'. | |
13108 */ | |
13109 InterfaceType _iterableDynamicType; | |
13110 | |
13111 /** | |
13112 * The type representing the built-in type 'Iterable'. | |
13113 */ | |
13114 InterfaceType _iterableType; | |
13115 | |
13116 /** | |
13117 * The type representing the built-in type 'List'. | |
13118 */ | |
13119 InterfaceType _listType; | |
13120 | |
13121 /** | |
13122 * The type representing the built-in type 'Map'. | |
13123 */ | |
13124 InterfaceType _mapType; | |
13125 | |
13126 /** | |
13127 * An shared object representing the value 'null'. | |
13128 */ | |
13129 DartObjectImpl _nullObject; | |
13130 | |
13131 /** | |
13132 * The type representing the type 'Null'. | |
13133 */ | |
13134 InterfaceType _nullType; | |
13135 | |
13136 /** | |
13137 * The type representing the built-in type 'num'. | |
13138 */ | |
13139 InterfaceType _numType; | |
13140 | |
13141 /** | |
13142 * The type representing the built-in type 'Object'. | |
13143 */ | |
13144 InterfaceType _objectType; | |
13145 | |
13146 /** | |
13147 * The type representing the built-in type 'StackTrace'. | |
13148 */ | |
13149 InterfaceType _stackTraceType; | |
13150 | |
13151 /** | |
13152 * The type representing 'Stream<dynamic>'. | |
13153 */ | |
13154 InterfaceType _streamDynamicType; | |
13155 | |
13156 /** | |
13157 * The type representing the built-in type 'Stream'. | |
13158 */ | |
13159 InterfaceType _streamType; | |
13160 | |
13161 /** | |
13162 * The type representing the built-in type 'String'. | |
13163 */ | |
13164 InterfaceType _stringType; | |
13165 | |
13166 /** | |
13167 * The type representing the built-in type 'Symbol'. | |
13168 */ | |
13169 InterfaceType _symbolType; | |
13170 | |
13171 /** | |
13172 * The type representing the built-in type 'Type'. | |
13173 */ | |
13174 InterfaceType _typeType; | |
13175 | |
13176 /** | |
13177 * The type representing typenames that can't be resolved. | |
13178 */ | |
13179 DartType _undefinedType; | |
13180 | |
13181 /** | |
13182 * Initialize a newly created type provider to provide the types defined in | |
13183 * the given [coreLibrary] and [asyncLibrary]. | |
13184 */ | |
13185 TypeProviderImpl(LibraryElement coreLibrary, LibraryElement asyncLibrary) { | |
13186 Namespace coreNamespace = | |
13187 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLibrary); | |
13188 Namespace asyncNamespace = | |
13189 new NamespaceBuilder().createPublicNamespaceForLibrary(asyncLibrary); | |
13190 _initializeFrom(coreNamespace, asyncNamespace); | |
13191 } | |
13192 | |
13193 /** | |
13194 * Initialize a newly created type provider to provide the types defined in | |
13195 * the given [Namespace]s. | |
13196 */ | |
13197 TypeProviderImpl.forNamespaces( | |
13198 Namespace coreNamespace, Namespace asyncNamespace) { | |
13199 _initializeFrom(coreNamespace, asyncNamespace); | |
13200 } | |
13201 | |
13202 @override | |
13203 InterfaceType get boolType => _boolType; | |
13204 | |
13205 @override | |
13206 DartType get bottomType => _bottomType; | |
13207 | |
13208 @override | |
13209 InterfaceType get deprecatedType => _deprecatedType; | |
13210 | |
13211 @override | |
13212 InterfaceType get doubleType => _doubleType; | |
13213 | |
13214 @override | |
13215 DartType get dynamicType => _dynamicType; | |
13216 | |
13217 @override | |
13218 InterfaceType get functionType => _functionType; | |
13219 | |
13220 @override | |
13221 InterfaceType get futureDynamicType => _futureDynamicType; | |
13222 | |
13223 @override | |
13224 InterfaceType get futureNullType => _futureNullType; | |
13225 | |
13226 @override | |
13227 InterfaceType get futureType => _futureType; | |
13228 | |
13229 @override | |
13230 InterfaceType get intType => _intType; | |
13231 | |
13232 @override | |
13233 InterfaceType get iterableDynamicType => _iterableDynamicType; | |
13234 | |
13235 @override | |
13236 InterfaceType get iterableType => _iterableType; | |
13237 | |
13238 @override | |
13239 InterfaceType get listType => _listType; | |
13240 | |
13241 @override | |
13242 InterfaceType get mapType => _mapType; | |
13243 | |
13244 @override | |
13245 List<InterfaceType> get nonSubtypableTypes => <InterfaceType>[ | |
13246 nullType, | |
13247 numType, | |
13248 intType, | |
13249 doubleType, | |
13250 boolType, | |
13251 stringType | |
13252 ]; | |
13253 | |
13254 @override | |
13255 DartObjectImpl get nullObject { | |
13256 if (_nullObject == null) { | |
13257 _nullObject = new DartObjectImpl(nullType, NullState.NULL_STATE); | |
13258 } | |
13259 return _nullObject; | |
13260 } | |
13261 | |
13262 @override | |
13263 InterfaceType get nullType => _nullType; | |
13264 | |
13265 @override | |
13266 InterfaceType get numType => _numType; | |
13267 | |
13268 @override | |
13269 InterfaceType get objectType => _objectType; | |
13270 | |
13271 @override | |
13272 InterfaceType get stackTraceType => _stackTraceType; | |
13273 | |
13274 @override | |
13275 InterfaceType get streamDynamicType => _streamDynamicType; | |
13276 | |
13277 @override | |
13278 InterfaceType get streamType => _streamType; | |
13279 | |
13280 @override | |
13281 InterfaceType get stringType => _stringType; | |
13282 | |
13283 @override | |
13284 InterfaceType get symbolType => _symbolType; | |
13285 | |
13286 @override | |
13287 InterfaceType get typeType => _typeType; | |
13288 | |
13289 @override | |
13290 DartType get undefinedType => _undefinedType; | |
13291 | |
13292 /** | |
13293 * Return the type with the given name from the given namespace, or `null` if
there is no | |
13294 * class with the given name. | |
13295 * | |
13296 * @param namespace the namespace in which to search for the given name | |
13297 * @param typeName the name of the type being searched for | |
13298 * @return the type that was found | |
13299 */ | |
13300 InterfaceType _getType(Namespace namespace, String typeName) { | |
13301 Element element = namespace.get(typeName); | |
13302 if (element == null) { | |
13303 AnalysisEngine.instance.logger | |
13304 .logInformation("No definition of type $typeName"); | |
13305 return null; | |
13306 } | |
13307 return (element as ClassElement).type; | |
13308 } | |
13309 | |
13310 /** | |
13311 * Initialize the types provided by this type provider from the given | |
13312 * [Namespace]s. | |
13313 */ | |
13314 void _initializeFrom(Namespace coreNamespace, Namespace asyncNamespace) { | |
13315 _boolType = _getType(coreNamespace, "bool"); | |
13316 _bottomType = BottomTypeImpl.instance; | |
13317 _deprecatedType = _getType(coreNamespace, "Deprecated"); | |
13318 _doubleType = _getType(coreNamespace, "double"); | |
13319 _dynamicType = DynamicTypeImpl.instance; | |
13320 _functionType = _getType(coreNamespace, "Function"); | |
13321 _futureType = _getType(asyncNamespace, "Future"); | |
13322 _intType = _getType(coreNamespace, "int"); | |
13323 _iterableType = _getType(coreNamespace, "Iterable"); | |
13324 _listType = _getType(coreNamespace, "List"); | |
13325 _mapType = _getType(coreNamespace, "Map"); | |
13326 _nullType = _getType(coreNamespace, "Null"); | |
13327 _numType = _getType(coreNamespace, "num"); | |
13328 _objectType = _getType(coreNamespace, "Object"); | |
13329 _stackTraceType = _getType(coreNamespace, "StackTrace"); | |
13330 _streamType = _getType(asyncNamespace, "Stream"); | |
13331 _stringType = _getType(coreNamespace, "String"); | |
13332 _symbolType = _getType(coreNamespace, "Symbol"); | |
13333 _typeType = _getType(coreNamespace, "Type"); | |
13334 _undefinedType = UndefinedTypeImpl.instance; | |
13335 _futureDynamicType = _futureType.substitute4(<DartType>[_dynamicType]); | |
13336 _futureNullType = _futureType.substitute4(<DartType>[_nullType]); | |
13337 _iterableDynamicType = _iterableType.substitute4(<DartType>[_dynamicType]); | |
13338 _streamDynamicType = _streamType.substitute4(<DartType>[_dynamicType]); | |
13339 } | |
13340 } | |
13341 | |
13342 /** | |
13343 * Instances of the class `TypeResolverVisitor` are used to resolve the types as
sociated with | |
13344 * the elements in the element model. This includes the types of superclasses, m
ixins, interfaces, | |
13345 * fields, methods, parameters, and local variables. As a side-effect, this also
finishes building | |
13346 * the type hierarchy. | |
13347 */ | |
13348 class TypeResolverVisitor extends ScopedVisitor { | |
13349 /** | |
13350 * The type representing the type 'dynamic'. | |
13351 */ | |
13352 DartType _dynamicType; | |
13353 | |
13354 /** | |
13355 * The type representing typenames that can't be resolved. | |
13356 */ | |
13357 DartType _undefinedType; | |
13358 | |
13359 /** | |
13360 * The flag specifying if currently visited class references 'super' expressio
n. | |
13361 */ | |
13362 bool _hasReferenceToSuper = false; | |
13363 | |
13364 /** | |
13365 * Initialize a newly created visitor to resolve the nodes in an AST node. | |
13366 * | |
13367 * [definingLibrary] is the element for the library containing the node being | |
13368 * visited. | |
13369 * [source] is the source representing the compilation unit containing the | |
13370 * node being visited. | |
13371 * [typeProvider] is the object used to access the types from the core | |
13372 * library. | |
13373 * [errorListener] is the error listener that will be informed of any errors | |
13374 * that are found during resolution. | |
13375 * [nameScope] is the scope used to resolve identifiers in the node that will | |
13376 * first be visited. If `null` or unspecified, a new [LibraryScope] will be | |
13377 * created based on [definingLibrary] and [typeProvider]. | |
13378 */ | |
13379 TypeResolverVisitor(LibraryElement definingLibrary, Source source, | |
13380 TypeProvider typeProvider, AnalysisErrorListener errorListener, | |
13381 {Scope nameScope}) | |
13382 : super(definingLibrary, source, typeProvider, errorListener, | |
13383 nameScope: nameScope) { | |
13384 _dynamicType = typeProvider.dynamicType; | |
13385 _undefinedType = typeProvider.undefinedType; | |
13386 } | |
13387 | |
13388 @override | |
13389 Object visitAnnotation(Annotation node) { | |
13390 // | |
13391 // Visit annotations, if the annotation is @proxy, on a class, and "proxy" | |
13392 // resolves to the proxy annotation in dart.core, then create create the | |
13393 // ElementAnnotationImpl and set it as the metadata on the enclosing class. | |
13394 // | |
13395 // Element resolution is done in the ElementResolver, and this work will be | |
13396 // done in the general case for all annotations in the ElementResolver. | |
13397 // The reason we resolve this particular element early is so that | |
13398 // ClassElement.isProxy() returns the correct information during all | |
13399 // phases of the ElementResolver. | |
13400 // | |
13401 super.visitAnnotation(node); | |
13402 Identifier identifier = node.name; | |
13403 if (identifier.name.endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) && | |
13404 node.parent is ClassDeclaration) { | |
13405 Element element = nameScope.lookup(identifier, definingLibrary); | |
13406 if (element != null && | |
13407 element.library.isDartCore && | |
13408 element is PropertyAccessorElement) { | |
13409 // This is the @proxy from dart.core | |
13410 ClassDeclaration classDeclaration = node.parent as ClassDeclaration; | |
13411 ElementAnnotationImpl elementAnnotation = | |
13412 new ElementAnnotationImpl(element); | |
13413 node.elementAnnotation = elementAnnotation; | |
13414 (classDeclaration.element as ClassElementImpl).metadata = | |
13415 <ElementAnnotationImpl>[elementAnnotation]; | |
13416 } | |
13417 } | |
13418 return null; | |
13419 } | |
13420 | |
13421 @override | |
13422 Object visitCatchClause(CatchClause node) { | |
13423 super.visitCatchClause(node); | |
13424 SimpleIdentifier exception = node.exceptionParameter; | |
13425 if (exception != null) { | |
13426 // If an 'on' clause is provided the type of the exception parameter is | |
13427 // the type in the 'on' clause. Otherwise, the type of the exception | |
13428 // parameter is 'Object'. | |
13429 TypeName exceptionTypeName = node.exceptionType; | |
13430 DartType exceptionType; | |
13431 if (exceptionTypeName == null) { | |
13432 exceptionType = typeProvider.dynamicType; | |
13433 } else { | |
13434 exceptionType = _getType(exceptionTypeName); | |
13435 } | |
13436 _recordType(exception, exceptionType); | |
13437 Element element = exception.staticElement; | |
13438 if (element is VariableElementImpl) { | |
13439 element.type = exceptionType; | |
13440 } else { | |
13441 // TODO(brianwilkerson) Report the internal error | |
13442 } | |
13443 } | |
13444 SimpleIdentifier stackTrace = node.stackTraceParameter; | |
13445 if (stackTrace != null) { | |
13446 _recordType(stackTrace, typeProvider.stackTraceType); | |
13447 Element element = stackTrace.staticElement; | |
13448 if (element is VariableElementImpl) { | |
13449 element.type = typeProvider.stackTraceType; | |
13450 } else { | |
13451 // TODO(brianwilkerson) Report the internal error | |
13452 } | |
13453 } | |
13454 return null; | |
13455 } | |
13456 | |
13457 @override | |
13458 Object visitClassDeclaration(ClassDeclaration node) { | |
13459 _hasReferenceToSuper = false; | |
13460 super.visitClassDeclaration(node); | |
13461 ClassElementImpl classElement = _getClassElement(node.name); | |
13462 if (classElement != null) { | |
13463 classElement.hasReferenceToSuper = _hasReferenceToSuper; | |
13464 } | |
13465 return null; | |
13466 } | |
13467 | |
13468 @override | |
13469 void visitClassDeclarationInScope(ClassDeclaration node) { | |
13470 super.visitClassDeclarationInScope(node); | |
13471 ExtendsClause extendsClause = node.extendsClause; | |
13472 WithClause withClause = node.withClause; | |
13473 ImplementsClause implementsClause = node.implementsClause; | |
13474 ClassElementImpl classElement = _getClassElement(node.name); | |
13475 InterfaceType superclassType = null; | |
13476 if (extendsClause != null) { | |
13477 ErrorCode errorCode = (withClause == null | |
13478 ? CompileTimeErrorCode.EXTENDS_NON_CLASS | |
13479 : CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS); | |
13480 superclassType = _resolveType(extendsClause.superclass, errorCode, | |
13481 CompileTimeErrorCode.EXTENDS_ENUM, errorCode); | |
13482 if (!identical(superclassType, typeProvider.objectType)) { | |
13483 classElement.validMixin = false; | |
13484 } | |
13485 } | |
13486 if (classElement != null) { | |
13487 if (superclassType == null) { | |
13488 InterfaceType objectType = typeProvider.objectType; | |
13489 if (!identical(classElement.type, objectType)) { | |
13490 superclassType = objectType; | |
13491 } | |
13492 } | |
13493 classElement.supertype = superclassType; | |
13494 } | |
13495 _resolve(classElement, withClause, implementsClause); | |
13496 return null; | |
13497 } | |
13498 | |
13499 @override | |
13500 void visitClassMembersInScope(ClassDeclaration node) { | |
13501 // | |
13502 // Process field declarations before constructors and methods so that the | |
13503 // types of field formal parameters can be correctly resolved. | |
13504 // | |
13505 List<ClassMember> nonFields = new List<ClassMember>(); | |
13506 node.visitChildren( | |
13507 new _TypeResolverVisitor_visitClassMembersInScope(this, nonFields)); | |
13508 int count = nonFields.length; | |
13509 for (int i = 0; i < count; i++) { | |
13510 nonFields[i].accept(this); | |
13511 } | |
13512 } | |
13513 | |
13514 @override | |
13515 Object visitClassTypeAlias(ClassTypeAlias node) { | |
13516 super.visitClassTypeAlias(node); | |
13517 ErrorCode errorCode = CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS; | |
13518 InterfaceType superclassType = _resolveType(node.superclass, errorCode, | |
13519 CompileTimeErrorCode.EXTENDS_ENUM, errorCode); | |
13520 if (superclassType == null) { | |
13521 superclassType = typeProvider.objectType; | |
13522 } | |
13523 ClassElementImpl classElement = _getClassElement(node.name); | |
13524 if (classElement != null) { | |
13525 classElement.supertype = superclassType; | |
13526 } | |
13527 _resolve(classElement, node.withClause, node.implementsClause); | |
13528 return null; | |
13529 } | |
13530 | |
13531 @override | |
13532 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
13533 super.visitConstructorDeclaration(node); | |
13534 ExecutableElementImpl element = node.element as ExecutableElementImpl; | |
13535 if (element == null) { | |
13536 ClassDeclaration classNode = | |
13537 node.getAncestor((node) => node is ClassDeclaration); | |
13538 StringBuffer buffer = new StringBuffer(); | |
13539 buffer.write("The element for the constructor "); | |
13540 buffer.write(node.name == null ? "<unnamed>" : node.name.name); | |
13541 buffer.write(" in "); | |
13542 if (classNode == null) { | |
13543 buffer.write("<unknown class>"); | |
13544 } else { | |
13545 buffer.write(classNode.name.name); | |
13546 } | |
13547 buffer.write(" in "); | |
13548 buffer.write(source.fullName); | |
13549 buffer.write(" was not set while trying to resolve types."); | |
13550 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
13551 new CaughtException(new AnalysisException(), null)); | |
13552 } else { | |
13553 ClassElement definingClass = element.enclosingElement as ClassElement; | |
13554 element.returnType = definingClass.type; | |
13555 FunctionTypeImpl type = new FunctionTypeImpl(element); | |
13556 type.typeArguments = definingClass.type.typeArguments; | |
13557 element.type = type; | |
13558 } | |
13559 return null; | |
13560 } | |
13561 | |
13562 @override | |
13563 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
13564 super.visitDeclaredIdentifier(node); | |
13565 DartType declaredType; | |
13566 TypeName typeName = node.type; | |
13567 if (typeName == null) { | |
13568 declaredType = _dynamicType; | |
13569 } else { | |
13570 declaredType = _getType(typeName); | |
13571 } | |
13572 LocalVariableElementImpl element = node.element as LocalVariableElementImpl; | |
13573 element.type = declaredType; | |
13574 return null; | |
13575 } | |
13576 | |
13577 @override | |
13578 Object visitFieldFormalParameter(FieldFormalParameter node) { | |
13579 super.visitFieldFormalParameter(node); | |
13580 Element element = node.identifier.staticElement; | |
13581 if (element is ParameterElementImpl) { | |
13582 ParameterElementImpl parameter = element; | |
13583 FormalParameterList parameterList = node.parameters; | |
13584 if (parameterList == null) { | |
13585 DartType type; | |
13586 TypeName typeName = node.type; | |
13587 if (typeName == null) { | |
13588 type = _dynamicType; | |
13589 if (parameter is FieldFormalParameterElement) { | |
13590 FieldElement fieldElement = | |
13591 (parameter as FieldFormalParameterElement).field; | |
13592 if (fieldElement != null) { | |
13593 type = fieldElement.type; | |
13594 } | |
13595 } | |
13596 } else { | |
13597 type = _getType(typeName); | |
13598 } | |
13599 parameter.type = type; | |
13600 } else { | |
13601 _setFunctionTypedParameterType(parameter, node.type, node.parameters); | |
13602 } | |
13603 } else { | |
13604 // TODO(brianwilkerson) Report this internal error | |
13605 } | |
13606 return null; | |
13607 } | |
13608 | |
13609 @override | |
13610 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
13611 super.visitFunctionDeclaration(node); | |
13612 ExecutableElementImpl element = node.element as ExecutableElementImpl; | |
13613 if (element == null) { | |
13614 StringBuffer buffer = new StringBuffer(); | |
13615 buffer.write("The element for the top-level function "); | |
13616 buffer.write(node.name); | |
13617 buffer.write(" in "); | |
13618 buffer.write(source.fullName); | |
13619 buffer.write(" was not set while trying to resolve types."); | |
13620 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
13621 new CaughtException(new AnalysisException(), null)); | |
13622 } | |
13623 element.returnType = _computeReturnType(node.returnType); | |
13624 FunctionTypeImpl type = new FunctionTypeImpl(element); | |
13625 ClassElement definingClass = | |
13626 element.getAncestor((element) => element is ClassElement); | |
13627 if (definingClass != null) { | |
13628 type.typeArguments = definingClass.type.typeArguments; | |
13629 } | |
13630 element.type = type; | |
13631 return null; | |
13632 } | |
13633 | |
13634 @override | |
13635 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
13636 FunctionTypeAliasElementImpl element = | |
13637 node.element as FunctionTypeAliasElementImpl; | |
13638 super.visitFunctionTypeAlias(node); | |
13639 element.returnType = _computeReturnType(node.returnType); | |
13640 return null; | |
13641 } | |
13642 | |
13643 @override | |
13644 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
13645 super.visitFunctionTypedFormalParameter(node); | |
13646 Element element = node.identifier.staticElement; | |
13647 if (element is ParameterElementImpl) { | |
13648 _setFunctionTypedParameterType(element, node.returnType, node.parameters); | |
13649 } else { | |
13650 // TODO(brianwilkerson) Report this internal error | |
13651 } | |
13652 return null; | |
13653 } | |
13654 | |
13655 @override | |
13656 Object visitMethodDeclaration(MethodDeclaration node) { | |
13657 super.visitMethodDeclaration(node); | |
13658 ExecutableElementImpl element = node.element as ExecutableElementImpl; | |
13659 if (element == null) { | |
13660 ClassDeclaration classNode = | |
13661 node.getAncestor((node) => node is ClassDeclaration); | |
13662 StringBuffer buffer = new StringBuffer(); | |
13663 buffer.write("The element for the method "); | |
13664 buffer.write(node.name.name); | |
13665 buffer.write(" in "); | |
13666 if (classNode == null) { | |
13667 buffer.write("<unknown class>"); | |
13668 } else { | |
13669 buffer.write(classNode.name.name); | |
13670 } | |
13671 buffer.write(" in "); | |
13672 buffer.write(source.fullName); | |
13673 buffer.write(" was not set while trying to resolve types."); | |
13674 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
13675 new CaughtException(new AnalysisException(), null)); | |
13676 } | |
13677 element.returnType = _computeReturnType(node.returnType); | |
13678 FunctionTypeImpl type = new FunctionTypeImpl(element); | |
13679 ClassElement definingClass = | |
13680 element.getAncestor((element) => element is ClassElement); | |
13681 if (definingClass != null) { | |
13682 type.typeArguments = definingClass.type.typeArguments; | |
13683 } | |
13684 element.type = type; | |
13685 if (element is PropertyAccessorElement) { | |
13686 PropertyAccessorElement accessor = element as PropertyAccessorElement; | |
13687 PropertyInducingElementImpl variable = | |
13688 accessor.variable as PropertyInducingElementImpl; | |
13689 if (accessor.isGetter) { | |
13690 variable.type = type.returnType; | |
13691 } else if (variable.type == null) { | |
13692 List<DartType> parameterTypes = type.normalParameterTypes; | |
13693 if (parameterTypes != null && parameterTypes.length > 0) { | |
13694 variable.type = parameterTypes[0]; | |
13695 } | |
13696 } | |
13697 } | |
13698 return null; | |
13699 } | |
13700 | |
13701 @override | |
13702 Object visitSimpleFormalParameter(SimpleFormalParameter node) { | |
13703 super.visitSimpleFormalParameter(node); | |
13704 DartType declaredType; | |
13705 TypeName typeName = node.type; | |
13706 if (typeName == null) { | |
13707 declaredType = _dynamicType; | |
13708 } else { | |
13709 declaredType = _getType(typeName); | |
13710 } | |
13711 Element element = node.identifier.staticElement; | |
13712 if (element is ParameterElement) { | |
13713 (element as ParameterElementImpl).type = declaredType; | |
13714 } else { | |
13715 // TODO(brianwilkerson) Report the internal error. | |
13716 } | |
13717 return null; | |
13718 } | |
13719 | |
13720 @override | |
13721 Object visitSuperExpression(SuperExpression node) { | |
13722 _hasReferenceToSuper = true; | |
13723 return super.visitSuperExpression(node); | |
13724 } | |
13725 | |
13726 @override | |
13727 Object visitTypeName(TypeName node) { | |
13728 super.visitTypeName(node); | |
13729 Identifier typeName = node.name; | |
13730 TypeArgumentList argumentList = node.typeArguments; | |
13731 Element element = nameScope.lookup(typeName, definingLibrary); | |
13732 if (element == null) { | |
13733 // | |
13734 // Check to see whether the type name is either 'dynamic' or 'void', | |
13735 // neither of which are in the name scope and hence will not be found by | |
13736 // normal means. | |
13737 // | |
13738 if (typeName.name == _dynamicType.name) { | |
13739 _setElement(typeName, _dynamicType.element); | |
13740 if (argumentList != null) { | |
13741 // TODO(brianwilkerson) Report this error | |
13742 // reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGU
MENTS, node, dynamicType.getName(), 0, argumentList.getArguments().size()); | |
13743 } | |
13744 typeName.staticType = _dynamicType; | |
13745 node.type = _dynamicType; | |
13746 return null; | |
13747 } | |
13748 VoidTypeImpl voidType = VoidTypeImpl.instance; | |
13749 if (typeName.name == voidType.name) { | |
13750 // There is no element for 'void'. | |
13751 if (argumentList != null) { | |
13752 // TODO(brianwilkerson) Report this error | |
13753 // reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGU
MENTS, node, voidType.getName(), 0, argumentList.getArguments().size()); | |
13754 } | |
13755 typeName.staticType = voidType; | |
13756 node.type = voidType; | |
13757 return null; | |
13758 } | |
13759 // | |
13760 // If not, the look to see whether we might have created the wrong AST | |
13761 // structure for a constructor name. If so, fix the AST structure and then | |
13762 // proceed. | |
13763 // | |
13764 AstNode parent = node.parent; | |
13765 if (typeName is PrefixedIdentifier && | |
13766 parent is ConstructorName && | |
13767 argumentList == null) { | |
13768 ConstructorName name = parent; | |
13769 if (name.name == null) { | |
13770 PrefixedIdentifier prefixedIdentifier = | |
13771 typeName as PrefixedIdentifier; | |
13772 SimpleIdentifier prefix = prefixedIdentifier.prefix; | |
13773 element = nameScope.lookup(prefix, definingLibrary); | |
13774 if (element is PrefixElement) { | |
13775 if (parent.parent is InstanceCreationExpression && | |
13776 (parent.parent as InstanceCreationExpression).isConst) { | |
13777 // If, if this is a const expression, then generate a | |
13778 // CompileTimeErrorCode.CONST_WITH_NON_TYPE error. | |
13779 reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE, | |
13780 prefixedIdentifier.identifier, | |
13781 [prefixedIdentifier.identifier.name]); | |
13782 } else { | |
13783 // Else, if this expression is a new expression, report a | |
13784 // NEW_WITH_NON_TYPE warning. | |
13785 reportErrorForNode(StaticWarningCode.NEW_WITH_NON_TYPE, | |
13786 prefixedIdentifier.identifier, | |
13787 [prefixedIdentifier.identifier.name]); | |
13788 } | |
13789 _setElement(prefix, element); | |
13790 return null; | |
13791 } else if (element != null) { | |
13792 // | |
13793 // Rewrite the constructor name. The parser, when it sees a | |
13794 // constructor named "a.b", cannot tell whether "a" is a prefix and | |
13795 // "b" is a class name, or whether "a" is a class name and "b" is a | |
13796 // constructor name. It arbitrarily chooses the former, but in this | |
13797 // case was wrong. | |
13798 // | |
13799 name.name = prefixedIdentifier.identifier; | |
13800 name.period = prefixedIdentifier.period; | |
13801 node.name = prefix; | |
13802 typeName = prefix; | |
13803 } | |
13804 } | |
13805 } | |
13806 } | |
13807 // check element | |
13808 bool elementValid = element is! MultiplyDefinedElement; | |
13809 if (elementValid && | |
13810 element is! ClassElement && | |
13811 _isTypeNameInInstanceCreationExpression(node)) { | |
13812 SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName); | |
13813 InstanceCreationExpression creation = | |
13814 node.parent.parent as InstanceCreationExpression; | |
13815 if (creation.isConst) { | |
13816 if (element == null) { | |
13817 reportErrorForNode( | |
13818 CompileTimeErrorCode.UNDEFINED_CLASS, typeNameSimple, [typeName]); | |
13819 } else { | |
13820 reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE, | |
13821 typeNameSimple, [typeName]); | |
13822 } | |
13823 elementValid = false; | |
13824 } else { | |
13825 if (element != null) { | |
13826 reportErrorForNode( | |
13827 StaticWarningCode.NEW_WITH_NON_TYPE, typeNameSimple, [typeName]); | |
13828 elementValid = false; | |
13829 } | |
13830 } | |
13831 } | |
13832 if (elementValid && element == null) { | |
13833 // We couldn't resolve the type name. | |
13834 // TODO(jwren) Consider moving the check for | |
13835 // CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE from the | |
13836 // ErrorVerifier, so that we don't have two errors on a built in | |
13837 // identifier being used as a class name. | |
13838 // See CompileTimeErrorCodeTest.test_builtInIdentifierAsType(). | |
13839 SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName); | |
13840 RedirectingConstructorKind redirectingConstructorKind; | |
13841 if (_isBuiltInIdentifier(node) && _isTypeAnnotation(node)) { | |
13842 reportErrorForNode(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, | |
13843 typeName, [typeName.name]); | |
13844 } else if (typeNameSimple.name == "boolean") { | |
13845 reportErrorForNode( | |
13846 StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, typeNameSimple, []); | |
13847 } else if (_isTypeNameInCatchClause(node)) { | |
13848 reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, | |
13849 [typeName.name]); | |
13850 } else if (_isTypeNameInAsExpression(node)) { | |
13851 reportErrorForNode( | |
13852 StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]); | |
13853 } else if (_isTypeNameInIsExpression(node)) { | |
13854 reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_UNDEFINED_NAME, | |
13855 typeName, [typeName.name]); | |
13856 } else if ((redirectingConstructorKind = | |
13857 _getRedirectingConstructorKind(node)) != | |
13858 null) { | |
13859 ErrorCode errorCode = (redirectingConstructorKind == | |
13860 RedirectingConstructorKind.CONST | |
13861 ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS | |
13862 : StaticWarningCode.REDIRECT_TO_NON_CLASS); | |
13863 reportErrorForNode(errorCode, typeName, [typeName.name]); | |
13864 } else if (_isTypeNameInTypeArgumentList(node)) { | |
13865 reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, | |
13866 typeName, [typeName.name]); | |
13867 } else { | |
13868 reportErrorForNode( | |
13869 StaticWarningCode.UNDEFINED_CLASS, typeName, [typeName.name]); | |
13870 } | |
13871 elementValid = false; | |
13872 } | |
13873 if (!elementValid) { | |
13874 if (element is MultiplyDefinedElement) { | |
13875 _setElement(typeName, element); | |
13876 } else { | |
13877 _setElement(typeName, _dynamicType.element); | |
13878 } | |
13879 typeName.staticType = _undefinedType; | |
13880 node.type = _undefinedType; | |
13881 return null; | |
13882 } | |
13883 DartType type = null; | |
13884 if (element is ClassElement) { | |
13885 _setElement(typeName, element); | |
13886 type = element.type; | |
13887 } else if (element is FunctionTypeAliasElement) { | |
13888 _setElement(typeName, element); | |
13889 type = element.type; | |
13890 } else if (element is TypeParameterElement) { | |
13891 _setElement(typeName, element); | |
13892 type = element.type; | |
13893 if (argumentList != null) { | |
13894 // Type parameters cannot have type arguments. | |
13895 // TODO(brianwilkerson) Report this error. | |
13896 // resolver.reportError(ResolverErrorCode.?, keyType); | |
13897 } | |
13898 } else if (element is MultiplyDefinedElement) { | |
13899 List<Element> elements = element.conflictingElements; | |
13900 type = _getTypeWhenMultiplyDefined(elements); | |
13901 if (type != null) { | |
13902 node.type = type; | |
13903 } | |
13904 } else { | |
13905 // The name does not represent a type. | |
13906 RedirectingConstructorKind redirectingConstructorKind; | |
13907 if (_isTypeNameInCatchClause(node)) { | |
13908 reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, | |
13909 [typeName.name]); | |
13910 } else if (_isTypeNameInAsExpression(node)) { | |
13911 reportErrorForNode( | |
13912 StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]); | |
13913 } else if (_isTypeNameInIsExpression(node)) { | |
13914 reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_NON_TYPE, typeName, | |
13915 [typeName.name]); | |
13916 } else if ((redirectingConstructorKind = | |
13917 _getRedirectingConstructorKind(node)) != | |
13918 null) { | |
13919 ErrorCode errorCode = (redirectingConstructorKind == | |
13920 RedirectingConstructorKind.CONST | |
13921 ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS | |
13922 : StaticWarningCode.REDIRECT_TO_NON_CLASS); | |
13923 reportErrorForNode(errorCode, typeName, [typeName.name]); | |
13924 } else if (_isTypeNameInTypeArgumentList(node)) { | |
13925 reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, | |
13926 typeName, [typeName.name]); | |
13927 } else { | |
13928 AstNode parent = typeName.parent; | |
13929 while (parent is TypeName) { | |
13930 parent = parent.parent; | |
13931 } | |
13932 if (parent is ExtendsClause || | |
13933 parent is ImplementsClause || | |
13934 parent is WithClause || | |
13935 parent is ClassTypeAlias) { | |
13936 // Ignored. The error will be reported elsewhere. | |
13937 } else { | |
13938 reportErrorForNode( | |
13939 StaticWarningCode.NOT_A_TYPE, typeName, [typeName.name]); | |
13940 } | |
13941 } | |
13942 _setElement(typeName, _dynamicType.element); | |
13943 typeName.staticType = _dynamicType; | |
13944 node.type = _dynamicType; | |
13945 return null; | |
13946 } | |
13947 if (argumentList != null) { | |
13948 NodeList<TypeName> arguments = argumentList.arguments; | |
13949 int argumentCount = arguments.length; | |
13950 List<DartType> parameters = _getTypeArguments(type); | |
13951 int parameterCount = parameters.length; | |
13952 List<DartType> typeArguments = new List<DartType>(parameterCount); | |
13953 if (argumentCount == parameterCount) { | |
13954 for (int i = 0; i < parameterCount; i++) { | |
13955 TypeName argumentTypeName = arguments[i]; | |
13956 DartType argumentType = _getType(argumentTypeName); | |
13957 if (argumentType == null) { | |
13958 argumentType = _dynamicType; | |
13959 } | |
13960 typeArguments[i] = argumentType; | |
13961 } | |
13962 } else { | |
13963 reportErrorForNode(_getInvalidTypeParametersErrorCode(node), node, [ | |
13964 typeName.name, | |
13965 parameterCount, | |
13966 argumentCount | |
13967 ]); | |
13968 for (int i = 0; i < parameterCount; i++) { | |
13969 typeArguments[i] = _dynamicType; | |
13970 } | |
13971 } | |
13972 if (type is InterfaceTypeImpl) { | |
13973 InterfaceTypeImpl interfaceType = type as InterfaceTypeImpl; | |
13974 type = interfaceType.substitute4(typeArguments); | |
13975 } else if (type is FunctionTypeImpl) { | |
13976 FunctionTypeImpl functionType = type as FunctionTypeImpl; | |
13977 type = functionType.substitute3(typeArguments); | |
13978 } else { | |
13979 // TODO(brianwilkerson) Report this internal error. | |
13980 } | |
13981 } else { | |
13982 // | |
13983 // Check for the case where there are no type arguments given for a | |
13984 // parameterized type. | |
13985 // | |
13986 List<DartType> parameters = _getTypeArguments(type); | |
13987 int parameterCount = parameters.length; | |
13988 if (parameterCount > 0) { | |
13989 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance; | |
13990 List<DartType> arguments = new List<DartType>(parameterCount); | |
13991 for (int i = 0; i < parameterCount; i++) { | |
13992 arguments[i] = dynamicType; | |
13993 } | |
13994 type = type.substitute2(arguments, parameters); | |
13995 } | |
13996 } | |
13997 typeName.staticType = type; | |
13998 node.type = type; | |
13999 return null; | |
14000 } | |
14001 | |
14002 @override | |
14003 Object visitTypeParameter(TypeParameter node) { | |
14004 super.visitTypeParameter(node); | |
14005 TypeName bound = node.bound; | |
14006 if (bound != null) { | |
14007 TypeParameterElementImpl typeParameter = | |
14008 node.name.staticElement as TypeParameterElementImpl; | |
14009 if (typeParameter != null) { | |
14010 typeParameter.bound = bound.type; | |
14011 } | |
14012 } | |
14013 return null; | |
14014 } | |
14015 | |
14016 @override | |
14017 Object visitVariableDeclaration(VariableDeclaration node) { | |
14018 super.visitVariableDeclaration(node); | |
14019 DartType declaredType; | |
14020 TypeName typeName = (node.parent as VariableDeclarationList).type; | |
14021 if (typeName == null) { | |
14022 declaredType = _dynamicType; | |
14023 } else { | |
14024 declaredType = _getType(typeName); | |
14025 } | |
14026 Element element = node.name.staticElement; | |
14027 if (element is VariableElement) { | |
14028 (element as VariableElementImpl).type = declaredType; | |
14029 if (element is PropertyInducingElement) { | |
14030 PropertyInducingElement variableElement = element; | |
14031 PropertyAccessorElementImpl getter = | |
14032 variableElement.getter as PropertyAccessorElementImpl; | |
14033 getter.returnType = declaredType; | |
14034 FunctionTypeImpl getterType = new FunctionTypeImpl(getter); | |
14035 ClassElement definingClass = | |
14036 element.getAncestor((element) => element is ClassElement); | |
14037 if (definingClass != null) { | |
14038 getterType.typeArguments = definingClass.type.typeArguments; | |
14039 } | |
14040 getter.type = getterType; | |
14041 PropertyAccessorElementImpl setter = | |
14042 variableElement.setter as PropertyAccessorElementImpl; | |
14043 if (setter != null) { | |
14044 List<ParameterElement> parameters = setter.parameters; | |
14045 if (parameters.length > 0) { | |
14046 (parameters[0] as ParameterElementImpl).type = declaredType; | |
14047 } | |
14048 setter.returnType = VoidTypeImpl.instance; | |
14049 FunctionTypeImpl setterType = new FunctionTypeImpl(setter); | |
14050 if (definingClass != null) { | |
14051 setterType.typeArguments = definingClass.type.typeArguments; | |
14052 } | |
14053 setter.type = setterType; | |
14054 } | |
14055 } | |
14056 } else { | |
14057 // TODO(brianwilkerson) Report the internal error. | |
14058 } | |
14059 return null; | |
14060 } | |
14061 | |
14062 /** | |
14063 * Given a type name representing the return type of a function, compute the r
eturn type of the | |
14064 * function. | |
14065 * | |
14066 * @param returnType the type name representing the return type of the functio
n | |
14067 * @return the return type that was computed | |
14068 */ | |
14069 DartType _computeReturnType(TypeName returnType) { | |
14070 if (returnType == null) { | |
14071 return _dynamicType; | |
14072 } else { | |
14073 return returnType.type; | |
14074 } | |
14075 } | |
14076 | |
14077 /** | |
14078 * Return the class element that represents the class whose name was provided. | |
14079 * | |
14080 * @param identifier the name from the declaration of a class | |
14081 * @return the class element that represents the class | |
14082 */ | |
14083 ClassElementImpl _getClassElement(SimpleIdentifier identifier) { | |
14084 // TODO(brianwilkerson) Seems like we should be using | |
14085 // ClassDeclaration.getElement(). | |
14086 if (identifier == null) { | |
14087 // TODO(brianwilkerson) Report this | |
14088 // Internal error: We should never build a class declaration without a | |
14089 // name. | |
14090 return null; | |
14091 } | |
14092 Element element = identifier.staticElement; | |
14093 if (element is! ClassElementImpl) { | |
14094 // TODO(brianwilkerson) Report this | |
14095 // Internal error: Failed to create an element for a class declaration. | |
14096 return null; | |
14097 } | |
14098 return element as ClassElementImpl; | |
14099 } | |
14100 | |
14101 /** | |
14102 * Return an array containing all of the elements associated with the paramete
rs in the given | |
14103 * list. | |
14104 * | |
14105 * @param parameterList the list of parameters whose elements are to be return
ed | |
14106 * @return the elements associated with the parameters | |
14107 */ | |
14108 List<ParameterElement> _getElements(FormalParameterList parameterList) { | |
14109 List<ParameterElement> elements = new List<ParameterElement>(); | |
14110 for (FormalParameter parameter in parameterList.parameters) { | |
14111 ParameterElement element = | |
14112 parameter.identifier.staticElement as ParameterElement; | |
14113 // TODO(brianwilkerson) Understand why the element would be null. | |
14114 if (element != null) { | |
14115 elements.add(element); | |
14116 } | |
14117 } | |
14118 return elements; | |
14119 } | |
14120 | |
14121 /** | |
14122 * The number of type arguments in the given type name does not match the numb
er of parameters in | |
14123 * the corresponding class element. Return the error code that should be used
to report this | |
14124 * error. | |
14125 * | |
14126 * @param node the type name with the wrong number of type arguments | |
14127 * @return the error code that should be used to report that the wrong number
of type arguments | |
14128 * were provided | |
14129 */ | |
14130 ErrorCode _getInvalidTypeParametersErrorCode(TypeName node) { | |
14131 AstNode parent = node.parent; | |
14132 if (parent is ConstructorName) { | |
14133 parent = parent.parent; | |
14134 if (parent is InstanceCreationExpression) { | |
14135 if (parent.isConst) { | |
14136 return CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS; | |
14137 } else { | |
14138 return StaticWarningCode.NEW_WITH_INVALID_TYPE_PARAMETERS; | |
14139 } | |
14140 } | |
14141 } | |
14142 return StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS; | |
14143 } | |
14144 | |
14145 /** | |
14146 * Checks if the given type name is the target in a redirected constructor. | |
14147 * | |
14148 * @param typeName the type name to analyze | |
14149 * @return some [RedirectingConstructorKind] if the given type name is used as
the type in a | |
14150 * redirected constructor, or `null` otherwise | |
14151 */ | |
14152 RedirectingConstructorKind _getRedirectingConstructorKind(TypeName typeName) { | |
14153 AstNode parent = typeName.parent; | |
14154 if (parent is ConstructorName) { | |
14155 ConstructorName constructorName = parent as ConstructorName; | |
14156 parent = constructorName.parent; | |
14157 if (parent is ConstructorDeclaration) { | |
14158 if (identical(parent.redirectedConstructor, constructorName)) { | |
14159 if (parent.constKeyword != null) { | |
14160 return RedirectingConstructorKind.CONST; | |
14161 } | |
14162 return RedirectingConstructorKind.NORMAL; | |
14163 } | |
14164 } | |
14165 } | |
14166 return null; | |
14167 } | |
14168 | |
14169 /** | |
14170 * Return the type represented by the given type name. | |
14171 * | |
14172 * @param typeName the type name representing the type to be returned | |
14173 * @return the type represented by the type name | |
14174 */ | |
14175 DartType _getType(TypeName typeName) { | |
14176 DartType type = typeName.type; | |
14177 if (type == null) { | |
14178 return _undefinedType; | |
14179 } | |
14180 return type; | |
14181 } | |
14182 | |
14183 /** | |
14184 * Return the type arguments associated with the given type. | |
14185 * | |
14186 * @param type the type whole type arguments are to be returned | |
14187 * @return the type arguments associated with the given type | |
14188 */ | |
14189 List<DartType> _getTypeArguments(DartType type) { | |
14190 if (type is InterfaceType) { | |
14191 return type.typeArguments; | |
14192 } else if (type is FunctionType) { | |
14193 return type.typeArguments; | |
14194 } | |
14195 return DartType.EMPTY_LIST; | |
14196 } | |
14197 | |
14198 /** | |
14199 * Returns the simple identifier of the given (may be qualified) type name. | |
14200 * | |
14201 * @param typeName the (may be qualified) qualified type name | |
14202 * @return the simple identifier of the given (may be qualified) type name. | |
14203 */ | |
14204 SimpleIdentifier _getTypeSimpleIdentifier(Identifier typeName) { | |
14205 if (typeName is SimpleIdentifier) { | |
14206 return typeName; | |
14207 } else { | |
14208 return (typeName as PrefixedIdentifier).identifier; | |
14209 } | |
14210 } | |
14211 | |
14212 /** | |
14213 * Given the multiple elements to which a single name could potentially be res
olved, return the | |
14214 * single interface type that should be used, or `null` if there is no clear c
hoice. | |
14215 * | |
14216 * @param elements the elements to which a single name could potentially be re
solved | |
14217 * @return the single interface type that should be used for the type name | |
14218 */ | |
14219 InterfaceType _getTypeWhenMultiplyDefined(List<Element> elements) { | |
14220 InterfaceType type = null; | |
14221 for (Element element in elements) { | |
14222 if (element is ClassElement) { | |
14223 if (type != null) { | |
14224 return null; | |
14225 } | |
14226 type = element.type; | |
14227 } | |
14228 } | |
14229 return type; | |
14230 } | |
14231 | |
14232 /** | |
14233 * Checks if the given type name is used as the type in an as expression. | |
14234 * | |
14235 * @param typeName the type name to analyzer | |
14236 * @return `true` if the given type name is used as the type in an as expressi
on | |
14237 */ | |
14238 bool _isTypeNameInAsExpression(TypeName typeName) { | |
14239 AstNode parent = typeName.parent; | |
14240 if (parent is AsExpression) { | |
14241 AsExpression asExpression = parent; | |
14242 return identical(asExpression.type, typeName); | |
14243 } | |
14244 return false; | |
14245 } | |
14246 | |
14247 /** | |
14248 * Checks if the given type name is used as the exception type in a catch clau
se. | |
14249 * | |
14250 * @param typeName the type name to analyzer | |
14251 * @return `true` if the given type name is used as the exception type in a ca
tch clause | |
14252 */ | |
14253 bool _isTypeNameInCatchClause(TypeName typeName) { | |
14254 AstNode parent = typeName.parent; | |
14255 if (parent is CatchClause) { | |
14256 CatchClause catchClause = parent; | |
14257 return identical(catchClause.exceptionType, typeName); | |
14258 } | |
14259 return false; | |
14260 } | |
14261 | |
14262 /** | |
14263 * Checks if the given type name is used as the type in an instance creation e
xpression. | |
14264 * | |
14265 * @param typeName the type name to analyzer | |
14266 * @return `true` if the given type name is used as the type in an instance cr
eation | |
14267 * expression | |
14268 */ | |
14269 bool _isTypeNameInInstanceCreationExpression(TypeName typeName) { | |
14270 AstNode parent = typeName.parent; | |
14271 if (parent is ConstructorName && | |
14272 parent.parent is InstanceCreationExpression) { | |
14273 ConstructorName constructorName = parent; | |
14274 return constructorName != null && | |
14275 identical(constructorName.type, typeName); | |
14276 } | |
14277 return false; | |
14278 } | |
14279 | |
14280 /** | |
14281 * Checks if the given type name is used as the type in an is expression. | |
14282 * | |
14283 * @param typeName the type name to analyzer | |
14284 * @return `true` if the given type name is used as the type in an is expressi
on | |
14285 */ | |
14286 bool _isTypeNameInIsExpression(TypeName typeName) { | |
14287 AstNode parent = typeName.parent; | |
14288 if (parent is IsExpression) { | |
14289 IsExpression isExpression = parent; | |
14290 return identical(isExpression.type, typeName); | |
14291 } | |
14292 return false; | |
14293 } | |
14294 | |
14295 /** | |
14296 * Checks if the given type name used in a type argument list. | |
14297 * | |
14298 * @param typeName the type name to analyzer | |
14299 * @return `true` if the given type name is in a type argument list | |
14300 */ | |
14301 bool _isTypeNameInTypeArgumentList(TypeName typeName) => | |
14302 typeName.parent is TypeArgumentList; | |
14303 | |
14304 /** | |
14305 * Record that the static type of the given node is the given type. | |
14306 * | |
14307 * @param expression the node whose type is to be recorded | |
14308 * @param type the static type of the node | |
14309 */ | |
14310 Object _recordType(Expression expression, DartType type) { | |
14311 if (type == null) { | |
14312 expression.staticType = _dynamicType; | |
14313 } else { | |
14314 expression.staticType = type; | |
14315 } | |
14316 return null; | |
14317 } | |
14318 | |
14319 /** | |
14320 * Resolve the types in the given with and implements clauses and associate th
ose types with the | |
14321 * given class element. | |
14322 * | |
14323 * @param classElement the class element with which the mixin and interface ty
pes are to be | |
14324 * associated | |
14325 * @param withClause the with clause to be resolved | |
14326 * @param implementsClause the implements clause to be resolved | |
14327 */ | |
14328 void _resolve(ClassElementImpl classElement, WithClause withClause, | |
14329 ImplementsClause implementsClause) { | |
14330 if (withClause != null) { | |
14331 List<InterfaceType> mixinTypes = _resolveTypes(withClause.mixinTypes, | |
14332 CompileTimeErrorCode.MIXIN_OF_NON_CLASS, | |
14333 CompileTimeErrorCode.MIXIN_OF_ENUM, | |
14334 CompileTimeErrorCode.MIXIN_OF_NON_CLASS); | |
14335 if (classElement != null) { | |
14336 classElement.mixins = mixinTypes; | |
14337 classElement.withClauseRange = | |
14338 new SourceRange(withClause.offset, withClause.length); | |
14339 } | |
14340 } | |
14341 if (implementsClause != null) { | |
14342 NodeList<TypeName> interfaces = implementsClause.interfaces; | |
14343 List<InterfaceType> interfaceTypes = _resolveTypes(interfaces, | |
14344 CompileTimeErrorCode.IMPLEMENTS_NON_CLASS, | |
14345 CompileTimeErrorCode.IMPLEMENTS_ENUM, | |
14346 CompileTimeErrorCode.IMPLEMENTS_DYNAMIC); | |
14347 if (classElement != null) { | |
14348 classElement.interfaces = interfaceTypes; | |
14349 } | |
14350 // TODO(brianwilkerson) Move the following checks to ErrorVerifier. | |
14351 int count = interfaces.length; | |
14352 List<bool> detectedRepeatOnIndex = new List<bool>.filled(count, false); | |
14353 for (int i = 0; i < detectedRepeatOnIndex.length; i++) { | |
14354 detectedRepeatOnIndex[i] = false; | |
14355 } | |
14356 for (int i = 0; i < count; i++) { | |
14357 TypeName typeName = interfaces[i]; | |
14358 if (!detectedRepeatOnIndex[i]) { | |
14359 Element element = typeName.name.staticElement; | |
14360 for (int j = i + 1; j < count; j++) { | |
14361 TypeName typeName2 = interfaces[j]; | |
14362 Identifier identifier2 = typeName2.name; | |
14363 String name2 = identifier2.name; | |
14364 Element element2 = identifier2.staticElement; | |
14365 if (element != null && element == element2) { | |
14366 detectedRepeatOnIndex[j] = true; | |
14367 reportErrorForNode( | |
14368 CompileTimeErrorCode.IMPLEMENTS_REPEATED, typeName2, [name2]); | |
14369 } | |
14370 } | |
14371 } | |
14372 } | |
14373 } | |
14374 } | |
14375 | |
14376 /** | |
14377 * Return the type specified by the given name. | |
14378 * | |
14379 * @param typeName the type name specifying the type to be returned | |
14380 * @param nonTypeError the error to produce if the type name is defined to be
something other than | |
14381 * a type | |
14382 * @param enumTypeError the error to produce if the type name is defined to be
an enum | |
14383 * @param dynamicTypeError the error to produce if the type name is "dynamic" | |
14384 * @return the type specified by the type name | |
14385 */ | |
14386 InterfaceType _resolveType(TypeName typeName, ErrorCode nonTypeError, | |
14387 ErrorCode enumTypeError, ErrorCode dynamicTypeError) { | |
14388 DartType type = typeName.type; | |
14389 if (type is InterfaceType) { | |
14390 ClassElement element = type.element; | |
14391 if (element != null && element.isEnum) { | |
14392 reportErrorForNode(enumTypeError, typeName); | |
14393 return null; | |
14394 } | |
14395 return type; | |
14396 } | |
14397 // If the type is not an InterfaceType, then visitTypeName() sets the type | |
14398 // to be a DynamicTypeImpl | |
14399 Identifier name = typeName.name; | |
14400 if (name.name == sc.Keyword.DYNAMIC.syntax) { | |
14401 reportErrorForNode(dynamicTypeError, name, [name.name]); | |
14402 } else { | |
14403 reportErrorForNode(nonTypeError, name, [name.name]); | |
14404 } | |
14405 return null; | |
14406 } | |
14407 | |
14408 /** | |
14409 * Resolve the types in the given list of type names. | |
14410 * | |
14411 * @param typeNames the type names to be resolved | |
14412 * @param nonTypeError the error to produce if the type name is defined to be
something other than | |
14413 * a type | |
14414 * @param enumTypeError the error to produce if the type name is defined to be
an enum | |
14415 * @param dynamicTypeError the error to produce if the type name is "dynamic" | |
14416 * @return an array containing all of the types that were resolved. | |
14417 */ | |
14418 List<InterfaceType> _resolveTypes(NodeList<TypeName> typeNames, | |
14419 ErrorCode nonTypeError, ErrorCode enumTypeError, | |
14420 ErrorCode dynamicTypeError) { | |
14421 List<InterfaceType> types = new List<InterfaceType>(); | |
14422 for (TypeName typeName in typeNames) { | |
14423 InterfaceType type = | |
14424 _resolveType(typeName, nonTypeError, enumTypeError, dynamicTypeError); | |
14425 if (type != null) { | |
14426 types.add(type); | |
14427 } | |
14428 } | |
14429 return types; | |
14430 } | |
14431 | |
14432 void _setElement(Identifier typeName, Element element) { | |
14433 if (element != null) { | |
14434 if (typeName is SimpleIdentifier) { | |
14435 typeName.staticElement = element; | |
14436 } else if (typeName is PrefixedIdentifier) { | |
14437 PrefixedIdentifier identifier = typeName; | |
14438 identifier.identifier.staticElement = element; | |
14439 SimpleIdentifier prefix = identifier.prefix; | |
14440 Element prefixElement = nameScope.lookup(prefix, definingLibrary); | |
14441 if (prefixElement != null) { | |
14442 prefix.staticElement = prefixElement; | |
14443 } | |
14444 } | |
14445 } | |
14446 } | |
14447 | |
14448 /** | |
14449 * Given a parameter element, create a function type based on the given return
type and parameter | |
14450 * list and associate the created type with the element. | |
14451 * | |
14452 * @param element the parameter element whose type is to be set | |
14453 * @param returnType the (possibly `null`) return type of the function | |
14454 * @param parameterList the list of parameters to the function | |
14455 */ | |
14456 void _setFunctionTypedParameterType(ParameterElementImpl element, | |
14457 TypeName returnType, FormalParameterList parameterList) { | |
14458 List<ParameterElement> parameters = _getElements(parameterList); | |
14459 FunctionTypeAliasElementImpl aliasElement = | |
14460 new FunctionTypeAliasElementImpl.forNode(null); | |
14461 aliasElement.synthetic = true; | |
14462 aliasElement.shareParameters(parameters); | |
14463 aliasElement.returnType = _computeReturnType(returnType); | |
14464 // FunctionTypeAliasElementImpl assumes the enclosing element is a | |
14465 // CompilationUnitElement (because non-synthetic function types can only be | |
14466 // declared at top level), so to avoid breaking things, go find the | |
14467 // compilation unit element. | |
14468 aliasElement.enclosingElement = | |
14469 element.getAncestor((element) => element is CompilationUnitElement); | |
14470 FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(aliasElement); | |
14471 ClassElement definingClass = | |
14472 element.getAncestor((element) => element is ClassElement); | |
14473 if (definingClass != null) { | |
14474 aliasElement.shareTypeParameters(definingClass.typeParameters); | |
14475 type.typeArguments = definingClass.type.typeArguments; | |
14476 } else { | |
14477 FunctionTypeAliasElement alias = | |
14478 element.getAncestor((element) => element is FunctionTypeAliasElement); | |
14479 while (alias != null && alias.isSynthetic) { | |
14480 alias = | |
14481 alias.getAncestor((element) => element is FunctionTypeAliasElement); | |
14482 } | |
14483 if (alias != null) { | |
14484 aliasElement.typeParameters = alias.typeParameters; | |
14485 type.typeArguments = alias.type.typeArguments; | |
14486 } else { | |
14487 type.typeArguments = DartType.EMPTY_LIST; | |
14488 } | |
14489 } | |
14490 element.type = type; | |
14491 } | |
14492 | |
14493 /** | |
14494 * @return `true` if the name of the given [TypeName] is an built-in identifie
r. | |
14495 */ | |
14496 static bool _isBuiltInIdentifier(TypeName node) { | |
14497 sc.Token token = node.name.beginToken; | |
14498 return token.type == sc.TokenType.KEYWORD; | |
14499 } | |
14500 | |
14501 /** | |
14502 * @return `true` if given [TypeName] is used as a type annotation. | |
14503 */ | |
14504 static bool _isTypeAnnotation(TypeName node) { | |
14505 AstNode parent = node.parent; | |
14506 if (parent is VariableDeclarationList) { | |
14507 return identical(parent.type, node); | |
14508 } | |
14509 if (parent is FieldFormalParameter) { | |
14510 return identical(parent.type, node); | |
14511 } | |
14512 if (parent is SimpleFormalParameter) { | |
14513 return identical(parent.type, node); | |
14514 } | |
14515 return false; | |
14516 } | |
14517 } | |
14518 | |
14519 /** | |
14520 * The interface `TypeSystem` defines the behavior of an object representing | |
14521 * the type system. This provides a common location to put methods that act on | |
14522 * types but may need access to more global data structures, and it paves the | |
14523 * way for a possible future where we may wish to make the type system | |
14524 * pluggable. | |
14525 */ | |
14526 abstract class TypeSystem { | |
14527 /** | |
14528 * Return the [TypeProvider] associated with this [TypeSystem]. | |
14529 */ | |
14530 TypeProvider get typeProvider; | |
14531 | |
14532 /** | |
14533 * Compute the least upper bound of two types. | |
14534 */ | |
14535 DartType getLeastUpperBound(DartType type1, DartType type2); | |
14536 } | |
14537 | |
14538 /** | |
14539 * Implementation of [TypeSystem] using the rules in the Dart specification. | |
14540 */ | |
14541 class TypeSystemImpl implements TypeSystem { | |
14542 @override | |
14543 final TypeProvider typeProvider; | |
14544 | |
14545 TypeSystemImpl(this.typeProvider); | |
14546 | |
14547 @override | |
14548 DartType getLeastUpperBound(DartType type1, DartType type2) { | |
14549 // The least upper bound relation is reflexive. | |
14550 if (identical(type1, type2)) { | |
14551 return type1; | |
14552 } | |
14553 // The least upper bound of dynamic and any type T is dynamic. | |
14554 if (type1.isDynamic) { | |
14555 return type1; | |
14556 } | |
14557 if (type2.isDynamic) { | |
14558 return type2; | |
14559 } | |
14560 // The least upper bound of void and any type T != dynamic is void. | |
14561 if (type1.isVoid) { | |
14562 return type1; | |
14563 } | |
14564 if (type2.isVoid) { | |
14565 return type2; | |
14566 } | |
14567 // The least upper bound of bottom and any type T is T. | |
14568 if (type1.isBottom) { | |
14569 return type2; | |
14570 } | |
14571 if (type2.isBottom) { | |
14572 return type1; | |
14573 } | |
14574 // Let U be a type variable with upper bound B. The least upper bound of U | |
14575 // and a type T is the least upper bound of B and T. | |
14576 while (type1 is TypeParameterType) { | |
14577 // TODO(paulberry): is this correct in the complex of F-bounded | |
14578 // polymorphism? | |
14579 DartType bound = (type1 as TypeParameterType).element.bound; | |
14580 if (bound == null) { | |
14581 bound = typeProvider.objectType; | |
14582 } | |
14583 type1 = bound; | |
14584 } | |
14585 while (type2 is TypeParameterType) { | |
14586 // TODO(paulberry): is this correct in the context of F-bounded | |
14587 // polymorphism? | |
14588 DartType bound = (type2 as TypeParameterType).element.bound; | |
14589 if (bound == null) { | |
14590 bound = typeProvider.objectType; | |
14591 } | |
14592 type2 = bound; | |
14593 } | |
14594 // The least upper bound of a function type and an interface type T is the | |
14595 // least upper bound of Function and T. | |
14596 if (type1 is FunctionType && type2 is InterfaceType) { | |
14597 type1 = typeProvider.functionType; | |
14598 } | |
14599 if (type2 is FunctionType && type1 is InterfaceType) { | |
14600 type2 = typeProvider.functionType; | |
14601 } | |
14602 | |
14603 // At this point type1 and type2 should both either be interface types or | |
14604 // function types. | |
14605 if (type1 is InterfaceType && type2 is InterfaceType) { | |
14606 InterfaceType result = | |
14607 InterfaceTypeImpl.computeLeastUpperBound(type1, type2); | |
14608 if (result == null) { | |
14609 return typeProvider.dynamicType; | |
14610 } | |
14611 return result; | |
14612 } else if (type1 is FunctionType && type2 is FunctionType) { | |
14613 FunctionType result = | |
14614 FunctionTypeImpl.computeLeastUpperBound(type1, type2); | |
14615 if (result == null) { | |
14616 return typeProvider.functionType; | |
14617 } | |
14618 return result; | |
14619 } else { | |
14620 // Should never happen. As a defensive measure, return the dynamic type. | |
14621 assert(false); | |
14622 return typeProvider.dynamicType; | |
14623 } | |
14624 } | |
14625 } | |
14626 | |
14627 /** | |
14628 * Instances of the class [UnusedLocalElementsVerifier] traverse an element | |
14629 * structure looking for cases of [HintCode.UNUSED_ELEMENT], | |
14630 * [HintCode.UNUSED_FIELD], [HintCode.UNUSED_LOCAL_VARIABLE], etc. | |
14631 */ | |
14632 class UnusedLocalElementsVerifier extends RecursiveElementVisitor { | |
14633 /** | |
14634 * The error listener to which errors will be reported. | |
14635 */ | |
14636 final AnalysisErrorListener _errorListener; | |
14637 | |
14638 /** | |
14639 * The elements know to be used. | |
14640 */ | |
14641 final UsedLocalElements _usedElements; | |
14642 | |
14643 /** | |
14644 * Create a new instance of the [UnusedLocalElementsVerifier]. | |
14645 */ | |
14646 UnusedLocalElementsVerifier(this._errorListener, this._usedElements); | |
14647 | |
14648 @override | |
14649 visitClassElement(ClassElement element) { | |
14650 if (!_isUsedElement(element)) { | |
14651 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
14652 element.kind.displayName, | |
14653 element.displayName | |
14654 ]); | |
14655 } | |
14656 super.visitClassElement(element); | |
14657 } | |
14658 | |
14659 @override | |
14660 visitFieldElement(FieldElement element) { | |
14661 if (!_isReadMember(element)) { | |
14662 _reportErrorForElement( | |
14663 HintCode.UNUSED_FIELD, element, [element.displayName]); | |
14664 } | |
14665 super.visitFieldElement(element); | |
14666 } | |
14667 | |
14668 @override | |
14669 visitFunctionElement(FunctionElement element) { | |
14670 if (!_isUsedElement(element)) { | |
14671 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
14672 element.kind.displayName, | |
14673 element.displayName | |
14674 ]); | |
14675 } | |
14676 super.visitFunctionElement(element); | |
14677 } | |
14678 | |
14679 @override | |
14680 visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { | |
14681 if (!_isUsedElement(element)) { | |
14682 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
14683 element.kind.displayName, | |
14684 element.displayName | |
14685 ]); | |
14686 } | |
14687 super.visitFunctionTypeAliasElement(element); | |
14688 } | |
14689 | |
14690 @override | |
14691 visitLocalVariableElement(LocalVariableElement element) { | |
14692 if (!_isUsedElement(element) && !_isNamedUnderscore(element)) { | |
14693 HintCode errorCode; | |
14694 if (_usedElements.isCatchException(element)) { | |
14695 errorCode = HintCode.UNUSED_CATCH_CLAUSE; | |
14696 } else if (_usedElements.isCatchStackTrace(element)) { | |
14697 errorCode = HintCode.UNUSED_CATCH_STACK; | |
14698 } else { | |
14699 errorCode = HintCode.UNUSED_LOCAL_VARIABLE; | |
14700 } | |
14701 _reportErrorForElement(errorCode, element, [element.displayName]); | |
14702 } | |
14703 } | |
14704 | |
14705 @override | |
14706 visitMethodElement(MethodElement element) { | |
14707 if (!_isUsedMember(element)) { | |
14708 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
14709 element.kind.displayName, | |
14710 element.displayName | |
14711 ]); | |
14712 } | |
14713 super.visitMethodElement(element); | |
14714 } | |
14715 | |
14716 @override | |
14717 visitPropertyAccessorElement(PropertyAccessorElement element) { | |
14718 if (!_isUsedMember(element)) { | |
14719 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
14720 element.kind.displayName, | |
14721 element.displayName | |
14722 ]); | |
14723 } | |
14724 super.visitPropertyAccessorElement(element); | |
14725 } | |
14726 | |
14727 bool _isNamedUnderscore(LocalVariableElement element) { | |
14728 String name = element.name; | |
14729 if (name != null) { | |
14730 for (int index = name.length - 1; index >= 0; --index) { | |
14731 if (name.codeUnitAt(index) != 0x5F) { | |
14732 // 0x5F => '_' | |
14733 return false; | |
14734 } | |
14735 } | |
14736 return true; | |
14737 } | |
14738 return false; | |
14739 } | |
14740 | |
14741 bool _isReadMember(Element element) { | |
14742 if (element.isPublic) { | |
14743 return true; | |
14744 } | |
14745 if (element.isSynthetic) { | |
14746 return true; | |
14747 } | |
14748 return _usedElements.readMembers.contains(element.displayName); | |
14749 } | |
14750 | |
14751 bool _isUsedElement(Element element) { | |
14752 if (element.isSynthetic) { | |
14753 return true; | |
14754 } | |
14755 if (element is LocalVariableElement || | |
14756 element is FunctionElement && !element.isStatic) { | |
14757 // local variable or function | |
14758 } else { | |
14759 if (element.isPublic) { | |
14760 return true; | |
14761 } | |
14762 } | |
14763 return _usedElements.elements.contains(element); | |
14764 } | |
14765 | |
14766 bool _isUsedMember(Element element) { | |
14767 if (element.isPublic) { | |
14768 return true; | |
14769 } | |
14770 if (element.isSynthetic) { | |
14771 return true; | |
14772 } | |
14773 if (_usedElements.members.contains(element.displayName)) { | |
14774 return true; | |
14775 } | |
14776 return _usedElements.elements.contains(element); | |
14777 } | |
14778 | |
14779 void _reportErrorForElement( | |
14780 ErrorCode errorCode, Element element, List<Object> arguments) { | |
14781 if (element != null) { | |
14782 _errorListener.onError(new AnalysisError(element.source, | |
14783 element.nameOffset, element.displayName.length, errorCode, | |
14784 arguments)); | |
14785 } | |
14786 } | |
14787 } | |
14788 | |
14789 /** | |
14790 * A container with information about used imports prefixes and used imported | |
14791 * elements. | |
14792 */ | |
14793 class UsedImportedElements { | |
14794 /** | |
14795 * The set of referenced [PrefixElement]s. | |
14796 */ | |
14797 final Set<PrefixElement> prefixes = new HashSet<PrefixElement>(); | |
14798 | |
14799 /** | |
14800 * The set of referenced top-level [Element]s. | |
14801 */ | |
14802 final Set<Element> elements = new HashSet<Element>(); | |
14803 } | |
14804 | |
14805 /** | |
14806 * A container with sets of used [Element]s. | |
14807 * All these elements are defined in a single compilation unit or a library. | |
14808 */ | |
14809 class UsedLocalElements { | |
14810 /** | |
14811 * Resolved, locally defined elements that are used or potentially can be | |
14812 * used. | |
14813 */ | |
14814 final HashSet<Element> elements = new HashSet<Element>(); | |
14815 | |
14816 /** | |
14817 * [LocalVariableElement]s that represent exceptions in [CatchClause]s. | |
14818 */ | |
14819 final HashSet<LocalVariableElement> catchExceptionElements = | |
14820 new HashSet<LocalVariableElement>(); | |
14821 | |
14822 /** | |
14823 * [LocalVariableElement]s that represent stack traces in [CatchClause]s. | |
14824 */ | |
14825 final HashSet<LocalVariableElement> catchStackTraceElements = | |
14826 new HashSet<LocalVariableElement>(); | |
14827 | |
14828 /** | |
14829 * Names of resolved or unresolved class members that are referenced in the | |
14830 * library. | |
14831 */ | |
14832 final HashSet<String> members = new HashSet<String>(); | |
14833 | |
14834 /** | |
14835 * Names of resolved or unresolved class members that are read in the | |
14836 * library. | |
14837 */ | |
14838 final HashSet<String> readMembers = new HashSet<String>(); | |
14839 | |
14840 UsedLocalElements(); | |
14841 | |
14842 factory UsedLocalElements.merge(List<UsedLocalElements> parts) { | |
14843 UsedLocalElements result = new UsedLocalElements(); | |
14844 for (UsedLocalElements part in parts) { | |
14845 result.elements.addAll(part.elements); | |
14846 result.catchExceptionElements.addAll(part.catchExceptionElements); | |
14847 result.catchStackTraceElements.addAll(part.catchStackTraceElements); | |
14848 result.members.addAll(part.members); | |
14849 result.readMembers.addAll(part.readMembers); | |
14850 } | |
14851 return result; | |
14852 } | |
14853 | |
14854 void addCatchException(LocalVariableElement element) { | |
14855 if (element != null) { | |
14856 catchExceptionElements.add(element); | |
14857 } | |
14858 } | |
14859 | |
14860 void addCatchStackTrace(LocalVariableElement element) { | |
14861 if (element != null) { | |
14862 catchStackTraceElements.add(element); | |
14863 } | |
14864 } | |
14865 | |
14866 void addElement(Element element) { | |
14867 if (element != null) { | |
14868 elements.add(element); | |
14869 } | |
14870 } | |
14871 | |
14872 bool isCatchException(LocalVariableElement element) { | |
14873 return catchExceptionElements.contains(element); | |
14874 } | |
14875 | |
14876 bool isCatchStackTrace(LocalVariableElement element) { | |
14877 return catchStackTraceElements.contains(element); | |
14878 } | |
14879 } | |
14880 | |
14881 /** | |
14882 * Instances of the class `VariableResolverVisitor` are used to resolve | |
14883 * [SimpleIdentifier]s to local variables and formal parameters. | |
14884 */ | |
14885 class VariableResolverVisitor extends ScopedVisitor { | |
14886 /** | |
14887 * The method or function that we are currently visiting, or `null` if we are
not inside a | |
14888 * method or function. | |
14889 */ | |
14890 ExecutableElement _enclosingFunction; | |
14891 | |
14892 /** | |
14893 * Initialize a newly created visitor to resolve the nodes in an AST node. | |
14894 * | |
14895 * [definingLibrary] is the element for the library containing the node being | |
14896 * visited. | |
14897 * [source] is the source representing the compilation unit containing the | |
14898 * node being visited | |
14899 * [typeProvider] is the object used to access the types from the core | |
14900 * library. | |
14901 * [errorListener] is the error listener that will be informed of any errors | |
14902 * that are found during resolution. | |
14903 * [nameScope] is the scope used to resolve identifiers in the node that will | |
14904 * first be visited. If `null` or unspecified, a new [LibraryScope] will be | |
14905 * created based on [definingLibrary] and [typeProvider]. | |
14906 */ | |
14907 VariableResolverVisitor(LibraryElement definingLibrary, Source source, | |
14908 TypeProvider typeProvider, AnalysisErrorListener errorListener, | |
14909 {Scope nameScope}) | |
14910 : super(definingLibrary, source, typeProvider, errorListener, | |
14911 nameScope: nameScope); | |
14912 | |
14913 /** | |
14914 * Initialize a newly created visitor to resolve the nodes in a compilation un
it. | |
14915 * | |
14916 * @param library the library containing the compilation unit being resolved | |
14917 * @param source the source representing the compilation unit being visited | |
14918 * @param typeProvider the object used to access the types from the core libra
ry | |
14919 * | |
14920 * Deprecated. Please use unnamed constructor instead. | |
14921 */ | |
14922 @deprecated | |
14923 VariableResolverVisitor.con1( | |
14924 Library library, Source source, TypeProvider typeProvider) | |
14925 : this( | |
14926 library.libraryElement, source, typeProvider, library.errorListener, | |
14927 nameScope: library.libraryScope); | |
14928 | |
14929 @override | |
14930 Object visitExportDirective(ExportDirective node) => null; | |
14931 | |
14932 @override | |
14933 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
14934 ExecutableElement outerFunction = _enclosingFunction; | |
14935 try { | |
14936 _enclosingFunction = node.element; | |
14937 return super.visitFunctionDeclaration(node); | |
14938 } finally { | |
14939 _enclosingFunction = outerFunction; | |
14940 } | |
14941 } | |
14942 | |
14943 @override | |
14944 Object visitFunctionExpression(FunctionExpression node) { | |
14945 if (node.parent is! FunctionDeclaration) { | |
14946 ExecutableElement outerFunction = _enclosingFunction; | |
14947 try { | |
14948 _enclosingFunction = node.element; | |
14949 return super.visitFunctionExpression(node); | |
14950 } finally { | |
14951 _enclosingFunction = outerFunction; | |
14952 } | |
14953 } else { | |
14954 return super.visitFunctionExpression(node); | |
14955 } | |
14956 } | |
14957 | |
14958 @override | |
14959 Object visitImportDirective(ImportDirective node) => null; | |
14960 | |
14961 @override | |
14962 Object visitMethodDeclaration(MethodDeclaration node) { | |
14963 ExecutableElement outerFunction = _enclosingFunction; | |
14964 try { | |
14965 _enclosingFunction = node.element; | |
14966 return super.visitMethodDeclaration(node); | |
14967 } finally { | |
14968 _enclosingFunction = outerFunction; | |
14969 } | |
14970 } | |
14971 | |
14972 @override | |
14973 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
14974 // Ignore if already resolved - declaration or type. | |
14975 if (node.staticElement != null) { | |
14976 return null; | |
14977 } | |
14978 // Ignore if qualified. | |
14979 AstNode parent = node.parent; | |
14980 if (parent is PrefixedIdentifier && identical(parent.identifier, node)) { | |
14981 return null; | |
14982 } | |
14983 if (parent is PropertyAccess && identical(parent.propertyName, node)) { | |
14984 return null; | |
14985 } | |
14986 if (parent is MethodInvocation && | |
14987 identical(parent.methodName, node) && | |
14988 parent.realTarget != null) { | |
14989 return null; | |
14990 } | |
14991 if (parent is ConstructorName) { | |
14992 return null; | |
14993 } | |
14994 if (parent is Label) { | |
14995 return null; | |
14996 } | |
14997 // Prepare VariableElement. | |
14998 Element element = nameScope.lookup(node, definingLibrary); | |
14999 if (element is! VariableElement) { | |
15000 return null; | |
15001 } | |
15002 // Must be local or parameter. | |
15003 ElementKind kind = element.kind; | |
15004 if (kind == ElementKind.LOCAL_VARIABLE) { | |
15005 node.staticElement = element; | |
15006 LocalVariableElementImpl variableImpl = | |
15007 element as LocalVariableElementImpl; | |
15008 if (node.inSetterContext()) { | |
15009 variableImpl.markPotentiallyMutatedInScope(); | |
15010 if (element.enclosingElement != _enclosingFunction) { | |
15011 variableImpl.markPotentiallyMutatedInClosure(); | |
15012 } | |
15013 } | |
15014 } else if (kind == ElementKind.PARAMETER) { | |
15015 node.staticElement = element; | |
15016 if (node.inSetterContext()) { | |
15017 ParameterElementImpl parameterImpl = element as ParameterElementImpl; | |
15018 parameterImpl.markPotentiallyMutatedInScope(); | |
15019 // If we are in some closure, check if it is not the same as where | |
15020 // variable is declared. | |
15021 if (_enclosingFunction != null && | |
15022 (element.enclosingElement != _enclosingFunction)) { | |
15023 parameterImpl.markPotentiallyMutatedInClosure(); | |
15024 } | |
15025 } | |
15026 } | |
15027 return null; | |
15028 } | |
15029 } | |
15030 | |
15031 class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor { | |
15032 final ConstantVerifier verifier; | |
15033 | |
15034 List<ParameterElement> parameterElements; | |
15035 | |
15036 _ConstantVerifier_validateInitializerExpression(TypeProvider typeProvider, | |
15037 ErrorReporter errorReporter, this.verifier, this.parameterElements, | |
15038 DeclaredVariables declaredVariables) | |
15039 : super(new ConstantEvaluationEngine(typeProvider, declaredVariables), | |
15040 errorReporter); | |
15041 | |
15042 @override | |
15043 DartObjectImpl visitSimpleIdentifier(SimpleIdentifier node) { | |
15044 Element element = node.staticElement; | |
15045 for (ParameterElement parameterElement in parameterElements) { | |
15046 if (identical(parameterElement, element) && parameterElement != null) { | |
15047 DartType type = parameterElement.type; | |
15048 if (type != null) { | |
15049 if (type.isDynamic) { | |
15050 return new DartObjectImpl( | |
15051 verifier._typeProvider.objectType, DynamicState.DYNAMIC_STATE); | |
15052 } else if (type.isSubtypeOf(verifier._boolType)) { | |
15053 return new DartObjectImpl( | |
15054 verifier._typeProvider.boolType, BoolState.UNKNOWN_VALUE); | |
15055 } else if (type.isSubtypeOf(verifier._typeProvider.doubleType)) { | |
15056 return new DartObjectImpl( | |
15057 verifier._typeProvider.doubleType, DoubleState.UNKNOWN_VALUE); | |
15058 } else if (type.isSubtypeOf(verifier._intType)) { | |
15059 return new DartObjectImpl( | |
15060 verifier._typeProvider.intType, IntState.UNKNOWN_VALUE); | |
15061 } else if (type.isSubtypeOf(verifier._numType)) { | |
15062 return new DartObjectImpl( | |
15063 verifier._typeProvider.numType, NumState.UNKNOWN_VALUE); | |
15064 } else if (type.isSubtypeOf(verifier._stringType)) { | |
15065 return new DartObjectImpl( | |
15066 verifier._typeProvider.stringType, StringState.UNKNOWN_VALUE); | |
15067 } | |
15068 // | |
15069 // We don't test for other types of objects (such as List, Map, | |
15070 // Function or Type) because there are no operations allowed on such | |
15071 // types other than '==' and '!=', which means that we don't need to | |
15072 // know the type when there is no specific data about the state of | |
15073 // such objects. | |
15074 // | |
15075 } | |
15076 return new DartObjectImpl( | |
15077 type is InterfaceType ? type : verifier._typeProvider.objectType, | |
15078 GenericState.UNKNOWN_VALUE); | |
15079 } | |
15080 } | |
15081 return super.visitSimpleIdentifier(node); | |
15082 } | |
15083 } | |
15084 | |
15085 class _ElementBuilder_visitClassDeclaration extends UnifyingAstVisitor<Object> { | |
15086 final ElementBuilder builder; | |
15087 | |
15088 List<ClassMember> nonFields; | |
15089 | |
15090 _ElementBuilder_visitClassDeclaration(this.builder, this.nonFields) : super(); | |
15091 | |
15092 @override | |
15093 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
15094 nonFields.add(node); | |
15095 return null; | |
15096 } | |
15097 | |
15098 @override | |
15099 Object visitMethodDeclaration(MethodDeclaration node) { | |
15100 nonFields.add(node); | |
15101 return null; | |
15102 } | |
15103 | |
15104 @override | |
15105 Object visitNode(AstNode node) => node.accept(builder); | |
15106 } | |
15107 | |
15108 class _ResolverVisitor_isVariableAccessedInClosure | |
15109 extends RecursiveAstVisitor<Object> { | |
15110 final Element variable; | |
15111 | |
15112 bool result = false; | |
15113 | |
15114 bool _inClosure = false; | |
15115 | |
15116 _ResolverVisitor_isVariableAccessedInClosure(this.variable); | |
15117 | |
15118 @override | |
15119 Object visitFunctionExpression(FunctionExpression node) { | |
15120 bool inClosure = this._inClosure; | |
15121 try { | |
15122 this._inClosure = true; | |
15123 return super.visitFunctionExpression(node); | |
15124 } finally { | |
15125 this._inClosure = inClosure; | |
15126 } | |
15127 } | |
15128 | |
15129 @override | |
15130 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
15131 if (result) { | |
15132 return null; | |
15133 } | |
15134 if (_inClosure && identical(node.staticElement, variable)) { | |
15135 result = true; | |
15136 } | |
15137 return null; | |
15138 } | |
15139 } | |
15140 | |
15141 class _ResolverVisitor_isVariablePotentiallyMutatedIn | |
15142 extends RecursiveAstVisitor<Object> { | |
15143 final Element variable; | |
15144 | |
15145 bool result = false; | |
15146 | |
15147 _ResolverVisitor_isVariablePotentiallyMutatedIn(this.variable); | |
15148 | |
15149 @override | |
15150 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
15151 if (result) { | |
15152 return null; | |
15153 } | |
15154 if (identical(node.staticElement, variable)) { | |
15155 if (node.inSetterContext()) { | |
15156 result = true; | |
15157 } | |
15158 } | |
15159 return null; | |
15160 } | |
15161 } | |
15162 | |
15163 class _TypeResolverVisitor_visitClassMembersInScope | |
15164 extends UnifyingAstVisitor<Object> { | |
15165 final TypeResolverVisitor TypeResolverVisitor_this; | |
15166 | |
15167 List<ClassMember> nonFields; | |
15168 | |
15169 _TypeResolverVisitor_visitClassMembersInScope( | |
15170 this.TypeResolverVisitor_this, this.nonFields) | |
15171 : super(); | |
15172 | |
15173 @override | |
15174 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
15175 nonFields.add(node); | |
15176 return null; | |
15177 } | |
15178 | |
15179 @override | |
15180 Object visitExtendsClause(ExtendsClause node) => null; | |
15181 | |
15182 @override | |
15183 Object visitImplementsClause(ImplementsClause node) => null; | |
15184 | |
15185 @override | |
15186 Object visitMethodDeclaration(MethodDeclaration node) { | |
15187 nonFields.add(node); | |
15188 return null; | |
15189 } | |
15190 | |
15191 @override | |
15192 Object visitNode(AstNode node) => node.accept(TypeResolverVisitor_this); | |
15193 | |
15194 @override | |
15195 Object visitWithClause(WithClause node) => null; | |
15196 } | |
OLD | NEW |