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

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: Address comments Created 5 years, 3 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 {
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 {}
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/generated/resolver.dart ('k') | pkg/analyzer/test/src/task/strong_mode_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698