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 |