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 { | |
| 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. The base class will be the first | |
| 110 * element of the list, the current class will be the last. | |
|
Leaf
2015/08/22 00:06:55
Are the references to ordering and list here out o
Brian Wilkerson
2015/08/24 16:52:58
It was out of date, and has been removed.
I was g
| |
| 111 */ | |
| 112 HashSet<ClassElementImpl> elementsBeingInferred = | |
| 113 new HashSet<ClassElementImpl>(); | |
| 114 | |
| 115 /** | |
| 116 * Initialize a newly create inferrer. | |
| 117 */ | |
| 118 InstanceMemberInferrer(this.typeProvider) { | |
| 119 typeSystem = new TypeSystemImpl(typeProvider); | |
| 120 } | |
| 121 | |
| 122 /** | |
| 123 * Infer type information for all of the instance members in the given | |
| 124 * compilation [unit]. | |
| 125 */ | |
| 126 void inferCompilationUnit(CompilationUnitElement unit) { | |
| 127 inheritanceManager = new InheritanceManager(unit.library); | |
| 128 unit.types.forEach((ClassElement classElement) { | |
| 129 try { | |
| 130 _inferClass(classElement); | |
| 131 } on _CycleException { | |
| 132 // This is a short circuit return to prevent types that inherit from | |
| 133 // types containing a circular reference from being inferred. | |
| 134 } | |
| 135 }); | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Compute the best return type for a method that must be compatible with the | |
| 140 * return types of each of the given [overriddenMethods]. | |
| 141 */ | |
| 142 DartType _computeReturnType(List<ExecutableElement> overriddenMethods) { | |
| 143 DartType returnType = null; | |
| 144 int length = overriddenMethods.length; | |
| 145 for (int i = 0; i < length; i++) { | |
| 146 DartType type = _getReturnType(overriddenMethods[i]); | |
| 147 if (returnType == null) { | |
| 148 returnType = type; | |
| 149 } else { | |
| 150 if (returnType != type) { | |
| 151 return typeProvider.dynamicType; | |
| 152 } | |
| 153 } | |
| 154 } | |
| 155 return returnType == null ? typeProvider.dynamicType : returnType; | |
| 156 } | |
| 157 | |
| 158 /** | |
| 159 * Return the element for the single parameter of the given [setter], or | |
| 160 * `null` if the executable element is not a setter or does not have a single | |
|
Leaf
2015/08/22 00:06:55
This doesn't actually check that the executable el
Brian Wilkerson
2015/08/24 16:52:57
No, but it does now.
| |
| 161 * parameter. | |
| 162 */ | |
| 163 ParameterElement _getParameter(ExecutableElement setter) { | |
| 164 if (setter != null) { | |
| 165 List<ParameterElement> parameters = setter.parameters; | |
| 166 if (parameters.length == 1) { | |
| 167 return parameters[0]; | |
| 168 } | |
| 169 } | |
| 170 return null; | |
| 171 } | |
| 172 | |
| 173 DartType _getReturnType(ExecutableElement element) { | |
| 174 DartType returnType = element.returnType; | |
| 175 if (returnType == null) { | |
| 176 FunctionType functionType = element.type; | |
|
Leaf
2015/08/22 00:06:55
When does this happen (we have no returnType, but
Brian Wilkerson
2015/08/24 16:52:57
In the absence of bugs, it doesn't. I was just bei
Paul Berry
2015/08/24 19:56:24
FWIW, I think I would argue in this case that this
Brian Wilkerson
2015/08/24 21:20:57
Ok. I've removed it.
| |
| 177 if (functionType != null) { | |
| 178 returnType = functionType.returnType; | |
| 179 } | |
| 180 } | |
| 181 if (returnType == null) { | |
| 182 return typeProvider.dynamicType; | |
| 183 } | |
| 184 return returnType; | |
| 185 } | |
| 186 | |
| 187 /** | |
| 188 * If the given [accessorElement] represents a non-synthetic instance getter | |
| 189 * for which no return type was provided, infer the return type of the getter. | |
| 190 */ | |
| 191 void _inferAccessor(PropertyAccessorElement accessorElement) { | |
| 192 if (!accessorElement.isSynthetic && | |
| 193 accessorElement.isGetter && | |
| 194 !accessorElement.isStatic && | |
| 195 accessorElement.hasImplicitReturnType && | |
| 196 _getReturnType(accessorElement).isDynamic) { | |
| 197 List<ExecutableElement> overriddenGetters = inheritanceManager | |
| 198 .lookupOverrides( | |
| 199 accessorElement.enclosingElement, accessorElement.name); | |
| 200 if (_onlyGetters(overriddenGetters)) { | |
| 201 DartType newType = _computeReturnType(overriddenGetters); | |
| 202 _setReturnType(accessorElement, newType); | |
| 203 (accessorElement.variable as FieldElementImpl).type = newType; | |
| 204 _setParameterType(accessorElement.correspondingSetter, newType); | |
| 205 } | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 /** | |
| 210 * Infer type information for all of the instance members in the given | |
| 211 * [classElement]. | |
| 212 */ | |
| 213 void _inferClass(ClassElement classElement) { | |
| 214 if (classElement is ClassElementImpl) { | |
| 215 if (classElement.hasBeenInferred) { | |
| 216 return; | |
| 217 } | |
| 218 if (!elementsBeingInferred.add(classElement)) { | |
| 219 // We have found a circularity in the class hierarchy. For now we just | |
| 220 // stop trying to infer any type information for any classes that | |
| 221 // inherit from any class in the cycle. We could potentially limit the | |
| 222 // algorithm to only not inferring types in the classes in the cycle, | |
| 223 // but it isn't clear that the results would be significantly better. | |
| 224 throw new _CycleException(); | |
| 225 } | |
| 226 try { | |
| 227 // | |
| 228 // Ensure that all of instance members in the supertypes have had types | |
| 229 // inferred for them. | |
| 230 // | |
| 231 _inferType(classElement.supertype); | |
| 232 classElement.mixins.forEach(_inferType); | |
| 233 classElement.interfaces.forEach(_inferType); | |
| 234 if (classElement.hasBeenInferred) { | |
|
Leaf
2015/08/22 00:06:55
Can this happen? We know it was false on entry to
Brian Wilkerson
2015/08/24 16:52:57
No, this was left over from when I was going to se
| |
| 235 return; | |
| 236 } | |
| 237 // | |
| 238 // Then infer the types for the members. | |
| 239 // | |
| 240 classElement.fields.forEach(_inferField); | |
| 241 classElement.accessors.forEach(_inferAccessor); | |
| 242 classElement.methods.forEach(_inferMethod); | |
| 243 classElement.hasBeenInferred = true; | |
| 244 } finally { | |
| 245 elementsBeingInferred.remove(classElement); | |
| 246 } | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 /** | |
| 251 * If the given [fieldElement] represents a non-synthetic instance field for | |
| 252 * which no type was provided, infer the type of the field. | |
| 253 */ | |
| 254 void _inferField(FieldElement fieldElement) { | |
| 255 if (!fieldElement.isSynthetic && | |
| 256 !fieldElement.isStatic && | |
| 257 fieldElement.hasImplicitType && | |
| 258 fieldElement.type.isDynamic) { | |
| 259 // | |
| 260 // First look for overridden getters with the same name as the field. | |
| 261 // | |
| 262 List<ExecutableElement> overriddenGetters = inheritanceManager | |
| 263 .lookupOverrides(fieldElement.enclosingElement, fieldElement.name); | |
| 264 DartType newType = typeProvider.dynamicType; | |
| 265 if (_onlyGetters(overriddenGetters)) { | |
| 266 List<ExecutableElement> overriddenSetters = inheritanceManager | |
| 267 .lookupOverrides( | |
| 268 fieldElement.enclosingElement, fieldElement.name + '='); | |
| 269 newType = _computeReturnType(overriddenGetters); | |
| 270 if (!_isCompatible(newType, overriddenSetters)) { | |
| 271 newType = typeProvider.dynamicType; | |
| 272 } | |
| 273 } | |
| 274 // | |
| 275 // Then, if none was found, infer the type from the initialization | |
| 276 // expression. | |
| 277 // | |
| 278 if (newType.isDynamic && fieldElement.initializer != null) { | |
|
Leaf
2015/08/22 00:06:55
I think we only want to do this if either this is
Brian Wilkerson
2015/08/24 16:52:58
Done
| |
| 279 newType = fieldElement.initializer.returnType; | |
|
Leaf
2015/08/22 00:06:55
The old code postponed doing this initializer base
Brian Wilkerson
2015/08/24 16:52:58
It's possible for the code to be invalid (and ther
| |
| 280 } | |
|
Leaf
2015/08/22 00:06:55
Siggi's code avoids inferring from the initializer
Brian Wilkerson
2015/08/24 16:52:57
No. I just saw that case while looking through the
| |
| 281 (fieldElement as FieldElementImpl).type = newType; | |
| 282 _setReturnType(fieldElement.getter, newType); | |
| 283 _setParameterType(fieldElement.setter, newType); | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 /** | |
| 288 * If the given [methodElement] represents a non-synthetic instance method | |
| 289 * for which no return type was provided, infer the return type of the method. | |
| 290 */ | |
| 291 void _inferMethod(MethodElement methodElement) { | |
| 292 if (!methodElement.isSynthetic && | |
| 293 !methodElement.isStatic && | |
| 294 methodElement.hasImplicitReturnType && | |
| 295 _getReturnType(methodElement).isDynamic) { | |
| 296 List<ExecutableElement> overriddenMethods = inheritanceManager | |
| 297 .lookupOverrides(methodElement.enclosingElement, methodElement.name); | |
| 298 if (_onlyMethods(overriddenMethods)) { | |
| 299 MethodElementImpl element = methodElement as MethodElementImpl; | |
| 300 _setReturnType(element, _computeReturnType(overriddenMethods)); | |
| 301 } | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 /** | |
| 306 * Infer type information for all of the instance members in the given | |
| 307 * interface [type]. | |
| 308 */ | |
| 309 void _inferType(InterfaceType type) { | |
| 310 if (type != null) { | |
| 311 ClassElement element = type.element; | |
| 312 if (element != null) { | |
| 313 _inferClass(element); | |
| 314 } | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 /** | |
| 319 * Return `true` if the given [type] is compatible with the argument types of | |
| 320 * all of the given [setters]. | |
| 321 */ | |
| 322 bool _isCompatible(DartType type, List<ExecutableElement> setters) { | |
| 323 for (ExecutableElement setter in setters) { | |
| 324 ParameterElement parameter = _getParameter(setter); | |
| 325 if (parameter != null && !type.isAssignableTo(parameter.type)) { | |
|
Leaf
2015/08/22 00:06:55
Once we've added a type system, we need to make th
Brian Wilkerson
2015/08/24 16:52:58
Done
| |
| 326 return false; | |
| 327 } | |
| 328 } | |
| 329 return true; | |
| 330 } | |
| 331 | |
| 332 /** | |
| 333 * Return `true` if the list of [elements] contains only getters. | |
| 334 */ | |
| 335 bool _onlyGetters(List<ExecutableElement> elements) { | |
| 336 for (ExecutableElement element in elements) { | |
| 337 if (!(element is PropertyAccessorElement && element.isGetter)) { | |
| 338 return false; | |
| 339 } | |
| 340 } | |
| 341 return true; | |
| 342 } | |
| 343 | |
| 344 /** | |
| 345 * Return `true` if the list of [elements] contains only methods. | |
| 346 */ | |
| 347 bool _onlyMethods(List<ExecutableElement> elements) { | |
| 348 for (ExecutableElement element in elements) { | |
| 349 if (element is! MethodElement) { | |
| 350 return false; | |
| 351 } | |
| 352 } | |
| 353 return true; | |
| 354 } | |
| 355 | |
| 356 /** | |
| 357 * Set the type of the sole parameter of the given [element] to the given [typ e]. | |
| 358 */ | |
| 359 void _setParameterType(PropertyAccessorElement element, DartType type) { | |
| 360 if (element is PropertyAccessorElementImpl) { | |
| 361 ParameterElement parameter = _getParameter(element); | |
| 362 if (parameter is ParameterElementImpl) { | |
| 363 parameter.type = type; | |
| 364 FunctionType functionType = element.type; | |
| 365 if (functionType is FunctionTypeImpl) { | |
| 366 element.type = | |
| 367 new FunctionTypeImpl(element, functionType.prunedTypedefs); | |
| 368 } | |
| 369 } | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 /** | |
| 374 * Set the return type of the given [element] to the given [type]. | |
| 375 */ | |
| 376 void _setReturnType(ExecutableElement element, DartType type) { | |
| 377 if (element is ExecutableElementImpl) { | |
| 378 element.returnType = type; | |
| 379 FunctionType functionType = element.type; | |
|
Leaf
2015/08/22 00:06:55
The code sequence from here down is duplicated abo
Brian Wilkerson
2015/08/24 16:52:57
Comments added. (I can't easily abstract the code
| |
| 380 if (functionType is FunctionTypeImpl) { | |
| 381 element.type = | |
| 382 new FunctionTypeImpl(element, functionType.prunedTypedefs); | |
| 383 } | |
| 384 } | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 /** | |
| 389 * A class of exception that is not used anywhere else. | |
| 390 */ | |
| 391 class _CycleException implements Exception {} | |
| OLD | NEW |