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

Side by Side Diff: lib/src/checker/resolver.dart

Issue 1011933002: Handle type-inference on fields, consts, and inferable overrides (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 9 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
« no previous file with comments | « no previous file | lib/src/options.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 /// Encapsulates how to invoke the analyzer resolver and overrides how it 5 /// Encapsulates how to invoke the analyzer resolver and overrides how it
6 /// computes types on expressions to use our restricted set of types. 6 /// computes types on expressions to use our restricted set of types.
7 library dev_compiler.src.checker.resolver; 7 library dev_compiler.src.checker.resolver;
8 8
9 import 'package:analyzer/analyzer.dart'; 9 import 'package:analyzer/analyzer.dart';
10 import 'package:analyzer/src/generated/ast.dart'; 10 import 'package:analyzer/src/generated/ast.dart';
11 import 'package:analyzer/src/generated/element.dart'; 11 import 'package:analyzer/src/generated/element.dart';
12 import 'package:analyzer/src/generated/engine.dart'; 12 import 'package:analyzer/src/generated/engine.dart';
13 import 'package:analyzer/src/generated/error.dart' as analyzer; 13 import 'package:analyzer/src/generated/error.dart' as analyzer;
14 import 'package:analyzer/src/generated/java_io.dart' show JavaFile; 14 import 'package:analyzer/src/generated/java_io.dart' show JavaFile;
15 import 'package:analyzer/src/generated/resolver.dart'; 15 import 'package:analyzer/src/generated/resolver.dart';
16 import 'package:analyzer/src/generated/static_type_analyzer.dart';
17 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk; 16 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk;
18 import 'package:analyzer/src/generated/source.dart' show DartUriResolver; 17 import 'package:analyzer/src/generated/source.dart' show DartUriResolver;
19 import 'package:analyzer/src/generated/source.dart' show Source; 18 import 'package:analyzer/src/generated/source.dart' show Source;
20 import 'package:analyzer/src/generated/source_io.dart'; 19 import 'package:analyzer/src/generated/source_io.dart';
20 import 'package:analyzer/src/generated/static_type_analyzer.dart';
21 import 'package:logging/logging.dart' as logger; 21 import 'package:logging/logging.dart' as logger;
22 22
23 import 'package:dev_compiler/src/options.dart'; 23 import 'package:dev_compiler/src/options.dart';
24 import 'package:dev_compiler/src/report.dart'; 24 import 'package:dev_compiler/src/report.dart';
25 import 'package:dev_compiler/src/utils.dart'; 25 import 'package:dev_compiler/src/utils.dart';
26 import 'dart_sdk.dart'; 26 import 'dart_sdk.dart';
27 import 'multi_package_resolver.dart'; 27 import 'multi_package_resolver.dart';
28 28
29 final _log = new logger.Logger('dev_compiler.src.resolver'); 29 final _log = new logger.Logger('dev_compiler.src.resolver');
30 30
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 99
100 const AnalyzerError(String message, logger.Level level, int begin, int end) 100 const AnalyzerError(String message, logger.Level level, int begin, int end)
101 : super('[from analyzer]: $message', level, begin, end); 101 : super('[from analyzer]: $message', level, begin, end);
102 } 102 }
103 103
104 /// Creates an analysis context that contains our restricted typing rules. 104 /// Creates an analysis context that contains our restricted typing rules.
105 InternalAnalysisContext _initContext(ResolverOptions options) { 105 InternalAnalysisContext _initContext(ResolverOptions options) {
106 var analysisOptions = new AnalysisOptionsImpl()..cacheSize = 512; 106 var analysisOptions = new AnalysisOptionsImpl()..cacheSize = 512;
107 AnalysisContextImpl res = AnalysisEngine.instance.createAnalysisContext(); 107 AnalysisContextImpl res = AnalysisEngine.instance.createAnalysisContext();
108 res.analysisOptions = analysisOptions; 108 res.analysisOptions = analysisOptions;
109 res.resolverVisitorFactory = RestrictedResolverVisitor.constructor(options); 109 res.libraryResolverFactory =
110 if (options.inferFromOverrides) { 110 (context) => new LibraryResolverWithInference(context, options);
111 res.typeResolverVisitorFactory = RestrictedTypeResolverVisitor.constructor;
112 }
113 return res; 111 return res;
114 } 112 }
115 113
116 /// Overrides the default [ResolverVisitor] to comply with DDC's restricted 114 /// A [LibraryResolver] that performs inference on top-levels and fields based
117 /// type rules. This changes how types are promoted in conditional expressions 115 /// on the value of the initializer, and on fields and methods based on
118 /// and statements, and how types are computed on expressions. 116 /// overriden members in super classes.
vsm 2015/03/17 15:55:27 nit: overriden -> overridden
Siggi Cherem (dart-lang) 2015/03/17 16:29:22 Done.
117 class LibraryResolverWithInference extends LibraryResolver {
118 final ResolverOptions _options;
119
120 LibraryResolverWithInference(context, this._options) : super(context);
121
122 @override
123 void resolveReferencesAndTypes() {
124 _resolveVariableReferences();
125
126 // Skip inference in the core libraries.
127 if (resolvedLibraries.any((l) => l.librarySource.isInSystemLibrary)) {
vsm 2015/03/17 15:55:27 Is resolvedLibraries the cycle here? I.e., if one
Siggi Cherem (dart-lang) 2015/03/17 16:29:22 Yeah, exactly, I added a comment to be clear.
128 _resolveReferencesAndTypes(false);
129 return;
130 }
131
132 // Run resolution in two stages, skipping method bodies first, so we can run
133 // type-inference before we fully analyze methods.
134 _resolveReferencesAndTypes(true);
135 _runInference();
136 _resolveReferencesAndTypes(false);
137 }
138
139 // Note: this was split from _resolveReferencesAndTypesInLibrary so we do it
140 // only once.
141 void _resolveVariableReferences() {
142 for (Library library in resolvedLibraries) {
143 for (Source source in library.compilationUnitSources) {
144 library.getAST(source).accept(
145 new VariableResolverVisitor.con1(library, source, typeProvider));
146 }
147 }
148 }
149
150 // Note: this was split from _resolveReferencesAndTypesInLibrary so we can do
151 // resolution in pieces.
152 void _resolveReferencesAndTypes(bool skipMethods) {
153 for (Library library in resolvedLibraries) {
154 for (Source source in library.compilationUnitSources) {
155 library.getAST(source).accept(new RestrictedResolverVisitor(
156 library, source, typeProvider, _options, skipMethods));
157 }
158 }
159 }
160
161 _runInference() {
162 var consts = [];
163 var statics = [];
164 var classes = [];
165
166 // Extract top-level members that are const, statics, or classes.
167 for (Library library in resolvedLibraries) {
168 for (Source source in library.compilationUnitSources) {
169 CompilationUnit ast = library.getAST(source);
170 for (var declaration in ast.declarations) {
171 if (declaration is TopLevelVariableDeclaration) {
172 if (declaration.variables.isConst) {
173 consts.add(declaration.variables);
174 } else {
175 statics.add(declaration.variables);
176 }
177 } else if (declaration is ClassDeclaration) {
178 classes.add(declaration);
179 for (var member in declaration.members) {
180 if (member is! FieldDeclaration) continue;
181 if (member.fields.isConst) {
182 consts.add(member.fields);
183 } else if (member.isStatic) {
184 statics.add(member.fields);
185 }
186 }
187 }
188 }
189 }
190 }
191
192 // TODO(sigmund): consider propagating const types after this layer of
193 // inferece, so their types can be used to initialize other members below.
vsm 2015/03/17 15:55:27 nit: inferece -> inference
Siggi Cherem (dart-lang) 2015/03/17 16:29:22 Done.
194 _inferVariableFromInitializer(consts);
195 _inferVariableFromInitializer(statics);
196
197 // Track types in this strongest connected component, ensure we visit
vsm 2015/03/17 15:55:27 strongly?
Siggi Cherem (dart-lang) 2015/03/17 16:29:22 Done.
198 // supertypes before subtypes.
199 var typeToDeclaration = <InterfaceType, ClassDeclaration>{};
200 classes.forEach((c) => typeToDeclaration[c.element.type] = c);
201 var seen = new Set<InterfaceType>();
202 visit(ClassDeclaration cls) {
203 var element = cls.element;
204 var type = element.type;
205 if (seen.contains(type)) return;
206 for (var supertype in element.allSupertypes) {
207 var supertypeClass = typeToDeclaration[supertype];
208 if (supertypeClass != null) visit(supertypeClass);
209 }
210 seen.add(type);
211
212 _isInstanceField(f) =>
213 f is FieldDeclaration && !f.isStatic && !f.fields.isConst;
214
215 if (_options.inferFromOverrides) {
216 // Infer field types from overrides first, otherwise from initializers.
217 var pending = new Set<VariableDeclarationList>();
218 cls.members
219 .where(_isInstanceField)
220 .forEach((f) => _inferFieldTypeFromOverride(f, pending));
221 if (pending.isNotEmpty) _inferVariableFromInitializer(pending);
222
223 // Infer return-types from overrides
224 cls.members
225 .where((m) => m is MethodDeclaration && !m.isStatic)
226 .forEach(_inferMethodReturnTypeFromOverride);
227 } else {
228 _inferVariableFromInitializer(
229 cls.members.where(_isInstanceField).map((f) => f.fields));
230 }
231 }
232 classes.forEach(visit);
233 }
234
235 /// Attempts to infer the type on [field] from overriden fields or getters if
236 /// a type was not specified. If no type could be inferred, but it contains an
237 /// initializer, we add it to [pending] so we can try to infer it using the
238 /// initializer type instead.
239 void _inferFieldTypeFromOverride(
240 FieldDeclaration field, Set<VariableDeclarationList> pending) {
241 var variables = field.fields;
242 for (var variable in variables.variables) {
243 var varElement = variable.element;
244 if (!varElement.type.isDynamic || variables.type != null) continue;
245 var getter = varElement.getter;
246 var type = searchTypeFor(varElement.enclosingElement.type, getter);
247 if (type != null && !type.returnType.isDynamic) {
248 var newType = type.returnType;
249 varElement.type = newType;
250 varElement.getter.returnType = newType;
251 if (!varElement.isFinal) varElement.setter.parameters[0].type = newType;
252 } else if (variable.initializer != null) {
253 pending.add(variables);
254 }
255 }
256 }
257
258 void _inferMethodReturnTypeFromOverride(MethodDeclaration method) {
259 var methodElement = method.element;
260 if ((methodElement is MethodElement ||
261 methodElement is PropertyAccessorElement) &&
262 methodElement.returnType.isDynamic &&
263 method.returnType == null) {
264 var type =
265 searchTypeFor(methodElement.enclosingElement.type, methodElement);
266 if (type != null && !type.returnType.isDynamic) {
267 methodElement.returnType = type.returnType;
268 }
269 }
270 }
271
272 void _inferVariableFromInitializer(
273 Iterable<VariableDeclarationList> variables) {
274 for (var variableList in variables) {
275 if (variableList.type != null) continue;
276 if (_options.onlyInferConstsAndFinalFields &&
277 !variableList.isFinal &&
278 !variableList.isConst) {
279 return;
280 }
281 for (var variable in variableList.variables) {
282 var initializer = variable.initializer;
283 if (initializer == null) continue;
284 var type = initializer.staticType;
285 if (type == null || type.isDynamic) continue;
vsm 2015/03/17 15:55:27 If the initializer type is Null or Bottom, we migh
Siggi Cherem (dart-lang) 2015/03/17 16:29:22 Ah, interesting. Good point. I added a check for i
286 if (!_canInferFrom(initializer)) continue;
287 var element = variable.element;
vsm 2015/03/17 15:55:27 Shouldn't we only update the type if it was declar
Siggi Cherem (dart-lang) 2015/03/17 16:29:22 Correct, the check in line 275 above: if (variab
vsm 2015/03/17 17:18:48 Ah, thanks - missed the check above.
288 // Note: it's ok to update the type here, since initializer.staticType
289 // is already computed for all declarations in the library cycle. The
290 // new types will only be propagated on a second run of the
291 // ResolverVisitor.
292 element.type = type;
293 element.getter.returnType = type;
294 if (!element.isFinal && !element.isConst) {
295 element.setter.parameters[0].type = type;
296 }
297 }
298 }
299 }
300
301 bool _canInferFrom(Expression expression) {
302 if (expression is Literal) return true;
303 if (expression is InstanceCreationExpression) return true;
304 if (expression is FunctionExpression) return true;
305 if (expression is AsExpression) return true;
306 if (expression is CascadeExpression) {
307 return _canInferFrom(expression.target);
308 }
309 if (expression is SimpleIdentifier || expression is PropertyAccess) {
310 return _options.inferStaticsFromIdentifiers;
311 }
312 if (expression is PrefixedIdentifier) {
313 if (expression.staticElement is PropertyAccessorElement) {
314 return _options.inferStaticsFromIdentifiers;
315 }
316 return _canInferFrom(expression.identifier);
317 }
318 if (expression is MethodInvocation) {
319 return _canInferFrom(expression.target);
320 }
321 if (expression is BinaryExpression) {
322 return _canInferFrom(expression.leftOperand);
323 }
324 if (expression is ConditionalExpression) {
325 return _canInferFrom(expression.thenExpression) &&
326 _canInferFrom(expression.elseExpression);
327 }
328 if (expression is PrefixExpression) {
329 return _canInferFrom(expression.operand);
330 }
331 if (expression is PostfixExpression) {
332 return _canInferFrom(expression.operand);
333 }
334 return false;
335 }
336 }
337
338 /// Overrides the default [ResolverVisitor] to support type inference in
339 /// [LibraryResolverWithInference] above.
340 ///
341 /// Before inference, this visitor is used to resolve top-levels, classes, and
342 /// fields, but nothing withihn method bodies. After inference, this visitor is
343 /// used again to step into method bodies and complete resolution as a second
344 /// phase.
119 class RestrictedResolverVisitor extends ResolverVisitor { 345 class RestrictedResolverVisitor extends ResolverVisitor {
120 final TypeProvider _typeProvider; 346 final TypeProvider _typeProvider;
121 347
348 /// Whether to skip resolution within method bodies.
349 final bool skipMethodBodies;
350
122 RestrictedResolverVisitor(Library library, Source source, 351 RestrictedResolverVisitor(Library library, Source source,
123 TypeProvider typeProvider, ResolverOptions options) 352 TypeProvider typeProvider, ResolverOptions options, this.skipMethodBodies)
124 : _typeProvider = typeProvider, 353 : _typeProvider = typeProvider,
125 super.con1(library, source, typeProvider, 354 super.con1(library, source, typeProvider,
126 typeAnalyzerFactory: RestrictedStaticTypeAnalyzer 355 typeAnalyzerFactory: RestrictedStaticTypeAnalyzer.constructor);
127 .constructor(options));
128
129 static constructor(options) =>
130 (Library library, Source source, TypeProvider typeProvider) =>
131 new RestrictedResolverVisitor(library, source, typeProvider, options);
132 356
133 @override 357 @override
134 visitCatchClause(CatchClause node) { 358 visitCatchClause(CatchClause node) {
135 var stack = node.stackTraceParameter; 359 var stack = node.stackTraceParameter;
136 if (stack != null) { 360 if (stack != null) {
137 // TODO(jmesserly): analyzer does not correctly associate StackTrace type. 361 // TODO(jmesserly): analyzer does not correctly associate StackTrace type.
138 // It happens too late in TypeResolverVisitor visitCatchClause. 362 // It happens too late in TypeResolverVisitor visitCatchClause.
139 var element = stack.staticElement; 363 var element = stack.staticElement;
140 if (element is VariableElementImpl && element.type == null) { 364 if (element is VariableElementImpl && element.type == null) {
141 // From the language spec: 365 // From the language spec:
142 // The static type of p1 is T and the static type of p2 is StackTrace. 366 // The static type of p1 is T and the static type of p2 is StackTrace.
143 element.type = _typeProvider.stackTraceType; 367 element.type = _typeProvider.stackTraceType;
144 } 368 }
145 } 369 }
146 return super.visitCatchClause(node); 370 return super.visitCatchClause(node);
147 } 371 }
148 372
149 @override 373 @override
150 Object visitCompilationUnit(CompilationUnit node) { 374 Object visitNode(AstNode node) {
151 // Similar to the definition in ResolverVisitor.visitCompilationUnit, but 375 if (skipMethodBodies &&
152 // changed to visit all top-level fields first, then static fields on all 376 (node is FunctionBody ||
153 // classes, then all top-level functions, then the rest of the classes. 377 node is FunctionExpression ||
154 RestrictedStaticTypeAnalyzer restrictedAnalyzer = typeAnalyzer_J2DAccessor; 378 node is FunctionExpressionInvocation ||
155 overrideManager.enterScope(); 379 node is SuperConstructorInvocation ||
156 try { 380 node is RedirectingConstructorInvocation ||
157 var thisLib = node.element.enclosingElement; 381 node is Annotation ||
158 restrictedAnalyzer._isLibraryContainedInSingleUnit.putIfAbsent(thisLib, 382 node is Comment)) {
159 () { 383 return null;
160 if (thisLib.units.length > 1) return false;
161 for (var lib in thisLib.visibleLibraries) {
162 if (lib != thisLib && lib.visibleLibraries.contains(thisLib)) {
163 return false;
164 }
165 }
166 return true;
167 });
168
169 void accept(n) {
170 n.accept(this);
171 }
172 node.directives.forEach(accept);
173 var declarations = node.declarations;
174
175 declarations
176 .where((d) => d is TopLevelVariableDeclaration)
177 .forEach(accept);
178
179 // Visit classes before top-level methods so that we can visit static
180 // fields first.
181 // TODO(sigmund): consider visiting static fields only at this point
182 // (the challenge is that to visit them we first need to create the scope
183 // for the class here, and reuse it later when visiting the class
184 // declaration to ensure that we correctly construct the scopes and that
185 // we visit each static field only once).
186 declarations.where((d) => d is ClassDeclaration).forEach(accept);
187
188 declarations
189 .where((d) =>
190 d is! TopLevelVariableDeclaration && d is! ClassDeclaration)
191 .forEach(accept);
192 } finally {
193 overrideManager.exitScope();
194 } 384 }
195 node.accept(elementResolver_J2DAccessor); 385 assert(node is! Statement || !skipMethodBodies);
196 node.accept(restrictedAnalyzer); 386 return super.visitNode(node);
197 return null;
198 } 387 }
199 388
200 @override 389 @override
201 void visitClassMembersInScope(ClassDeclaration node) { 390 Object visitMethodDeclaration(MethodDeclaration node) {
202 safelyVisit(node.documentationComment); 391 if (skipMethodBodies) {
203 node.metadata.accept(this); 392 node.accept(elementResolver_J2DAccessor);
393 node.accept(typeAnalyzer_J2DAccessor);
394 return null;
395 } else {
396 return super.visitMethodDeclaration(node);
397 }
398 }
204 399
205 // This overrides the default way members are visited so that fields are 400 @override
206 // visited before method declarations. 401 Object visitFunctionDeclaration(FunctionDeclaration node) {
207 for (var n in node.members) { 402 if (skipMethodBodies) {
208 if (n is FieldDeclaration) n.accept(this); 403 node.accept(elementResolver_J2DAccessor);
404 node.accept(typeAnalyzer_J2DAccessor);
405 return null;
406 } else {
407 return super.visitFunctionDeclaration(node);
209 } 408 }
210 for (var n in node.members) { 409 }
211 if (n is! FieldDeclaration) n.accept(this); 410
411 @override
412 Object visitConstructorDeclaration(ConstructorDeclaration node) {
413 if (skipMethodBodies) {
414 node.accept(elementResolver_J2DAccessor);
415 node.accept(typeAnalyzer_J2DAccessor);
416 return null;
417 } else {
418 return super.visitConstructorDeclaration(node);
212 } 419 }
213 } 420 }
214 } 421 }
215 422
216 /// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter 423 /// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter
217 /// in the restricted type system and to infer types for untyped local 424 /// in the restricted type system and to infer types for untyped local
218 /// variables. 425 /// variables.
219 class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { 426 class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer {
220 final TypeProvider _typeProvider; 427 final TypeProvider _typeProvider;
221 final ResolverOptions _options;
222 428
223 // TODO(sigmund): this needs to go away. This is currently a restriction 429 RestrictedStaticTypeAnalyzer(ResolverVisitor r)
224 // because we are not overriding things early enough in the analyzer. This
225 // restriction makes it safe to run the inference later, but only on libraries
226 // that are contained in a single file and are not part of a cycle.
227 Map<LibraryElement, bool> _isLibraryContainedInSingleUnit = {};
228
229 RestrictedStaticTypeAnalyzer(ResolverVisitor r, this._options)
230 : _typeProvider = r.typeProvider, 430 : _typeProvider = r.typeProvider,
231 super(r); 431 super(r);
232 432
233 static constructor(options) => 433 static constructor(ResolverVisitor r) => new RestrictedStaticTypeAnalyzer(r);
234 (r) => new RestrictedStaticTypeAnalyzer(r, options);
235 434
236 @override // to infer type from initializers 435 @override // to infer type from initializers
237 visitVariableDeclaration(VariableDeclaration node) { 436 visitVariableDeclaration(VariableDeclaration node) {
238 _inferType(node); 437 _inferType(node);
239 return super.visitVariableDeclaration(node); 438 return super.visitVariableDeclaration(node);
240 } 439 }
241 440
242 /// Infer the type of a variable based on the initializer's type. 441 /// Infer the type of a variable based on the initializer's type.
243 void _inferType(VariableDeclaration node) { 442 void _inferType(VariableDeclaration node) {
244 var initializer = node.initializer; 443 var initializer = node.initializer;
245 if (initializer == null) return; 444 if (initializer == null) return;
246 445
247 var declaredType = (node.parent as VariableDeclarationList).type; 446 var declaredType = (node.parent as VariableDeclarationList).type;
248 if (declaredType != null) return; 447 if (declaredType != null) return;
249 var element = node.element; 448 var element = node.element;
449 if (element is! LocalVariableElement) return;
250 if (element.type != _typeProvider.dynamicType) return; 450 if (element.type != _typeProvider.dynamicType) return;
251 451
252 // Local variables can be inferred automatically, for top-levels and fields
253 // we rule out cases that could depend on the order in which we process
254 // them.
255 if (element is! LocalVariableElement) {
256 if (_options.onlyInferConstsAndFinalFields &&
257 !element.isConst &&
258 !element.isFinal) {
259 return;
260 }
261 // Only infer types if the library is not in a cycle. Otherwise we can't
262 // guarantee that we are order independent (we can't guarantee that we'll
263 // visit all top-level declarations in all libraries, before we visit
264 // methods in all libraries).
265 var thisLib = enclosingLibrary(element);
266 if (!_canBeInferredIndependently(initializer, thisLib)) return;
267 }
268
269 var type = initializer.staticType; 452 var type = initializer.staticType;
270 if (type == null || type == _typeProvider.bottomType) return; 453 if (type == null || type == _typeProvider.bottomType) return;
271 element.type = type; 454 element.type = type;
272 if (element is PropertyInducingElement) { 455 if (element is PropertyInducingElement) {
273 element.getter.returnType = type; 456 element.getter.returnType = type;
274 if (!element.isFinal && !element.isConst) { 457 if (!element.isFinal && !element.isConst) {
275 element.setter.parameters[0].type = type; 458 element.setter.parameters[0].type = type;
276 } 459 }
277 } 460 }
278 } 461 }
279 462
280 /// Whether we could determine the type of an [expression] in a way
281 /// that doesn't depend on the order in which we infer types within a
282 /// strongest connected component of libraries.
283 ///
284 /// This will return true if the expression consists just of literals or
285 /// allocations, if it only uses symbols that come from libraries that are
286 /// clearly processed before the library where this expression occurs
287 /// ([thisLib]), or if it's composed of these subexpressions (excluding fields
288 /// and top-levels that could've been inferred as well).
289 ///
290 /// The [inFieldContext] is used internally when visiting nested expressions
291 /// recursively. It indicates that the subexpression will be used in the
292 /// context of a field dereference.
293 bool _canBeInferredIndependently(
294 Expression expression, LibraryElement thisLib,
295 {bool inFieldContext: false}) {
296 if (_options.inferInNonStableOrder) return true;
297 if (!_options.inferStaticsFromIdentifiers && inFieldContext) return false;
298 if (!_isLibraryContainedInSingleUnit[thisLib]) return false;
299 if (expression is Literal) return true;
300
301 if (expression is InstanceCreationExpression) {
302 if (!inFieldContext) return true;
303 var element = expression.staticElement;
304 if (element == null) {
305 print('Unexpected `null` element for $expression');
306 return false;
307 }
308 return !_sameConnectedComponent(thisLib, element);
309 }
310 if (expression is FunctionExpression) return true;
311 if (expression is CascadeExpression) {
312 return _canBeInferredIndependently(expression.target, thisLib,
313 inFieldContext: inFieldContext);
314 }
315
316 if (expression is MethodInvocation) {
317 return _canBeInferredIndependently(expression.target, thisLib,
318 inFieldContext: true);
319 }
320
321 // Binary expressions, prefix/postfix expressions are are derived from the
322 // type of the operand, which is known at this time even for classes in the
323 // same library.
324 if (expression is BinaryExpression) {
325 return _canBeInferredIndependently(expression.leftOperand, thisLib,
326 inFieldContext: false);
327 }
328 if (expression is PrefixExpression) {
329 return _canBeInferredIndependently(expression.operand, thisLib,
330 inFieldContext: false);
331 }
332 if (expression is PostfixExpression) {
333 return _canBeInferredIndependently(expression.operand, thisLib,
334 inFieldContext: false);
335 }
336
337 // Property accesses and prefix identifiers can be resolved as fields, in
338 // which case, we need to choose whether or not to infer based on the
339 // target.
340 if (expression is PropertyAccess) {
341 return _canBeInferredIndependently(expression.target, thisLib,
342 inFieldContext: true);
343 }
344 if (expression is PrefixedIdentifier) {
345 return _canBeInferredIndependently(expression.identifier, thisLib,
346 inFieldContext: true);
347 }
348
349 if (expression is SimpleIdentifier) {
350 if (!_options.inferStaticsFromIdentifiers) return false;
351 var element = expression.bestElement;
352 if (element == null) {
353 print('Unexpected `null` element for $expression');
354 return false;
355 }
356 return !_sameConnectedComponent(thisLib, element);
357 }
358 return false;
359 }
360
361 /// Whether [dependency] is in the same strongest connected component of
362 /// libraries as [declaration].
363 bool _sameConnectedComponent(LibraryElement thisLib, Element dependency) {
364 assert(dependency != null);
365 var otherLib = enclosingLibrary(dependency);
366 // Note: we would check here also whether
367 // otherLib.visibleLibraries.contains(thisLib), however because we are not
368 // inferring type on any library that belongs to a cycle or that contains
369 // parts, we know that this cannot be true.
370 return thisLib == otherLib;
371 }
372
373 @override // to propagate types to identifiers 463 @override // to propagate types to identifiers
374 visitMethodInvocation(MethodInvocation node) { 464 visitMethodInvocation(MethodInvocation node) {
375 // TODO(sigmund): follow up with analyzer team - why is this needed? 465 // TODO(sigmund): follow up with analyzer team - why is this needed?
376 visitSimpleIdentifier(node.methodName); 466 visitSimpleIdentifier(node.methodName);
377 super.visitMethodInvocation(node); 467 super.visitMethodInvocation(node);
378 468
379 var e = node.methodName.staticElement; 469 var e = node.methodName.staticElement;
380 if (e is FunctionElement && 470 if (e is FunctionElement &&
381 e.library.name == '_foreign_helper' && 471 e.library.name == '_foreign_helper' &&
382 e.name == 'JS') { 472 e.name == 'JS') {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 } 505 }
416 } 506 }
417 507
418 // Review note: no longer need to override visitFunctionExpression, this is 508 // Review note: no longer need to override visitFunctionExpression, this is
419 // handled by the analyzer internally. 509 // handled by the analyzer internally.
420 // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result? 510 // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result?
421 // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression 511 // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression
422 // type in a (...) => expr or just the written type? 512 // type in a (...) => expr or just the written type?
423 513
424 } 514 }
425
426 class RestrictedTypeResolverVisitor extends TypeResolverVisitor {
427 RestrictedTypeResolverVisitor(
428 Library library, Source source, TypeProvider typeProvider)
429 : super.con1(library, source, typeProvider);
430
431 static TypeResolverVisitor constructor(
432 Library library, Source source, TypeProvider typeProvider) =>
433 new RestrictedTypeResolverVisitor(library, source, typeProvider);
434
435 @override
436 Object visitVariableDeclaration(VariableDeclaration node) {
437 var res = super.visitVariableDeclaration(node);
438
439 var element = node.element;
440 VariableDeclarationList parent = node.parent;
441 // only infer types if it was left blank
442 if (!element.type.isDynamic || parent.type != null) return res;
443
444 // const fields and top-levels will be inferred from the initializer value
445 // somewhere else.
446 if (parent.isConst) return res;
447
448 // If the type was omitted on a field, we can infer it from a supertype.
449 if (node.element is FieldElement) {
450 var getter = element.getter;
451 var type = searchTypeFor(element.enclosingElement.type, getter);
452 if (type != null && !type.returnType.isDynamic) {
453 var newType = type.returnType;
454 element.type = newType;
455 getter.returnType = newType;
456 if (!element.isFinal) element.setter.parameters[0].type = newType;
457 }
458 }
459 return res;
460 }
461
462 @override
463 Object visitMethodDeclaration(MethodDeclaration node) {
464 var res = super.visitMethodDeclaration(node);
465 var element = node.element;
466 if ((element is MethodElement || element is PropertyAccessorElement) &&
467 element.returnType.isDynamic &&
468 node.returnType == null) {
469 var type = searchTypeFor(element.enclosingElement.type, element);
470 if (type != null && !type.returnType.isDynamic) {
471 element.returnType = type.returnType;
472 }
473 }
474 return res;
475 }
476 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/options.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698