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 /// 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 Loading... | |
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 /// overridden members in super classes. |
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 (note: resolvedLibraries are the | |
127 // libraries in the current strongly connected component). | |
128 if (resolvedLibraries.any((l) => l.librarySource.isInSystemLibrary)) { | |
129 _resolveReferencesAndTypes(false); | |
130 return; | |
131 } | |
132 | |
133 // Run resolution in two stages, skipping method bodies first, so we can run | |
134 // type-inference before we fully analyze methods. | |
135 _resolveReferencesAndTypes(true); | |
136 _runInference(); | |
137 _resolveReferencesAndTypes(false); | |
138 } | |
139 | |
140 // Note: this was split from _resolveReferencesAndTypesInLibrary so we do it | |
141 // only once. | |
142 void _resolveVariableReferences() { | |
143 for (Library library in resolvedLibraries) { | |
144 for (Source source in library.compilationUnitSources) { | |
145 library.getAST(source).accept( | |
146 new VariableResolverVisitor.con1(library, source, typeProvider)); | |
147 } | |
148 } | |
149 } | |
150 | |
151 // Note: this was split from _resolveReferencesAndTypesInLibrary so we can do | |
152 // resolution in pieces. | |
153 void _resolveReferencesAndTypes(bool skipMethods) { | |
154 for (Library library in resolvedLibraries) { | |
155 for (Source source in library.compilationUnitSources) { | |
156 library.getAST(source).accept(new RestrictedResolverVisitor( | |
157 library, source, typeProvider, _options, skipMethods)); | |
158 } | |
159 } | |
160 } | |
161 | |
162 _runInference() { | |
163 var consts = []; | |
164 var statics = []; | |
165 var classes = []; | |
166 | |
167 // Extract top-level members that are const, statics, or classes. | |
168 for (Library library in resolvedLibraries) { | |
169 for (Source source in library.compilationUnitSources) { | |
170 CompilationUnit ast = library.getAST(source); | |
171 for (var declaration in ast.declarations) { | |
172 if (declaration is TopLevelVariableDeclaration) { | |
173 if (declaration.variables.isConst) { | |
174 consts.addAll(declaration.variables.variables); | |
175 } else { | |
176 statics.addAll(declaration.variables.variables); | |
177 } | |
178 } else if (declaration is ClassDeclaration) { | |
179 classes.add(declaration); | |
180 for (var member in declaration.members) { | |
181 if (member is! FieldDeclaration) continue; | |
182 if (member.fields.isConst) { | |
183 consts.addAll(member.fields.variables); | |
184 } else if (member.isStatic) { | |
185 statics.addAll(member.fields.variables); | |
186 } | |
187 } | |
188 } | |
189 } | |
190 } | |
191 } | |
192 | |
193 // TODO(sigmund): consider propagating const types after this layer of | |
194 // inference, so their types can be used to initialize other members below. | |
195 _inferVariableFromInitializer(consts); | |
196 _inferVariableFromInitializer(statics); | |
197 | |
198 // Track types in this strongly connected component, ensure we visit | |
199 // supertypes before subtypes. | |
200 var typeToDeclaration = <InterfaceType, ClassDeclaration>{}; | |
201 classes.forEach((c) => typeToDeclaration[c.element.type] = c); | |
202 var seen = new Set<InterfaceType>(); | |
203 visit(ClassDeclaration cls) { | |
204 var element = cls.element; | |
205 var type = element.type; | |
206 if (seen.contains(type)) return; | |
207 for (var supertype in element.allSupertypes) { | |
208 var supertypeClass = typeToDeclaration[supertype]; | |
209 if (supertypeClass != null) visit(supertypeClass); | |
210 } | |
211 seen.add(type); | |
212 | |
213 _isInstanceField(f) => | |
214 f is FieldDeclaration && !f.isStatic && !f.fields.isConst; | |
215 | |
216 if (_options.inferFromOverrides) { | |
217 // Infer field types from overrides first, otherwise from initializers. | |
218 var pending = new Set<VariableDeclaration>(); | |
219 cls.members | |
220 .where(_isInstanceField) | |
221 .forEach((f) => _inferFieldTypeFromOverride(f, pending)); | |
222 if (pending.isNotEmpty) _inferVariableFromInitializer(pending); | |
223 | |
224 // Infer return-types from overrides | |
225 cls.members | |
226 .where((m) => m is MethodDeclaration && !m.isStatic) | |
227 .forEach(_inferMethodReturnTypeFromOverride); | |
228 } else { | |
229 _inferVariableFromInitializer(cls.members | |
230 .where(_isInstanceField) | |
231 .expand((f) => f.fields.variables)); | |
232 } | |
233 } | |
234 classes.forEach(visit); | |
235 } | |
236 | |
237 /// Attempts to infer the type on [field] from overridden fields or getters if | |
238 /// a type was not specified. If no type could be inferred, but it contains an | |
239 /// initializer, we add it to [pending] so we can try to infer it using the | |
240 /// initializer type instead. | |
241 void _inferFieldTypeFromOverride( | |
242 FieldDeclaration field, Set<VariableDeclaration> pending) { | |
243 var variables = field.fields; | |
244 for (var variable in variables.variables) { | |
245 var varElement = variable.element; | |
246 if (!varElement.type.isDynamic || variables.type != null) continue; | |
247 var getter = varElement.getter; | |
248 var type = searchTypeFor(varElement.enclosingElement.type, getter); | |
249 // Note: type will be null only when there are no overrides. When some | |
250 // override's type was not specified and couldn't be inferred, the type | |
251 // here will be dynamic. | |
252 if (type == null) { | |
253 // Infer from the RHS only if there are no overrides. | |
vsm
2015/03/17 19:51:03
Perhaps we could allow if this is final regardless
Siggi Cherem (dart-lang)
2015/03/17 20:11:10
Good point. Done.
| |
254 if (variable.initializer != null) pending.add(variable); | |
255 } else if (!type.returnType.isDynamic) { | |
256 var newType = type.returnType; | |
257 varElement.type = newType; | |
258 varElement.getter.returnType = newType; | |
259 if (!varElement.isFinal) varElement.setter.parameters[0].type = newType; | |
260 } | |
261 } | |
262 } | |
263 | |
264 void _inferMethodReturnTypeFromOverride(MethodDeclaration method) { | |
265 var methodElement = method.element; | |
266 if ((methodElement is MethodElement || | |
267 methodElement is PropertyAccessorElement) && | |
268 methodElement.returnType.isDynamic && | |
269 method.returnType == null) { | |
270 var type = | |
271 searchTypeFor(methodElement.enclosingElement.type, methodElement); | |
272 if (type != null && !type.returnType.isDynamic) { | |
273 methodElement.returnType = type.returnType; | |
274 } | |
275 } | |
276 } | |
277 | |
278 void _inferVariableFromInitializer(Iterable<VariableDeclaration> variables) { | |
279 for (var variable in variables) { | |
280 var declaration = variable.parent; | |
281 // Only infer on variabless that don't have any declared type. | |
vsm
2015/03/17 19:51:04
nit: variabless to variables
Siggi Cherem (dart-lang)
2015/03/17 20:11:10
Done.
| |
282 if (declaration.type != null) continue; | |
283 if (_options.onlyInferConstsAndFinalFields && | |
284 !declaration.isFinal && | |
285 !declaration.isConst) { | |
286 return; | |
287 } | |
288 var initializer = variable.initializer; | |
289 if (initializer == null) continue; | |
290 var type = initializer.staticType; | |
291 if (type == null || type.isDynamic || type.isBottom) continue; | |
292 if (!_canInferFrom(initializer)) continue; | |
293 var element = variable.element; | |
294 // Note: it's ok to update the type here, since initializer.staticType | |
295 // is already computed for all declarations in the library cycle. The | |
296 // new types will only be propagated on a second run of the | |
297 // ResolverVisitor. | |
298 element.type = type; | |
299 element.getter.returnType = type; | |
300 if (!element.isFinal && !element.isConst) { | |
301 element.setter.parameters[0].type = type; | |
302 } | |
303 } | |
304 } | |
305 | |
306 bool _canInferFrom(Expression expression) { | |
307 if (expression is Literal) return true; | |
308 if (expression is InstanceCreationExpression) return true; | |
309 if (expression is FunctionExpression) return true; | |
310 if (expression is AsExpression) return true; | |
311 if (expression is CascadeExpression) { | |
312 return _canInferFrom(expression.target); | |
313 } | |
314 if (expression is SimpleIdentifier || expression is PropertyAccess) { | |
315 return _options.inferStaticsFromIdentifiers; | |
316 } | |
317 if (expression is PrefixedIdentifier) { | |
318 if (expression.staticElement is PropertyAccessorElement) { | |
319 return _options.inferStaticsFromIdentifiers; | |
320 } | |
321 return _canInferFrom(expression.identifier); | |
322 } | |
323 if (expression is MethodInvocation) { | |
324 return _canInferFrom(expression.target); | |
325 } | |
326 if (expression is BinaryExpression) { | |
327 return _canInferFrom(expression.leftOperand); | |
328 } | |
329 if (expression is ConditionalExpression) { | |
330 return _canInferFrom(expression.thenExpression) && | |
331 _canInferFrom(expression.elseExpression); | |
332 } | |
333 if (expression is PrefixExpression) { | |
334 return _canInferFrom(expression.operand); | |
335 } | |
336 if (expression is PostfixExpression) { | |
337 return _canInferFrom(expression.operand); | |
338 } | |
339 return false; | |
340 } | |
341 } | |
342 | |
343 /// Overrides the default [ResolverVisitor] to support type inference in | |
344 /// [LibraryResolverWithInference] above. | |
345 /// | |
346 /// Before inference, this visitor is used to resolve top-levels, classes, and | |
347 /// fields, but nothing withihn method bodies. After inference, this visitor is | |
348 /// used again to step into method bodies and complete resolution as a second | |
349 /// phase. | |
119 class RestrictedResolverVisitor extends ResolverVisitor { | 350 class RestrictedResolverVisitor extends ResolverVisitor { |
120 final TypeProvider _typeProvider; | 351 final TypeProvider _typeProvider; |
121 | 352 |
353 /// Whether to skip resolution within method bodies. | |
354 final bool skipMethodBodies; | |
355 | |
122 RestrictedResolverVisitor(Library library, Source source, | 356 RestrictedResolverVisitor(Library library, Source source, |
123 TypeProvider typeProvider, ResolverOptions options) | 357 TypeProvider typeProvider, ResolverOptions options, this.skipMethodBodies) |
124 : _typeProvider = typeProvider, | 358 : _typeProvider = typeProvider, |
125 super.con1(library, source, typeProvider, | 359 super.con1(library, source, typeProvider, |
126 typeAnalyzerFactory: RestrictedStaticTypeAnalyzer | 360 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 | 361 |
133 @override | 362 @override |
134 visitCatchClause(CatchClause node) { | 363 visitCatchClause(CatchClause node) { |
135 var stack = node.stackTraceParameter; | 364 var stack = node.stackTraceParameter; |
136 if (stack != null) { | 365 if (stack != null) { |
137 // TODO(jmesserly): analyzer does not correctly associate StackTrace type. | 366 // TODO(jmesserly): analyzer does not correctly associate StackTrace type. |
138 // It happens too late in TypeResolverVisitor visitCatchClause. | 367 // It happens too late in TypeResolverVisitor visitCatchClause. |
139 var element = stack.staticElement; | 368 var element = stack.staticElement; |
140 if (element is VariableElementImpl && element.type == null) { | 369 if (element is VariableElementImpl && element.type == null) { |
141 // From the language spec: | 370 // From the language spec: |
142 // The static type of p1 is T and the static type of p2 is StackTrace. | 371 // The static type of p1 is T and the static type of p2 is StackTrace. |
143 element.type = _typeProvider.stackTraceType; | 372 element.type = _typeProvider.stackTraceType; |
144 } | 373 } |
145 } | 374 } |
146 return super.visitCatchClause(node); | 375 return super.visitCatchClause(node); |
147 } | 376 } |
148 | 377 |
149 @override | 378 @override |
150 Object visitCompilationUnit(CompilationUnit node) { | 379 Object visitNode(AstNode node) { |
151 // Similar to the definition in ResolverVisitor.visitCompilationUnit, but | 380 if (skipMethodBodies && |
152 // changed to visit all top-level fields first, then static fields on all | 381 (node is FunctionBody || |
153 // classes, then all top-level functions, then the rest of the classes. | 382 node is FunctionExpression || |
154 RestrictedStaticTypeAnalyzer restrictedAnalyzer = typeAnalyzer_J2DAccessor; | 383 node is FunctionExpressionInvocation || |
155 overrideManager.enterScope(); | 384 node is SuperConstructorInvocation || |
156 try { | 385 node is RedirectingConstructorInvocation || |
157 var thisLib = node.element.enclosingElement; | 386 node is Annotation || |
158 restrictedAnalyzer._isLibraryContainedInSingleUnit.putIfAbsent(thisLib, | 387 node is Comment)) { |
159 () { | 388 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 } | 389 } |
195 node.accept(elementResolver_J2DAccessor); | 390 assert(node is! Statement || !skipMethodBodies); |
196 node.accept(restrictedAnalyzer); | 391 return super.visitNode(node); |
197 return null; | |
198 } | 392 } |
199 | 393 |
200 @override | 394 @override |
201 void visitClassMembersInScope(ClassDeclaration node) { | 395 Object visitMethodDeclaration(MethodDeclaration node) { |
202 safelyVisit(node.documentationComment); | 396 if (skipMethodBodies) { |
203 node.metadata.accept(this); | 397 node.accept(elementResolver_J2DAccessor); |
398 node.accept(typeAnalyzer_J2DAccessor); | |
399 return null; | |
400 } else { | |
401 return super.visitMethodDeclaration(node); | |
402 } | |
403 } | |
204 | 404 |
205 // This overrides the default way members are visited so that fields are | 405 @override |
206 // visited before method declarations. | 406 Object visitFunctionDeclaration(FunctionDeclaration node) { |
207 for (var n in node.members) { | 407 if (skipMethodBodies) { |
208 if (n is FieldDeclaration) n.accept(this); | 408 node.accept(elementResolver_J2DAccessor); |
409 node.accept(typeAnalyzer_J2DAccessor); | |
410 return null; | |
411 } else { | |
412 return super.visitFunctionDeclaration(node); | |
209 } | 413 } |
210 for (var n in node.members) { | 414 } |
211 if (n is! FieldDeclaration) n.accept(this); | 415 |
416 @override | |
417 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
418 if (skipMethodBodies) { | |
419 node.accept(elementResolver_J2DAccessor); | |
420 node.accept(typeAnalyzer_J2DAccessor); | |
421 return null; | |
422 } else { | |
423 return super.visitConstructorDeclaration(node); | |
212 } | 424 } |
213 } | 425 } |
214 } | 426 } |
215 | 427 |
216 /// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter | 428 /// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter |
217 /// in the restricted type system and to infer types for untyped local | 429 /// in the restricted type system and to infer types for untyped local |
218 /// variables. | 430 /// variables. |
219 class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { | 431 class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
220 final TypeProvider _typeProvider; | 432 final TypeProvider _typeProvider; |
221 final ResolverOptions _options; | |
222 | 433 |
223 // TODO(sigmund): this needs to go away. This is currently a restriction | 434 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, | 435 : _typeProvider = r.typeProvider, |
231 super(r); | 436 super(r); |
232 | 437 |
233 static constructor(options) => | 438 static constructor(ResolverVisitor r) => new RestrictedStaticTypeAnalyzer(r); |
234 (r) => new RestrictedStaticTypeAnalyzer(r, options); | |
235 | 439 |
236 @override // to infer type from initializers | 440 @override // to infer type from initializers |
237 visitVariableDeclaration(VariableDeclaration node) { | 441 visitVariableDeclaration(VariableDeclaration node) { |
238 _inferType(node); | 442 _inferType(node); |
239 return super.visitVariableDeclaration(node); | 443 return super.visitVariableDeclaration(node); |
240 } | 444 } |
241 | 445 |
242 /// Infer the type of a variable based on the initializer's type. | 446 /// Infer the type of a variable based on the initializer's type. |
243 void _inferType(VariableDeclaration node) { | 447 void _inferType(VariableDeclaration node) { |
244 var initializer = node.initializer; | 448 var initializer = node.initializer; |
245 if (initializer == null) return; | 449 if (initializer == null) return; |
246 | 450 |
247 var declaredType = (node.parent as VariableDeclarationList).type; | 451 var declaredType = (node.parent as VariableDeclarationList).type; |
248 if (declaredType != null) return; | 452 if (declaredType != null) return; |
249 var element = node.element; | 453 var element = node.element; |
454 if (element is! LocalVariableElement) return; | |
250 if (element.type != _typeProvider.dynamicType) return; | 455 if (element.type != _typeProvider.dynamicType) return; |
251 | 456 |
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; | 457 var type = initializer.staticType; |
270 if (type == null || type == _typeProvider.bottomType) return; | 458 if (type == null || type == _typeProvider.bottomType) return; |
271 element.type = type; | 459 element.type = type; |
272 if (element is PropertyInducingElement) { | 460 if (element is PropertyInducingElement) { |
273 element.getter.returnType = type; | 461 element.getter.returnType = type; |
274 if (!element.isFinal && !element.isConst) { | 462 if (!element.isFinal && !element.isConst) { |
275 element.setter.parameters[0].type = type; | 463 element.setter.parameters[0].type = type; |
276 } | 464 } |
277 } | 465 } |
278 } | 466 } |
279 | 467 |
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 | 468 @override // to propagate types to identifiers |
374 visitMethodInvocation(MethodInvocation node) { | 469 visitMethodInvocation(MethodInvocation node) { |
375 // TODO(sigmund): follow up with analyzer team - why is this needed? | 470 // TODO(sigmund): follow up with analyzer team - why is this needed? |
376 visitSimpleIdentifier(node.methodName); | 471 visitSimpleIdentifier(node.methodName); |
377 super.visitMethodInvocation(node); | 472 super.visitMethodInvocation(node); |
378 | 473 |
379 var e = node.methodName.staticElement; | 474 var e = node.methodName.staticElement; |
380 if (e is FunctionElement && | 475 if (e is FunctionElement && |
381 e.library.name == '_foreign_helper' && | 476 e.library.name == '_foreign_helper' && |
382 e.name == 'JS') { | 477 e.name == 'JS') { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
415 } | 510 } |
416 } | 511 } |
417 | 512 |
418 // Review note: no longer need to override visitFunctionExpression, this is | 513 // Review note: no longer need to override visitFunctionExpression, this is |
419 // handled by the analyzer internally. | 514 // handled by the analyzer internally. |
420 // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result? | 515 // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result? |
421 // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression | 516 // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression |
422 // type in a (...) => expr or just the written type? | 517 // type in a (...) => expr or just the written type? |
423 | 518 |
424 } | 519 } |
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 } | |
OLD | NEW |