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/src/generated/ast.dart'; | 9 import 'package:analyzer/src/generated/ast.dart'; |
10 import 'package:analyzer/src/generated/element.dart'; | 10 import 'package:analyzer/src/generated/element.dart'; |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 } | 224 } |
225 // | 225 // |
226 // Then return the type of the parameter. | 226 // Then return the type of the parameter. |
227 // | 227 // |
228 return matchingParameter == null | 228 return matchingParameter == null |
229 ? typeProvider.dynamicType | 229 ? typeProvider.dynamicType |
230 : matchingParameter.type; | 230 : matchingParameter.type; |
231 } | 231 } |
232 | 232 |
233 /** | 233 /** |
234 * If the given [accessorElement] represents a non-synthetic instance getter | |
235 * for which no return type was provided, infer the return type of the getter. | |
236 */ | |
237 void _inferAccessor(PropertyAccessorElement accessorElement) { | |
238 if (!accessorElement.isSynthetic && | |
239 accessorElement.isGetter && | |
240 !accessorElement.isStatic && | |
241 accessorElement.hasImplicitReturnType) { | |
242 List<ExecutableElement> overriddenGetters = inheritanceManager | |
243 .lookupOverrides( | |
244 accessorElement.enclosingElement, accessorElement.name); | |
245 if (overriddenGetters.isNotEmpty && _onlyGetters(overriddenGetters)) { | |
246 DartType newType = _computeReturnType(overriddenGetters); | |
247 List<ExecutableElement> overriddenSetters = inheritanceManager | |
248 .lookupOverrides( | |
249 accessorElement.enclosingElement, accessorElement.name + '='); | |
250 PropertyAccessorElement setter = (accessorElement.enclosingElement | |
251 as ClassElement).getSetter(accessorElement.name); | |
252 if (setter != null) { | |
253 overriddenSetters.add(setter); | |
254 } | |
255 if (!_isCompatible(newType, overriddenSetters)) { | |
256 newType = typeProvider.dynamicType; | |
257 } | |
258 setReturnType(accessorElement, newType); | |
259 (accessorElement.variable as FieldElementImpl).type = newType; | |
260 } | |
261 } | |
262 } | |
263 | |
264 /** | |
265 * Infer type information for all of the instance members in the given | 234 * Infer type information for all of the instance members in the given |
266 * [classElement]. | 235 * [classElement]. |
267 */ | 236 */ |
268 void _inferClass(ClassElement classElement) { | 237 void _inferClass(ClassElement classElement) { |
269 if (classElement is ClassElementImpl) { | 238 if (classElement is ClassElementImpl) { |
270 if (classElement.hasBeenInferred) { | 239 if (classElement.hasBeenInferred) { |
271 return; | 240 return; |
272 } | 241 } |
273 if (!elementsBeingInferred.add(classElement)) { | 242 if (!elementsBeingInferred.add(classElement)) { |
274 // We have found a circularity in the class hierarchy. For now we just | 243 // We have found a circularity in the class hierarchy. For now we just |
275 // stop trying to infer any type information for any classes that | 244 // stop trying to infer any type information for any classes that |
276 // inherit from any class in the cycle. We could potentially limit the | 245 // inherit from any class in the cycle. We could potentially limit the |
277 // algorithm to only not inferring types in the classes in the cycle, | 246 // algorithm to only not inferring types in the classes in the cycle, |
278 // but it isn't clear that the results would be significantly better. | 247 // but it isn't clear that the results would be significantly better. |
279 throw new _CycleException(); | 248 throw new _CycleException(); |
280 } | 249 } |
281 try { | 250 try { |
282 // | 251 // |
283 // Ensure that all of instance members in the supertypes have had types | 252 // Ensure that all of instance members in the supertypes have had types |
284 // inferred for them. | 253 // inferred for them. |
285 // | 254 // |
286 _inferType(classElement.supertype); | 255 _inferType(classElement.supertype); |
287 classElement.mixins.forEach(_inferType); | 256 classElement.mixins.forEach(_inferType); |
288 classElement.interfaces.forEach(_inferType); | 257 classElement.interfaces.forEach(_inferType); |
289 // | 258 // |
290 // Then infer the types for the members. | 259 // Then infer the types for the members. |
291 // | 260 // |
292 classElement.fields.forEach(_inferField); | 261 classElement.fields.forEach(_inferField); |
293 classElement.accessors.forEach(_inferAccessor); | 262 classElement.accessors.forEach(_inferExecutable); |
294 classElement.methods.forEach(_inferMethod); | 263 classElement.methods.forEach(_inferExecutable); |
295 classElement.hasBeenInferred = true; | 264 classElement.hasBeenInferred = true; |
296 } finally { | 265 } finally { |
297 elementsBeingInferred.remove(classElement); | 266 elementsBeingInferred.remove(classElement); |
298 } | 267 } |
299 } | 268 } |
300 } | 269 } |
301 | 270 |
302 /** | 271 /** |
303 * If the given [fieldElement] represents a non-synthetic instance field for | 272 * If the given [fieldElement] represents a non-synthetic instance field for |
304 * which no type was provided, infer the type of the field. | 273 * which no type was provided, infer the type of the field. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 } | 306 } |
338 (fieldElement as FieldElementImpl).type = newType; | 307 (fieldElement as FieldElementImpl).type = newType; |
339 setReturnType(fieldElement.getter, newType); | 308 setReturnType(fieldElement.getter, newType); |
340 if (!fieldElement.isFinal && !fieldElement.isConst) { | 309 if (!fieldElement.isFinal && !fieldElement.isConst) { |
341 setParameterType(fieldElement.setter, newType); | 310 setParameterType(fieldElement.setter, newType); |
342 } | 311 } |
343 } | 312 } |
344 } | 313 } |
345 | 314 |
346 /** | 315 /** |
347 * If the given [methodElement] represents a non-synthetic instance method | 316 * If the given [element] represents a non-synthetic instance method, |
348 * for which no return type was provided, infer the return type of the method. | 317 * getter or setter, infer the return type and any parameter type(s) where |
| 318 * they were not provided. |
349 */ | 319 */ |
350 void _inferMethod(MethodElement methodElement) { | 320 void _inferExecutable(ExecutableElement element) { |
351 if (methodElement.isSynthetic || methodElement.isStatic) { | 321 if (element.isSynthetic || element.isStatic) { |
352 return; | 322 return; |
353 } | 323 } |
354 List<ExecutableElement> overriddenMethods = null; | 324 List<ExecutableElement> overriddenMethods = null; |
355 // | 325 // |
356 // Infer the return type. | 326 // Infer the return type. |
357 // | 327 // |
358 if (methodElement.hasImplicitReturnType) { | 328 if (element.hasImplicitReturnType) { |
359 overriddenMethods = inheritanceManager.lookupOverrides( | 329 overriddenMethods = inheritanceManager.lookupOverrides( |
360 methodElement.enclosingElement, methodElement.name); | 330 element.enclosingElement, element.name); |
361 if (overriddenMethods.isEmpty || !_onlyMethods(overriddenMethods)) { | 331 if (overriddenMethods.isEmpty || |
| 332 !_allSameElementKind(element, overriddenMethods)) { |
362 return; | 333 return; |
363 } | 334 } |
364 MethodElementImpl element = methodElement as MethodElementImpl; | |
365 setReturnType(element, _computeReturnType(overriddenMethods)); | 335 setReturnType(element, _computeReturnType(overriddenMethods)); |
| 336 if (element is PropertyAccessorElement) { |
| 337 _updateSyntheticVariableType(element); |
| 338 } |
366 } | 339 } |
367 // | 340 // |
368 // Infer the parameter types. | 341 // Infer the parameter types. |
369 // | 342 // |
370 List<ParameterElement> parameters = methodElement.parameters; | 343 List<ParameterElement> parameters = element.parameters; |
371 int length = parameters.length; | 344 int length = parameters.length; |
372 for (int i = 0; i < length; ++i) { | 345 for (int i = 0; i < length; ++i) { |
373 ParameterElement parameter = parameters[i]; | 346 ParameterElement parameter = parameters[i]; |
374 if (parameter is ParameterElementImpl && parameter.hasImplicitType) { | 347 if (parameter is ParameterElementImpl && parameter.hasImplicitType) { |
375 if (overriddenMethods == null) { | 348 if (overriddenMethods == null) { |
376 overriddenMethods = inheritanceManager.lookupOverrides( | 349 overriddenMethods = inheritanceManager.lookupOverrides( |
377 methodElement.enclosingElement, methodElement.name); | 350 element.enclosingElement, element.name); |
378 } | 351 } |
379 if (overriddenMethods.isEmpty || !_onlyMethods(overriddenMethods)) { | 352 if (overriddenMethods.isEmpty || |
| 353 !_allSameElementKind(element, overriddenMethods)) { |
380 return; | 354 return; |
381 } | 355 } |
382 parameter.type = _computeParameterType(parameter, i, overriddenMethods); | 356 parameter.type = _computeParameterType(parameter, i, overriddenMethods); |
| 357 if (element is PropertyAccessorElement) { |
| 358 _updateSyntheticVariableType(element); |
| 359 } |
383 } | 360 } |
384 } | 361 } |
385 } | 362 } |
386 | 363 |
387 /** | 364 /** |
| 365 * If the given [element] is a non-synthetic getter or setter, update its |
| 366 * synthetic variable's type to match the getter's return type, or if no |
| 367 * corresponding getter exists, use the setter's parameter type. |
| 368 * |
| 369 * In general, the type of the synthetic variable should not be used, because |
| 370 * getters and setters are independent methods. But this logic matches what |
| 371 * `TypeResolverVisitor.visitMethodDeclaration` would fill in there. |
| 372 */ |
| 373 void _updateSyntheticVariableType(PropertyAccessorElement element) { |
| 374 assert(!element.isSynthetic); |
| 375 PropertyAccessorElement getter = element; |
| 376 if (element.isSetter) { |
| 377 // See if we can find any getter. |
| 378 getter = element.correspondingGetter; |
| 379 } |
| 380 DartType newType; |
| 381 if (getter != null) { |
| 382 newType = getter.returnType; |
| 383 } else if (element.isSetter && element.parameters.isNotEmpty) { |
| 384 newType = element.parameters[0].type; |
| 385 } |
| 386 if (newType != null) { |
| 387 (element.variable as VariableElementImpl).type = newType; |
| 388 } |
| 389 } |
| 390 |
| 391 /** |
388 * Infer type information for all of the instance members in the given | 392 * Infer type information for all of the instance members in the given |
389 * interface [type]. | 393 * interface [type]. |
390 */ | 394 */ |
391 void _inferType(InterfaceType type) { | 395 void _inferType(InterfaceType type) { |
392 if (type != null) { | 396 if (type != null) { |
393 ClassElement element = type.element; | 397 ClassElement element = type.element; |
394 if (element != null) { | 398 if (element != null) { |
395 _inferClass(element); | 399 _inferClass(element); |
396 } | 400 } |
397 } | 401 } |
(...skipping 21 matching lines...) Expand all Loading... |
419 if (!(element is PropertyAccessorElement && element.isGetter)) { | 423 if (!(element is PropertyAccessorElement && element.isGetter)) { |
420 return false; | 424 return false; |
421 } | 425 } |
422 } | 426 } |
423 return true; | 427 return true; |
424 } | 428 } |
425 | 429 |
426 /** | 430 /** |
427 * Return `true` if the list of [elements] contains only methods. | 431 * Return `true` if the list of [elements] contains only methods. |
428 */ | 432 */ |
429 bool _onlyMethods(List<ExecutableElement> elements) { | 433 bool _allSameElementKind( |
430 for (ExecutableElement element in elements) { | 434 ExecutableElement element, List<ExecutableElement> elements) { |
431 if (element is! MethodElement) { | 435 return elements.every((e) => e.kind == element.kind); |
432 return false; | |
433 } | |
434 } | |
435 return true; | |
436 } | 436 } |
437 } | 437 } |
438 | 438 |
439 /** | 439 /** |
440 * A visitor that will gather all of the variables referenced within a given | 440 * A visitor that will gather all of the variables referenced within a given |
441 * AST structure. The collection can be restricted to contain only those | 441 * AST structure. The collection can be restricted to contain only those |
442 * variables that pass a specified filter. | 442 * variables that pass a specified filter. |
443 */ | 443 */ |
444 class VariableGatherer extends RecursiveAstVisitor { | 444 class VariableGatherer extends RecursiveAstVisitor { |
445 /** | 445 /** |
(...skipping 24 matching lines...) Expand all Loading... |
470 results.add(element); | 470 results.add(element); |
471 } | 471 } |
472 } | 472 } |
473 } | 473 } |
474 } | 474 } |
475 | 475 |
476 /** | 476 /** |
477 * A class of exception that is not used anywhere else. | 477 * A class of exception that is not used anywhere else. |
478 */ | 478 */ |
479 class _CycleException implements Exception {} | 479 class _CycleException implements Exception {} |
OLD | NEW |