OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library analyzer.src.task.strong_mode; | 5 library analyzer.src.task.strong_mode; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 | 8 |
| 9 import 'package:analyzer/dart/element/element.dart'; |
| 10 import 'package:analyzer/dart/element/type.dart'; |
| 11 import 'package:analyzer/src/dart/element/element.dart'; |
| 12 import 'package:analyzer/src/dart/element/type.dart'; |
9 import 'package:analyzer/src/generated/ast.dart'; | 13 import 'package:analyzer/src/generated/ast.dart'; |
10 import 'package:analyzer/src/generated/element.dart'; | |
11 import 'package:analyzer/src/generated/resolver.dart' | 14 import 'package:analyzer/src/generated/resolver.dart' |
12 show TypeProvider, InheritanceManager; | 15 show TypeProvider, InheritanceManager; |
13 import 'package:analyzer/src/generated/type_system.dart'; | 16 import 'package:analyzer/src/generated/type_system.dart'; |
14 import 'package:analyzer/src/generated/utilities_dart.dart'; | 17 import 'package:analyzer/src/generated/utilities_dart.dart'; |
15 | 18 |
16 /** | 19 /** |
17 * Set the type of the sole parameter of the given [element] to the given [type]
. | 20 * Set the type of the sole parameter of the given [element] to the given [type]
. |
18 */ | 21 */ |
19 void setParameterType(PropertyAccessorElement element, DartType type) { | 22 void setParameterType(PropertyAccessorElement element, DartType type) { |
20 if (element is PropertyAccessorElementImpl) { | 23 if (element is PropertyAccessorElementImpl) { |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 try { | 133 try { |
131 _inferClass(classElement); | 134 _inferClass(classElement); |
132 } on _CycleException { | 135 } on _CycleException { |
133 // This is a short circuit return to prevent types that inherit from | 136 // This is a short circuit return to prevent types that inherit from |
134 // types containing a circular reference from being inferred. | 137 // types containing a circular reference from being inferred. |
135 } | 138 } |
136 }); | 139 }); |
137 } | 140 } |
138 | 141 |
139 /** | 142 /** |
| 143 * Return `true` if the list of [elements] contains only methods. |
| 144 */ |
| 145 bool _allSameElementKind( |
| 146 ExecutableElement element, List<ExecutableElement> elements) { |
| 147 return elements.every((e) => e.kind == element.kind); |
| 148 } |
| 149 |
| 150 /** |
140 * Compute the best type for the [parameter] at the given [index] that must be | 151 * Compute the best type for the [parameter] at the given [index] that must be |
141 * compatible with the types of the corresponding parameters of the given | 152 * compatible with the types of the corresponding parameters of the given |
142 * [overriddenMethods]. | 153 * [overriddenMethods]. |
143 * | 154 * |
144 * At the moment, this method will only return a type other than 'dynamic' if | 155 * At the moment, this method will only return a type other than 'dynamic' if |
145 * the types of all of the parameters are the same. In the future we might | 156 * the types of all of the parameters are the same. In the future we might |
146 * want to be smarter about it, such as by returning the least upper bound of | 157 * want to be smarter about it, such as by returning the least upper bound of |
147 * the parameter types. | 158 * the parameter types. |
148 */ | 159 */ |
149 DartType _computeParameterType(ParameterElement parameter, int index, | 160 DartType _computeParameterType(ParameterElement parameter, int index, |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
206 List<ParameterElement> methodParameters = method.parameters; | 217 List<ParameterElement> methodParameters = method.parameters; |
207 ParameterElement matchingParameter = null; | 218 ParameterElement matchingParameter = null; |
208 if (parameter.parameterKind == ParameterKind.NAMED) { | 219 if (parameter.parameterKind == ParameterKind.NAMED) { |
209 // | 220 // |
210 // If we're looking for a named parameter, only a named parameter with | 221 // If we're looking for a named parameter, only a named parameter with |
211 // the same name will be matched. | 222 // the same name will be matched. |
212 // | 223 // |
213 matchingParameter = methodParameters.lastWhere( | 224 matchingParameter = methodParameters.lastWhere( |
214 (ParameterElement methodParameter) => | 225 (ParameterElement methodParameter) => |
215 methodParameter.parameterKind == ParameterKind.NAMED && | 226 methodParameter.parameterKind == ParameterKind.NAMED && |
216 methodParameter.name == parameter.name, | 227 methodParameter.name == parameter.name, |
217 orElse: () => null); | 228 orElse: () => null); |
218 } else { | 229 } else { |
219 // | 230 // |
220 // If we're looking for a positional parameter we ignore the difference | 231 // If we're looking for a positional parameter we ignore the difference |
221 // between required and optional parameters. | 232 // between required and optional parameters. |
222 // | 233 // |
223 if (index < methodParameters.length) { | 234 if (index < methodParameters.length) { |
224 matchingParameter = methodParameters[index]; | 235 matchingParameter = methodParameters[index]; |
225 if (matchingParameter.parameterKind == ParameterKind.NAMED) { | 236 if (matchingParameter.parameterKind == ParameterKind.NAMED) { |
226 matchingParameter = null; | 237 matchingParameter = null; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
271 // field types are inferred. | 282 // field types are inferred. |
272 // | 283 // |
273 classElement.constructors.forEach(_inferConstructorFieldFormals); | 284 classElement.constructors.forEach(_inferConstructorFieldFormals); |
274 classElement.hasBeenInferred = true; | 285 classElement.hasBeenInferred = true; |
275 } finally { | 286 } finally { |
276 elementsBeingInferred.remove(classElement); | 287 elementsBeingInferred.remove(classElement); |
277 } | 288 } |
278 } | 289 } |
279 } | 290 } |
280 | 291 |
281 /** | 292 void _inferConstructorFieldFormals(ConstructorElement element) { |
282 * If the given [fieldElement] represents a non-synthetic instance field for | 293 for (ParameterElement p in element.parameters) { |
283 * which no type was provided, infer the type of the field. | 294 if (p is FieldFormalParameterElement) { |
284 */ | 295 _inferFieldFormalParameter(p); |
285 void _inferField(FieldElement fieldElement) { | |
286 if (!fieldElement.isSynthetic && | |
287 !fieldElement.isStatic && | |
288 fieldElement.hasImplicitType) { | |
289 // | |
290 // First look for overridden getters with the same name as the field. | |
291 // | |
292 List<ExecutableElement> overriddenGetters = inheritanceManager | |
293 .lookupOverrides(fieldElement.enclosingElement, fieldElement.name); | |
294 DartType newType = null; | |
295 if (overriddenGetters.isNotEmpty && _onlyGetters(overriddenGetters)) { | |
296 newType = _computeReturnType(overriddenGetters); | |
297 List<ExecutableElement> overriddenSetters = inheritanceManager | |
298 .lookupOverrides( | |
299 fieldElement.enclosingElement, fieldElement.name + '='); | |
300 if (!_isCompatible(newType, overriddenSetters)) { | |
301 newType = null; | |
302 } | |
303 } | |
304 // | |
305 // If there is no overridden getter or if the overridden getter's type is | |
306 // dynamic, then we can infer the type from the initialization expression | |
307 // without breaking subtype rules. We could potentially infer a consistent | |
308 // return type even if the overridden getter's type was not dynamic, but | |
309 // choose not to for simplicity. The field is required to be final to | |
310 // prevent choosing a type that is inconsistent with assignments we cannot | |
311 // analyze. | |
312 // | |
313 if (newType == null || newType.isDynamic) { | |
314 if (fieldElement.initializer != null && | |
315 (fieldElement.isFinal || overriddenGetters.isEmpty)) { | |
316 newType = fieldElement.initializer.returnType; | |
317 } | |
318 } | |
319 if (newType == null || newType.isBottom) { | |
320 newType = typeProvider.dynamicType; | |
321 } | |
322 (fieldElement as FieldElementImpl).type = newType; | |
323 setReturnType(fieldElement.getter, newType); | |
324 if (!fieldElement.isFinal && !fieldElement.isConst) { | |
325 setParameterType(fieldElement.setter, newType); | |
326 } | 296 } |
327 } | 297 } |
328 } | 298 } |
329 | 299 |
330 /** | 300 /** |
331 * If the given [element] represents a non-synthetic instance method, | 301 * If the given [element] represents a non-synthetic instance method, |
332 * getter or setter, infer the return type and any parameter type(s) where | 302 * getter or setter, infer the return type and any parameter type(s) where |
333 * they were not provided. | 303 * they were not provided. |
334 */ | 304 */ |
335 void _inferExecutable(ExecutableElement element) { | 305 void _inferExecutable(ExecutableElement element) { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 } | 340 } |
371 parameter.type = _computeParameterType(parameter, i, overriddenMethods); | 341 parameter.type = _computeParameterType(parameter, i, overriddenMethods); |
372 if (element is PropertyAccessorElement) { | 342 if (element is PropertyAccessorElement) { |
373 _updateSyntheticVariableType(element); | 343 _updateSyntheticVariableType(element); |
374 } | 344 } |
375 } | 345 } |
376 } | 346 } |
377 } | 347 } |
378 | 348 |
379 /** | 349 /** |
380 * If the given [element] is a non-synthetic getter or setter, update its | 350 * If the given [fieldElement] represents a non-synthetic instance field for |
381 * synthetic variable's type to match the getter's return type, or if no | 351 * which no type was provided, infer the type of the field. |
382 * corresponding getter exists, use the setter's parameter type. | |
383 * | |
384 * In general, the type of the synthetic variable should not be used, because | |
385 * getters and setters are independent methods. But this logic matches what | |
386 * `TypeResolverVisitor.visitMethodDeclaration` would fill in there. | |
387 */ | 352 */ |
388 void _updateSyntheticVariableType(PropertyAccessorElement element) { | 353 void _inferField(FieldElement fieldElement) { |
389 assert(!element.isSynthetic); | 354 if (!fieldElement.isSynthetic && |
390 PropertyAccessorElement getter = element; | 355 !fieldElement.isStatic && |
391 if (element.isSetter) { | 356 fieldElement.hasImplicitType) { |
392 // See if we can find any getter. | 357 // |
393 getter = element.correspondingGetter; | 358 // First look for overridden getters with the same name as the field. |
394 } | 359 // |
395 DartType newType; | 360 List<ExecutableElement> overriddenGetters = inheritanceManager |
396 if (getter != null) { | 361 .lookupOverrides(fieldElement.enclosingElement, fieldElement.name); |
397 newType = getter.returnType; | 362 DartType newType = null; |
398 } else if (element.isSetter && element.parameters.isNotEmpty) { | 363 if (overriddenGetters.isNotEmpty && _onlyGetters(overriddenGetters)) { |
399 newType = element.parameters[0].type; | 364 newType = _computeReturnType(overriddenGetters); |
400 } | 365 List<ExecutableElement> overriddenSetters = |
401 if (newType != null) { | 366 inheritanceManager.lookupOverrides( |
402 (element.variable as VariableElementImpl).type = newType; | 367 fieldElement.enclosingElement, fieldElement.name + '='); |
| 368 if (!_isCompatible(newType, overriddenSetters)) { |
| 369 newType = null; |
| 370 } |
| 371 } |
| 372 // |
| 373 // If there is no overridden getter or if the overridden getter's type is |
| 374 // dynamic, then we can infer the type from the initialization expression |
| 375 // without breaking subtype rules. We could potentially infer a consistent |
| 376 // return type even if the overridden getter's type was not dynamic, but |
| 377 // choose not to for simplicity. The field is required to be final to |
| 378 // prevent choosing a type that is inconsistent with assignments we cannot |
| 379 // analyze. |
| 380 // |
| 381 if (newType == null || newType.isDynamic) { |
| 382 if (fieldElement.initializer != null && |
| 383 (fieldElement.isFinal || overriddenGetters.isEmpty)) { |
| 384 newType = fieldElement.initializer.returnType; |
| 385 } |
| 386 } |
| 387 if (newType == null || newType.isBottom) { |
| 388 newType = typeProvider.dynamicType; |
| 389 } |
| 390 (fieldElement as FieldElementImpl).type = newType; |
| 391 setReturnType(fieldElement.getter, newType); |
| 392 if (!fieldElement.isFinal && !fieldElement.isConst) { |
| 393 setParameterType(fieldElement.setter, newType); |
| 394 } |
403 } | 395 } |
404 } | 396 } |
405 | 397 |
| 398 void _inferFieldFormalParameter(FieldFormalParameterElement element) { |
| 399 FieldElement field = element.field; |
| 400 if (field != null && element.hasImplicitType) { |
| 401 (element as FieldFormalParameterElementImpl).type = field.type; |
| 402 } |
| 403 } |
| 404 |
406 /** | 405 /** |
407 * Infer type information for all of the instance members in the given | 406 * Infer type information for all of the instance members in the given |
408 * interface [type]. | 407 * interface [type]. |
409 */ | 408 */ |
410 void _inferType(InterfaceType type) { | 409 void _inferType(InterfaceType type) { |
411 if (type != null) { | 410 if (type != null) { |
412 ClassElement element = type.element; | 411 ClassElement element = type.element; |
413 if (element != null) { | 412 if (element != null) { |
414 _inferClass(element); | 413 _inferClass(element); |
415 } | 414 } |
(...skipping 20 matching lines...) Expand all Loading... |
436 bool _onlyGetters(List<ExecutableElement> elements) { | 435 bool _onlyGetters(List<ExecutableElement> elements) { |
437 for (ExecutableElement element in elements) { | 436 for (ExecutableElement element in elements) { |
438 if (!(element is PropertyAccessorElement && element.isGetter)) { | 437 if (!(element is PropertyAccessorElement && element.isGetter)) { |
439 return false; | 438 return false; |
440 } | 439 } |
441 } | 440 } |
442 return true; | 441 return true; |
443 } | 442 } |
444 | 443 |
445 /** | 444 /** |
446 * Return `true` if the list of [elements] contains only methods. | 445 * If the given [element] is a non-synthetic getter or setter, update its |
| 446 * synthetic variable's type to match the getter's return type, or if no |
| 447 * corresponding getter exists, use the setter's parameter type. |
| 448 * |
| 449 * In general, the type of the synthetic variable should not be used, because |
| 450 * getters and setters are independent methods. But this logic matches what |
| 451 * `TypeResolverVisitor.visitMethodDeclaration` would fill in there. |
447 */ | 452 */ |
448 bool _allSameElementKind( | 453 void _updateSyntheticVariableType(PropertyAccessorElement element) { |
449 ExecutableElement element, List<ExecutableElement> elements) { | 454 assert(!element.isSynthetic); |
450 return elements.every((e) => e.kind == element.kind); | 455 PropertyAccessorElement getter = element; |
451 } | 456 if (element.isSetter) { |
452 | 457 // See if we can find any getter. |
453 void _inferConstructorFieldFormals(ConstructorElement element) { | 458 getter = element.correspondingGetter; |
454 for (ParameterElement p in element.parameters) { | |
455 if (p is FieldFormalParameterElement) { | |
456 _inferFieldFormalParameter(p); | |
457 } | |
458 } | 459 } |
459 } | 460 DartType newType; |
460 | 461 if (getter != null) { |
461 void _inferFieldFormalParameter(FieldFormalParameterElement element) { | 462 newType = getter.returnType; |
462 FieldElement field = element.field; | 463 } else if (element.isSetter && element.parameters.isNotEmpty) { |
463 if (field != null && element.hasImplicitType) { | 464 newType = element.parameters[0].type; |
464 (element as FieldFormalParameterElementImpl).type = field.type; | 465 } |
| 466 if (newType != null) { |
| 467 (element.variable as VariableElementImpl).type = newType; |
465 } | 468 } |
466 } | 469 } |
467 } | 470 } |
468 | 471 |
469 /** | 472 /** |
470 * A visitor that will gather all of the variables referenced within a given | 473 * A visitor that will gather all of the variables referenced within a given |
471 * AST structure. The collection can be restricted to contain only those | 474 * AST structure. The collection can be restricted to contain only those |
472 * variables that pass a specified filter. | 475 * variables that pass a specified filter. |
473 */ | 476 */ |
474 class VariableGatherer extends RecursiveAstVisitor { | 477 class VariableGatherer extends RecursiveAstVisitor { |
(...skipping 25 matching lines...) Expand all Loading... |
500 results.add(element); | 503 results.add(element); |
501 } | 504 } |
502 } | 505 } |
503 } | 506 } |
504 } | 507 } |
505 | 508 |
506 /** | 509 /** |
507 * A class of exception that is not used anywhere else. | 510 * A class of exception that is not used anywhere else. |
508 */ | 511 */ |
509 class _CycleException implements Exception {} | 512 class _CycleException implements Exception {} |
OLD | NEW |