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 |