Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(319)

Side by Side Diff: pkg/analyzer/lib/src/task/strong_mode.dart

Issue 1306313002: Implement instance member inference (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698