Chromium Code Reviews| 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'; | |
| 8 | |
| 7 import 'package:analyzer/src/generated/ast.dart'; | 9 import 'package:analyzer/src/generated/ast.dart'; |
| 8 import 'package:analyzer/src/generated/element.dart'; | 10 import 'package:analyzer/src/generated/element.dart'; |
| 11 import 'package:analyzer/src/generated/resolver.dart'; | |
| 9 | 12 |
| 10 /** | 13 /** |
| 11 * An object used to find static variables whose types should be inferred and | 14 * An object used to find static variables whose types should be inferred and |
| 12 * classes whose members should have types inferred. Clients are expected to | 15 * classes whose members should have types inferred. Clients are expected to |
| 13 * visit a [CompilationUnit]. | 16 * visit a [CompilationUnit]. |
| 14 */ | 17 */ |
| 15 class InferrenceFinder extends SimpleAstVisitor { | 18 class InferrenceFinder extends SimpleAstVisitor { |
| 16 /** | 19 /** |
| 17 * The static variables that should have types inferred for them. | 20 * The static variables that should have types inferred for them. |
| 18 */ | 21 */ |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 * refer to fields whose type was inferred. | 76 * refer to fields whose type was inferred. |
| 74 */ | 77 */ |
| 75 void _addVariables(NodeList<VariableDeclaration> variables) { | 78 void _addVariables(NodeList<VariableDeclaration> variables) { |
| 76 for (VariableDeclaration variable in variables) { | 79 for (VariableDeclaration variable in variables) { |
| 77 if (variable.initializer != null) { | 80 if (variable.initializer != null) { |
| 78 staticVariables.add(variable.element); | 81 staticVariables.add(variable.element); |
| 79 } | 82 } |
| 80 } | 83 } |
| 81 } | 84 } |
| 82 } | 85 } |
| 86 | |
| 87 /** | |
| 88 * An object used to infer the type of instance fields and the return types of | |
| 89 * instance methods within a single compilation unit. | |
| 90 */ | |
| 91 class InstanceMemberInferrer { | |
|
Paul Berry
2015/08/24 19:56:25
I have a broad concern about this implementation s
Brian Wilkerson
2015/08/24 21:20:57
I added you comments (reworded slightly) as a comm
Leaf
2015/08/24 21:32:09
Just as a passing comment - we've discussed keepin
| |
| 92 /** | |
| 93 * The type provider used to look up types. | |
| 94 */ | |
| 95 final TypeProvider typeProvider; | |
| 96 | |
| 97 /** | |
| 98 * The type system used to compute the least upper bound of types. | |
| 99 */ | |
| 100 TypeSystem typeSystem; | |
| 101 | |
| 102 /** | |
| 103 * The inheritance manager used to find overridden method. | |
| 104 */ | |
| 105 InheritanceManager inheritanceManager; | |
| 106 | |
| 107 /** | |
| 108 * The classes that have been visited while attempting to infer the types of | |
| 109 * instance members of some base class. | |
| 110 */ | |
| 111 HashSet<ClassElementImpl> elementsBeingInferred = | |
| 112 new HashSet<ClassElementImpl>(); | |
| 113 | |
| 114 /** | |
| 115 * Initialize a newly create inferrer. | |
| 116 */ | |
| 117 InstanceMemberInferrer(this.typeProvider) { | |
| 118 typeSystem = new TypeSystemImpl(typeProvider); | |
| 119 } | |
| 120 | |
| 121 /** | |
| 122 * Infer type information for all of the instance members in the given | |
| 123 * compilation [unit]. | |
| 124 */ | |
| 125 void inferCompilationUnit(CompilationUnitElement unit) { | |
| 126 inheritanceManager = new InheritanceManager(unit.library); | |
| 127 unit.types.forEach((ClassElement classElement) { | |
| 128 try { | |
| 129 _inferClass(classElement); | |
| 130 } on _CycleException { | |
| 131 // This is a short circuit return to prevent types that inherit from | |
| 132 // types containing a circular reference from being inferred. | |
| 133 } | |
| 134 }); | |
| 135 } | |
| 136 | |
| 137 /** | |
| 138 * Compute the best return type for a method that must be compatible with the | |
| 139 * return types of each of the given [overriddenMethods]. | |
| 140 */ | |
| 141 DartType _computeReturnType(List<ExecutableElement> overriddenMethods) { | |
| 142 DartType returnType = null; | |
| 143 int length = overriddenMethods.length; | |
| 144 for (int i = 0; i < length; i++) { | |
|
Paul Berry
2015/08/24 19:56:25
Why not "for (DartType type in overriddenMethods)"
Brian Wilkerson
2015/08/24 21:20:58
Historic reasons that no longer apply. I'm happy t
| |
| 145 DartType type = _getReturnType(overriddenMethods[i]); | |
| 146 if (returnType == null) { | |
| 147 returnType = type; | |
| 148 } else { | |
| 149 if (returnType != type) { | |
|
Paul Berry
2015/08/24 19:56:25
If I understand correctly, this means that if ther
Brian Wilkerson
2015/08/24 21:20:58
Done
| |
| 150 return typeProvider.dynamicType; | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 return returnType == null ? typeProvider.dynamicType : returnType; | |
| 155 } | |
| 156 | |
| 157 /** | |
| 158 * Return the element for the single parameter of the given [setter], or | |
| 159 * `null` if the executable element is not a setter or does not have a single | |
| 160 * parameter. | |
| 161 */ | |
| 162 ParameterElement _getParameter(ExecutableElement setter) { | |
| 163 if (setter is PropertyAccessorElement && setter.isSetter) { | |
| 164 List<ParameterElement> parameters = setter.parameters; | |
| 165 if (parameters.length == 1) { | |
| 166 return parameters[0]; | |
| 167 } | |
| 168 } | |
| 169 return null; | |
| 170 } | |
| 171 | |
| 172 DartType _getReturnType(ExecutableElement element) { | |
| 173 DartType returnType = element.returnType; | |
| 174 if (returnType == null) { | |
| 175 FunctionType functionType = element.type; | |
| 176 if (functionType != null) { | |
| 177 returnType = functionType.returnType; | |
| 178 } | |
| 179 } | |
| 180 if (returnType == null) { | |
| 181 return typeProvider.dynamicType; | |
| 182 } | |
| 183 return returnType; | |
| 184 } | |
| 185 | |
| 186 /** | |
| 187 * If the given [accessorElement] represents a non-synthetic instance getter | |
| 188 * for which no return type was provided, infer the return type of the getter. | |
| 189 */ | |
| 190 void _inferAccessor(PropertyAccessorElement accessorElement) { | |
| 191 if (!accessorElement.isSynthetic && | |
| 192 accessorElement.isGetter && | |
| 193 !accessorElement.isStatic && | |
| 194 accessorElement.hasImplicitReturnType && | |
| 195 _getReturnType(accessorElement).isDynamic) { | |
| 196 List<ExecutableElement> overriddenGetters = inheritanceManager | |
| 197 .lookupOverrides( | |
| 198 accessorElement.enclosingElement, accessorElement.name); | |
| 199 if (_onlyGetters(overriddenGetters)) { | |
| 200 DartType newType = _computeReturnType(overriddenGetters); | |
| 201 _setReturnType(accessorElement, newType); | |
| 202 (accessorElement.variable as FieldElementImpl).type = newType; | |
| 203 _setParameterType(accessorElement.correspondingSetter, newType); | |
| 204 } | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 /** | |
| 209 * Infer type information for all of the instance members in the given | |
| 210 * [classElement]. | |
| 211 */ | |
| 212 void _inferClass(ClassElement classElement) { | |
| 213 if (classElement is ClassElementImpl) { | |
| 214 if (classElement.hasBeenInferred) { | |
| 215 return; | |
| 216 } | |
| 217 if (!elementsBeingInferred.add(classElement)) { | |
| 218 // We have found a circularity in the class hierarchy. For now we just | |
| 219 // stop trying to infer any type information for any classes that | |
| 220 // inherit from any class in the cycle. We could potentially limit the | |
| 221 // algorithm to only not inferring types in the classes in the cycle, | |
| 222 // but it isn't clear that the results would be significantly better. | |
| 223 throw new _CycleException(); | |
|
Paul Berry
2015/08/24 19:56:24
I'm concerned that this will lead to nondeterminis
Brian Wilkerson
2015/08/24 21:20:57
I can't think of a case where there is any nondete
| |
| 224 } | |
| 225 try { | |
| 226 // | |
| 227 // Ensure that all of instance members in the supertypes have had types | |
| 228 // inferred for them. | |
| 229 // | |
| 230 _inferType(classElement.supertype); | |
| 231 classElement.mixins.forEach(_inferType); | |
| 232 classElement.interfaces.forEach(_inferType); | |
| 233 // | |
| 234 // Then infer the types for the members. | |
| 235 // | |
| 236 classElement.fields.forEach(_inferField); | |
| 237 classElement.accessors.forEach(_inferAccessor); | |
| 238 classElement.methods.forEach(_inferMethod); | |
| 239 classElement.hasBeenInferred = true; | |
| 240 } finally { | |
| 241 elementsBeingInferred.remove(classElement); | |
| 242 } | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 /** | |
| 247 * If the given [fieldElement] represents a non-synthetic instance field for | |
| 248 * which no type was provided, infer the type of the field. | |
| 249 */ | |
| 250 void _inferField(FieldElement fieldElement) { | |
| 251 if (!fieldElement.isSynthetic && | |
| 252 !fieldElement.isStatic && | |
| 253 fieldElement.hasImplicitType && | |
| 254 fieldElement.type.isDynamic) { | |
| 255 // | |
| 256 // First look for overridden getters with the same name as the field. | |
| 257 // | |
| 258 List<ExecutableElement> overriddenGetters = inheritanceManager | |
| 259 .lookupOverrides(fieldElement.enclosingElement, fieldElement.name); | |
| 260 DartType newType = null; | |
| 261 if (_onlyGetters(overriddenGetters)) { | |
| 262 List<ExecutableElement> overriddenSetters = inheritanceManager | |
| 263 .lookupOverrides( | |
| 264 fieldElement.enclosingElement, fieldElement.name + '='); | |
| 265 newType = _computeReturnType(overriddenGetters); | |
| 266 if (!_isCompatible(newType, overriddenSetters)) { | |
| 267 newType = null; | |
| 268 } | |
| 269 } | |
| 270 // | |
| 271 // Then, if none was found, infer the type from the initialization | |
| 272 // expression. | |
| 273 // | |
| 274 if (newType == null) { | |
| 275 if (fieldElement.initializer != null && | |
| 276 (fieldElement.isFinal || overriddenGetters.isEmpty)) { | |
| 277 newType = fieldElement.initializer.returnType; | |
| 278 } | |
| 279 } | |
| 280 if (newType != null && !newType.isBottom) { | |
| 281 (fieldElement as FieldElementImpl).type = newType; | |
| 282 _setReturnType(fieldElement.getter, newType); | |
| 283 _setParameterType(fieldElement.setter, newType); | |
| 284 } | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 /** | |
| 289 * If the given [methodElement] represents a non-synthetic instance method | |
| 290 * for which no return type was provided, infer the return type of the method. | |
| 291 */ | |
| 292 void _inferMethod(MethodElement methodElement) { | |
| 293 if (!methodElement.isSynthetic && | |
| 294 !methodElement.isStatic && | |
| 295 methodElement.hasImplicitReturnType && | |
| 296 _getReturnType(methodElement).isDynamic) { | |
| 297 List<ExecutableElement> overriddenMethods = inheritanceManager | |
| 298 .lookupOverrides(methodElement.enclosingElement, methodElement.name); | |
| 299 if (_onlyMethods(overriddenMethods)) { | |
| 300 MethodElementImpl element = methodElement as MethodElementImpl; | |
| 301 _setReturnType(element, _computeReturnType(overriddenMethods)); | |
| 302 } | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 /** | |
| 307 * Infer type information for all of the instance members in the given | |
| 308 * interface [type]. | |
| 309 */ | |
| 310 void _inferType(InterfaceType type) { | |
| 311 if (type != null) { | |
| 312 ClassElement element = type.element; | |
| 313 if (element != null) { | |
| 314 _inferClass(element); | |
| 315 } | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 /** | |
| 320 * Return `true` if the given [type] is compatible with the argument types of | |
| 321 * all of the given [setters]. | |
| 322 */ | |
| 323 bool _isCompatible(DartType type, List<ExecutableElement> setters) { | |
| 324 for (ExecutableElement setter in setters) { | |
| 325 ParameterElement parameter = _getParameter(setter); | |
| 326 if (parameter != null && !typeSystem.isSubtypeOf(parameter.type, type)) { | |
| 327 return false; | |
| 328 } | |
| 329 } | |
| 330 return true; | |
| 331 } | |
| 332 | |
| 333 /** | |
| 334 * Return `true` if the list of [elements] contains only getters. | |
| 335 */ | |
| 336 bool _onlyGetters(List<ExecutableElement> elements) { | |
| 337 for (ExecutableElement element in elements) { | |
| 338 if (!(element is PropertyAccessorElement && element.isGetter)) { | |
| 339 return false; | |
| 340 } | |
| 341 } | |
| 342 return elements.isNotEmpty; | |
|
Leaf
2015/08/24 18:24:48
I can't work out why this matters, nor why it shou
Paul Berry
2015/08/24 19:56:25
I'm confused by this too. If the non-empty behavi
Brian Wilkerson
2015/08/24 21:20:58
When _computeReturnType is passed an empty list it
Leaf
2015/08/24 21:32:09
Ah, missed that, makes sense. I think this way of
| |
| 343 } | |
| 344 | |
| 345 /** | |
| 346 * Return `true` if the list of [elements] contains only methods. | |
| 347 */ | |
| 348 bool _onlyMethods(List<ExecutableElement> elements) { | |
| 349 for (ExecutableElement element in elements) { | |
| 350 if (element is! MethodElement) { | |
| 351 return false; | |
| 352 } | |
| 353 } | |
| 354 return true; | |
| 355 } | |
| 356 | |
| 357 /** | |
| 358 * Set the type of the sole parameter of the given [element] to the given [typ e]. | |
| 359 */ | |
| 360 void _setParameterType(PropertyAccessorElement element, DartType type) { | |
| 361 if (element is PropertyAccessorElementImpl) { | |
| 362 ParameterElement parameter = _getParameter(element); | |
| 363 if (parameter is ParameterElementImpl) { | |
| 364 // | |
| 365 // Update the type of the parameter. | |
| 366 // | |
| 367 parameter.type = type; | |
| 368 // | |
| 369 // Update the type of the setter to reflect the new parameter type. | |
| 370 // | |
| 371 FunctionType functionType = element.type; | |
| 372 if (functionType is FunctionTypeImpl) { | |
| 373 element.type = | |
| 374 new FunctionTypeImpl(element, functionType.prunedTypedefs); | |
| 375 } | |
| 376 } | |
| 377 } | |
| 378 } | |
| 379 | |
| 380 /** | |
| 381 * Set the return type of the given [element] to the given [type]. | |
| 382 */ | |
| 383 void _setReturnType(ExecutableElement element, DartType type) { | |
| 384 if (element is ExecutableElementImpl) { | |
| 385 // | |
| 386 // Update the return type of the element, which is stored in two places: | |
| 387 // directly in the element and indirectly in the type of the element. | |
| 388 // | |
| 389 element.returnType = type; | |
| 390 FunctionType functionType = element.type; | |
| 391 if (functionType is FunctionTypeImpl) { | |
| 392 element.type = | |
| 393 new FunctionTypeImpl(element, functionType.prunedTypedefs); | |
| 394 } | |
| 395 } | |
| 396 } | |
| 397 } | |
| 398 | |
| 399 /** | |
| 400 * A class of exception that is not used anywhere else. | |
| 401 */ | |
| 402 class _CycleException implements Exception {} | |
| OLD | NEW |