| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /// Encapsulates how to invoke the analyzer resolver and overrides how it | |
| 6 /// computes types on expressions to use our restricted set of types. | |
| 7 library dev_compiler.src.checker.resolver; | |
| 8 | |
| 9 import 'package:analyzer/analyzer.dart'; | |
| 10 import 'package:analyzer/src/generated/ast.dart'; | |
| 11 import 'package:analyzer/src/generated/element.dart'; | |
| 12 import 'package:analyzer/src/generated/resolver.dart'; | |
| 13 import 'package:analyzer/src/generated/source.dart' show Source; | |
| 14 import 'package:analyzer/src/generated/source_io.dart'; | |
| 15 import 'package:analyzer/src/generated/static_type_analyzer.dart'; | |
| 16 import 'package:analyzer/src/generated/utilities_collection.dart' | |
| 17 show DirectedGraph; | |
| 18 import 'package:logging/logging.dart' as logger; | |
| 19 | |
| 20 import '../../strong_mode.dart' show StrongModeOptions; | |
| 21 import '../utils.dart'; | |
| 22 import 'rules.dart'; | |
| 23 | |
| 24 final _log = new logger.Logger('dev_compiler.src.resolver'); | |
| 25 | |
| 26 /// A [LibraryResolver] that performs inference on top-levels and fields based | |
| 27 /// on the value of the initializer, and on fields and methods based on | |
| 28 /// overridden members in super classes. | |
| 29 class LibraryResolverWithInference extends LibraryResolver { | |
| 30 final StrongModeOptions _options; | |
| 31 | |
| 32 LibraryResolverWithInference(context, this._options) : super(context); | |
| 33 | |
| 34 @override | |
| 35 void resolveReferencesAndTypes() { | |
| 36 _resolveVariableReferences(); | |
| 37 | |
| 38 // Run resolution in two stages, skipping method bodies first, so we can run | |
| 39 // type-inference before we fully analyze methods. | |
| 40 var visitors = _createVisitors(); | |
| 41 _resolveEverything(visitors); | |
| 42 _runInference(visitors); | |
| 43 | |
| 44 visitors.values.forEach((v) => v.skipMethodBodies = false); | |
| 45 _resolveEverything(visitors); | |
| 46 } | |
| 47 | |
| 48 // Note: this was split from _resolveReferencesAndTypesInLibrary so we do it | |
| 49 // only once. | |
| 50 void _resolveVariableReferences() { | |
| 51 for (Library library in resolvedLibraries) { | |
| 52 for (Source source in library.compilationUnitSources) { | |
| 53 library.getAST(source).accept(new VariableResolverVisitor( | |
| 54 library.libraryElement, source, typeProvider, library.errorListener, | |
| 55 nameScope: library.libraryScope)); | |
| 56 } | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 // Note: this was split from _resolveReferencesAndTypesInLibrary so we can do | |
| 61 // resolution in pieces. | |
| 62 Map<Source, RestrictedResolverVisitor> _createVisitors() { | |
| 63 var visitors = <Source, RestrictedResolverVisitor>{}; | |
| 64 for (Library library in resolvedLibraries) { | |
| 65 for (Source source in library.compilationUnitSources) { | |
| 66 var visitor = new RestrictedResolverVisitor( | |
| 67 library, source, typeProvider, _options); | |
| 68 visitors[source] = visitor; | |
| 69 } | |
| 70 } | |
| 71 return visitors; | |
| 72 } | |
| 73 | |
| 74 /// Runs the resolver on the entire library cycle. | |
| 75 void _resolveEverything(Map<Source, RestrictedResolverVisitor> visitors) { | |
| 76 for (Library library in resolvedLibraries) { | |
| 77 for (Source source in library.compilationUnitSources) { | |
| 78 library.getAST(source).accept(visitors[source]); | |
| 79 } | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 _runInference(Map<Source, RestrictedResolverVisitor> visitors) { | |
| 84 var globalsAndStatics = <VariableDeclaration>[]; | |
| 85 var classes = <ClassDeclaration>[]; | |
| 86 | |
| 87 // Extract top-level members that are const, statics, or classes. | |
| 88 for (Library library in resolvedLibraries) { | |
| 89 for (Source source in library.compilationUnitSources) { | |
| 90 CompilationUnit ast = library.getAST(source); | |
| 91 for (var declaration in ast.declarations) { | |
| 92 if (declaration is TopLevelVariableDeclaration) { | |
| 93 globalsAndStatics.addAll(declaration.variables.variables); | |
| 94 } else if (declaration is ClassDeclaration) { | |
| 95 classes.add(declaration); | |
| 96 for (var member in declaration.members) { | |
| 97 if (member is FieldDeclaration && | |
| 98 (member.fields.isConst || member.isStatic)) { | |
| 99 globalsAndStatics.addAll(member.fields.variables); | |
| 100 } | |
| 101 } | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 } | |
| 106 _inferGlobalsAndStatics(globalsAndStatics, visitors); | |
| 107 _inferInstanceFields(classes, visitors); | |
| 108 } | |
| 109 | |
| 110 _inferGlobalsAndStatics(List<VariableDeclaration> globalsAndStatics, | |
| 111 Map<Source, RestrictedResolverVisitor> visitors) { | |
| 112 var elementToDeclaration = {}; | |
| 113 for (var c in globalsAndStatics) { | |
| 114 elementToDeclaration[c.element] = c; | |
| 115 } | |
| 116 var constGraph = new DirectedGraph<VariableDeclaration>(); | |
| 117 globalsAndStatics.forEach(constGraph.addNode); | |
| 118 for (var c in globalsAndStatics) { | |
| 119 for (var e in _VarExtractor.extract(c.initializer)) { | |
| 120 // Note: declaration is null for variables that come from other strongly | |
| 121 // connected components. | |
| 122 var declaration = elementToDeclaration[e]; | |
| 123 if (declaration != null) constGraph.addEdge(c, declaration); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 for (var component in constGraph.computeTopologicalSort()) { | |
| 128 component.forEach((v) => _reanalyzeVar(visitors, v)); | |
| 129 _inferVariableFromInitializer(component); | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 _inferInstanceFields(List<ClassDeclaration> classes, | |
| 134 Map<Source, RestrictedResolverVisitor> visitors) { | |
| 135 // First propagate what was inferred from globals to all instance fields. | |
| 136 | |
| 137 // TODO(sigmund): also do a fine-grain propagation between fields. We want | |
| 138 // infer-by-override to take precedence, so we would have to include | |
| 139 // classes in the dependency graph and ensure that fields depend on their | |
| 140 // class, and classes depend on superclasses. | |
| 141 classes | |
| 142 .expand((c) => c.members.where(_isInstanceField)) | |
| 143 .expand((f) => f.fields.variables) | |
| 144 .forEach((v) => _reanalyzeVar(visitors, v)); | |
| 145 | |
| 146 // Track types in this strongly connected component, ensure we visit | |
| 147 // supertypes before subtypes. | |
| 148 var typeToDeclaration = <InterfaceType, ClassDeclaration>{}; | |
| 149 classes.forEach((c) => typeToDeclaration[c.element.type] = c); | |
| 150 var seen = new Set<InterfaceType>(); | |
| 151 visit(ClassDeclaration cls) { | |
| 152 var element = cls.element; | |
| 153 var type = element.type; | |
| 154 if (seen.contains(type)) return; | |
| 155 seen.add(type); | |
| 156 for (var supertype in element.allSupertypes) { | |
| 157 var supertypeClass = typeToDeclaration[supertype]; | |
| 158 if (supertypeClass != null) visit(supertypeClass); | |
| 159 } | |
| 160 | |
| 161 // Infer field types from overrides first, otherwise from initializers. | |
| 162 var pending = new Set<VariableDeclaration>(); | |
| 163 cls.members | |
| 164 .where(_isInstanceField) | |
| 165 .forEach((f) => _inferFieldTypeFromOverride(f, pending)); | |
| 166 if (pending.isNotEmpty) _inferVariableFromInitializer(pending); | |
| 167 | |
| 168 // Infer return-types and param-types from overrides | |
| 169 cls.members | |
| 170 .where((m) => m is MethodDeclaration && !m.isStatic) | |
| 171 .forEach(_inferMethodTypesFromOverride); | |
| 172 } | |
| 173 classes.forEach(visit); | |
| 174 } | |
| 175 | |
| 176 void _reanalyzeVar(Map<Source, RestrictedResolverVisitor> visitors, | |
| 177 VariableDeclaration variable) { | |
| 178 if (variable.initializer == null) return; | |
| 179 var visitor = visitors[(variable.root as CompilationUnit).element.source]; | |
| 180 visitor.reanalyzeInitializer(variable); | |
| 181 } | |
| 182 | |
| 183 static bool _isInstanceField(f) => | |
| 184 f is FieldDeclaration && !f.isStatic && !f.fields.isConst; | |
| 185 | |
| 186 /// Attempts to infer the type on [field] from overridden fields or getters if | |
| 187 /// a type was not specified. If no type could be inferred, but it contains an | |
| 188 /// initializer, we add it to [pending] so we can try to infer it using the | |
| 189 /// initializer type instead. | |
| 190 void _inferFieldTypeFromOverride( | |
| 191 FieldDeclaration field, Set<VariableDeclaration> pending) { | |
| 192 var variables = field.fields; | |
| 193 for (var variable in variables.variables) { | |
| 194 var varElement = variable.element as FieldElement; | |
| 195 if (!varElement.type.isDynamic || variables.type != null) continue; | |
| 196 var getter = varElement.getter; | |
| 197 // Note: type will be null only when there are no overrides. When some | |
| 198 // override's type was not specified and couldn't be inferred, the type | |
| 199 // here will be dynamic. | |
| 200 var enclosingElement = varElement.enclosingElement; | |
| 201 var type = searchTypeFor(enclosingElement.type, getter); | |
| 202 | |
| 203 // Infer from the RHS when there are no overrides. | |
| 204 if (type == null) { | |
| 205 if (variable.initializer != null) pending.add(variable); | |
| 206 continue; | |
| 207 } | |
| 208 | |
| 209 // When field is final and overridden getter is dynamic, we can infer from | |
| 210 // the RHS without breaking subtyping rules (return type is covariant). | |
| 211 if (type.returnType.isDynamic) { | |
| 212 if (variables.isFinal && variable.initializer != null) { | |
| 213 pending.add(variable); | |
| 214 } | |
| 215 continue; | |
| 216 } | |
| 217 | |
| 218 // Use type from the override. | |
| 219 var newType = type.returnType; | |
| 220 varElement.type = newType; | |
| 221 varElement.getter.returnType = newType; | |
| 222 if (!varElement.isFinal) varElement.setter.parameters[0].type = newType; | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 void _inferMethodTypesFromOverride(MethodDeclaration method) { | |
| 227 var methodElement = method.element; | |
| 228 if (methodElement is! MethodElement && | |
| 229 methodElement is! PropertyAccessorElement) return; | |
| 230 | |
| 231 var enclosingElement = methodElement.enclosingElement as ClassElement; | |
| 232 FunctionType type = null; | |
| 233 | |
| 234 // Infer the return type if omitted | |
| 235 if (methodElement.returnType.isDynamic && method.returnType == null) { | |
| 236 type = searchTypeFor(enclosingElement.type, methodElement); | |
| 237 if (type == null) return; | |
| 238 if (!type.returnType.isDynamic) { | |
| 239 methodElement.returnType = type.returnType; | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 // Infer parameter types if omitted | |
| 244 if (method.parameters == null) return; | |
| 245 var parameters = method.parameters.parameters; | |
| 246 var length = parameters.length; | |
| 247 for (int i = 0; i < length; ++i) { | |
| 248 var parameter = parameters[i]; | |
| 249 if (parameter is DefaultFormalParameter) parameter = parameter.parameter; | |
| 250 if (parameter is SimpleFormalParameter && parameter.type == null) { | |
| 251 type = type ?? searchTypeFor(enclosingElement.type, methodElement); | |
| 252 if (type == null) return; | |
| 253 if (type.parameters.length > i && !type.parameters[i].type.isDynamic) { | |
| 254 parameter.element.type = type.parameters[i].type; | |
| 255 } | |
| 256 } | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 void _inferVariableFromInitializer(Iterable<VariableDeclaration> variables) { | |
| 261 for (var variable in variables) { | |
| 262 var declaration = variable.parent as VariableDeclarationList; | |
| 263 // Only infer on variables that don't have any declared type. | |
| 264 if (declaration.type != null) continue; | |
| 265 var initializer = variable.initializer; | |
| 266 if (initializer == null) continue; | |
| 267 var type = initializer.staticType; | |
| 268 if (type == null || type.isDynamic || type.isBottom) continue; | |
| 269 var element = variable.element as PropertyInducingElement; | |
| 270 // Note: it's ok to update the type here, since initializer.staticType | |
| 271 // is already computed for all declarations in the library cycle. The | |
| 272 // new types will only be propagated on a second run of the | |
| 273 // ResolverVisitor. | |
| 274 element.type = type; | |
| 275 element.getter.returnType = type; | |
| 276 if (!element.isFinal && !element.isConst) { | |
| 277 element.setter.parameters[0].type = type; | |
| 278 } | |
| 279 } | |
| 280 } | |
| 281 } | |
| 282 | |
| 283 /// Extracts the [VariableElement]s used in an initializer expression. | |
| 284 class _VarExtractor extends RecursiveAstVisitor { | |
| 285 final elements = <VariableElement>[]; | |
| 286 visitSimpleIdentifier(SimpleIdentifier node) { | |
| 287 var e = node.staticElement; | |
| 288 if (e is PropertyAccessorElement) elements.add(e.variable); | |
| 289 } | |
| 290 | |
| 291 static List<VariableElement> extract(Expression initializer) { | |
| 292 if (initializer == null) return const []; | |
| 293 var extractor = new _VarExtractor(); | |
| 294 initializer.accept(extractor); | |
| 295 return extractor.elements; | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 /// Overrides the default [ResolverVisitor] to support type inference in | |
| 300 /// [LibraryResolverWithInference] above. | |
| 301 /// | |
| 302 /// Before inference, this visitor is used to resolve top-levels, classes, and | |
| 303 /// fields, but nothing within method bodies. After inference, this visitor is | |
| 304 /// used again to step into method bodies and complete resolution as a second | |
| 305 /// phase. | |
| 306 class RestrictedResolverVisitor extends ResolverVisitor { | |
| 307 final TypeProvider _typeProvider; | |
| 308 | |
| 309 /// Whether to skip resolution within method bodies. | |
| 310 bool skipMethodBodies = true; | |
| 311 | |
| 312 /// State of the resolver at the point a field or variable was declared. | |
| 313 final _stateAtDeclaration = <AstNode, _ResolverState>{}; | |
| 314 | |
| 315 /// Internal tracking of whether a node was skipped while visiting, for | |
| 316 /// example, if it contained a function expression with a function body. | |
| 317 bool _nodeWasSkipped = false; | |
| 318 | |
| 319 /// Internal state, whether we are revisiting an initializer, so we minimize | |
| 320 /// the work being done elsewhere. | |
| 321 bool _revisiting = false; | |
| 322 | |
| 323 /// Initializers that have been visited, reanalyzed, and for which no node was | |
| 324 /// internally skipped. These initializers are fully resolved and don't need | |
| 325 /// to be re-resolved on a sunsequent pass. | |
| 326 final _visitedInitializers = new Set<VariableDeclaration>(); | |
| 327 | |
| 328 RestrictedResolverVisitor(Library library, Source source, | |
| 329 TypeProvider typeProvider, StrongModeOptions options) | |
| 330 : _typeProvider = typeProvider, | |
| 331 super( | |
| 332 library.libraryElement, source, typeProvider, library.errorListener, | |
| 333 nameScope: library.libraryScope, | |
| 334 inheritanceManager: library.inheritanceManager, | |
| 335 typeAnalyzerFactory: RestrictedStaticTypeAnalyzer.constructor); | |
| 336 | |
| 337 reanalyzeInitializer(VariableDeclaration variable) { | |
| 338 try { | |
| 339 _revisiting = true; | |
| 340 _nodeWasSkipped = false; | |
| 341 var node = variable.parent.parent; | |
| 342 var oldState; | |
| 343 var state = _stateAtDeclaration[node]; | |
| 344 if (state != null) { | |
| 345 oldState = new _ResolverState(this); | |
| 346 state.restore(this); | |
| 347 if (node is FieldDeclaration) { | |
| 348 var cls = node.parent as ClassDeclaration; | |
| 349 enclosingClass = cls.element; | |
| 350 } | |
| 351 } | |
| 352 visitNode(variable.initializer); | |
| 353 if (!_nodeWasSkipped) _visitedInitializers.add(variable); | |
| 354 if (oldState != null) oldState.restore(this); | |
| 355 } finally { | |
| 356 _revisiting = false; | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 @override | |
| 361 Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
| 362 _stateAtDeclaration[node] = new _ResolverState(this); | |
| 363 return super.visitTopLevelVariableDeclaration(node); | |
| 364 } | |
| 365 | |
| 366 @override | |
| 367 Object visitFieldDeclaration(FieldDeclaration node) { | |
| 368 _stateAtDeclaration[node] = new _ResolverState(this); | |
| 369 return super.visitFieldDeclaration(node); | |
| 370 } | |
| 371 | |
| 372 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 373 var state = new _ResolverState(this); | |
| 374 try { | |
| 375 if (_revisiting) { | |
| 376 _stateAtDeclaration[node].restore(this); | |
| 377 } else { | |
| 378 _stateAtDeclaration[node] = state; | |
| 379 } | |
| 380 return super.visitVariableDeclaration(node); | |
| 381 } finally { | |
| 382 state.restore(this); | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 @override | |
| 387 Object visitNode(AstNode node) { | |
| 388 if (skipMethodBodies && node is FunctionBody) { | |
| 389 _nodeWasSkipped = true; | |
| 390 return null; | |
| 391 } | |
| 392 if (_visitedInitializers.contains(node)) return null; | |
| 393 assert(node is! Statement || !skipMethodBodies); | |
| 394 return super.visitNode(node); | |
| 395 } | |
| 396 | |
| 397 @override | |
| 398 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 399 if (skipMethodBodies) { | |
| 400 node.accept(elementResolver); | |
| 401 node.accept(typeAnalyzer); | |
| 402 return null; | |
| 403 } else { | |
| 404 return super.visitMethodDeclaration(node); | |
| 405 } | |
| 406 } | |
| 407 | |
| 408 @override | |
| 409 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 410 if (skipMethodBodies) { | |
| 411 node.accept(elementResolver); | |
| 412 node.accept(typeAnalyzer); | |
| 413 return null; | |
| 414 } else { | |
| 415 return super.visitFunctionDeclaration(node); | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 @override | |
| 420 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 421 if (skipMethodBodies) { | |
| 422 node.accept(elementResolver); | |
| 423 node.accept(typeAnalyzer); | |
| 424 return null; | |
| 425 } else { | |
| 426 return super.visitConstructorDeclaration(node); | |
| 427 } | |
| 428 } | |
| 429 | |
| 430 @override | |
| 431 visitFieldFormalParameter(FieldFormalParameter node) { | |
| 432 // Ensure the field formal parameter's type is updated after inference. | |
| 433 // Normally this happens during TypeResolver, but that's before we've done | |
| 434 // inference on the field type. | |
| 435 var element = node.element; | |
| 436 if (element is FieldFormalParameterElement) { | |
| 437 if (element.type.isDynamic) { | |
| 438 // In malformed code, there may be no actual field. | |
| 439 if (element.field != null) { | |
| 440 element.type = element.field.type; | |
| 441 } | |
| 442 } | |
| 443 } | |
| 444 super.visitFieldFormalParameter(node); | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 /// Internal state of the resolver, stored so we can reanalyze portions of the | |
| 449 /// AST quickly, without recomputing everything from the top. | |
| 450 class _ResolverState { | |
| 451 final TypePromotionManager_TypePromoteScope promotionScope; | |
| 452 final TypeOverrideManager_TypeOverrideScope overrideScope; | |
| 453 final Scope nameScope; | |
| 454 | |
| 455 _ResolverState(ResolverVisitor visitor) | |
| 456 : promotionScope = visitor.promoteManager.currentScope, | |
| 457 overrideScope = visitor.overrideManager.currentScope, | |
| 458 nameScope = visitor.nameScope; | |
| 459 | |
| 460 void restore(ResolverVisitor visitor) { | |
| 461 visitor.promoteManager.currentScope = promotionScope; | |
| 462 visitor.overrideManager.currentScope = overrideScope; | |
| 463 visitor.nameScope = nameScope; | |
| 464 } | |
| 465 } | |
| 466 | |
| 467 /// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter | |
| 468 /// in the restricted type system and to infer types for untyped local | |
| 469 /// variables. | |
| 470 class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { | |
| 471 final TypeProvider _typeProvider; | |
| 472 Map<String, DartType> _objectMembers; | |
| 473 | |
| 474 RestrictedStaticTypeAnalyzer(ResolverVisitor r) | |
| 475 : _typeProvider = r.typeProvider, | |
| 476 super(r) { | |
| 477 _objectMembers = getObjectMemberMap(_typeProvider); | |
| 478 } | |
| 479 | |
| 480 static constructor(ResolverVisitor r) => new RestrictedStaticTypeAnalyzer(r); | |
| 481 | |
| 482 @override // to infer type from initializers | |
| 483 visitVariableDeclaration(VariableDeclaration node) { | |
| 484 _inferType(node); | |
| 485 return super.visitVariableDeclaration(node); | |
| 486 } | |
| 487 | |
| 488 /// Infer the type of a variable based on the initializer's type. | |
| 489 void _inferType(VariableDeclaration node) { | |
| 490 var initializer = node.initializer; | |
| 491 if (initializer == null) return; | |
| 492 | |
| 493 var declaredType = (node.parent as VariableDeclarationList).type; | |
| 494 if (declaredType != null) return; | |
| 495 var element = node.element; | |
| 496 if (element is! LocalVariableElement) return; | |
| 497 if (element.type != _typeProvider.dynamicType) return; | |
| 498 | |
| 499 var type = initializer.staticType; | |
| 500 if (type == null || type == _typeProvider.bottomType) return; | |
| 501 element.type = type; | |
| 502 if (element is PropertyInducingElement) { | |
| 503 element.getter.returnType = type; | |
| 504 if (!element.isFinal && !element.isConst) { | |
| 505 element.setter.parameters[0].type = type; | |
| 506 } | |
| 507 } | |
| 508 } | |
| 509 | |
| 510 // TODO(vsm): Use leafp's matchType here? | |
| 511 DartType _findIteratedType(InterfaceType type) { | |
| 512 if (type.element == _typeProvider.iterableType.element) { | |
| 513 var typeArguments = type.typeArguments; | |
| 514 assert(typeArguments.length == 1); | |
| 515 return typeArguments[0]; | |
| 516 } | |
| 517 | |
| 518 if (type == _typeProvider.objectType) return null; | |
| 519 | |
| 520 var result = _findIteratedType(type.superclass); | |
| 521 if (result != null) return result; | |
| 522 | |
| 523 for (final parent in type.interfaces) { | |
| 524 result = _findIteratedType(parent); | |
| 525 if (result != null) return result; | |
| 526 } | |
| 527 | |
| 528 for (final parent in type.mixins) { | |
| 529 result = _findIteratedType(parent); | |
| 530 if (result != null) return result; | |
| 531 } | |
| 532 | |
| 533 return null; | |
| 534 } | |
| 535 | |
| 536 @override | |
| 537 visitDeclaredIdentifier(DeclaredIdentifier node) { | |
| 538 super.visitDeclaredIdentifier(node); | |
| 539 if (node.type != null) return; | |
| 540 | |
| 541 var parent = node.parent as ForEachStatement; | |
| 542 var expr = parent.iterable; | |
| 543 var element = node.element as LocalVariableElementImpl; | |
| 544 var exprType = expr.staticType; | |
| 545 if (exprType is InterfaceType) { | |
| 546 var iteratedType = _findIteratedType(exprType); | |
| 547 if (iteratedType != null) { | |
| 548 element.type = iteratedType; | |
| 549 } | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 bool _isSealed(DartType t) { | |
| 554 return _typeProvider.nonSubtypableTypes.contains(t); | |
| 555 } | |
| 556 | |
| 557 List<List> _genericList = null; | |
| 558 | |
| 559 DartType _matchGeneric(MethodInvocation node, Element element) { | |
| 560 var e = node.methodName.staticElement; | |
| 561 | |
| 562 if (_genericList == null) { | |
| 563 var minmax = (DartType tx, DartType ty) => (tx == ty && | |
| 564 (tx == _typeProvider.intType || tx == _typeProvider.doubleType)) | |
| 565 ? tx | |
| 566 : null; | |
| 567 | |
| 568 var map = (DartType tx) => (tx is FunctionType) | |
| 569 ? _typeProvider.iterableType.substitute4([tx.returnType]) | |
| 570 : null; | |
| 571 | |
| 572 // TODO(vsm): LUB? | |
| 573 var fold = (DartType tx, DartType ty) => | |
| 574 (ty is FunctionType && tx == ty.returnType) ? tx : null; | |
| 575 | |
| 576 // TODO(vsm): Flatten? | |
| 577 var then = (DartType tx) => (tx is FunctionType) | |
| 578 ? _typeProvider.futureType.substitute4([tx.returnType]) | |
| 579 : null; | |
| 580 | |
| 581 var wait = (DartType tx) { | |
| 582 // Iterable<Future<T>> -> Future<List<T>> | |
| 583 var futureType = _findIteratedType(tx); | |
| 584 if (futureType.element.type != _typeProvider.futureType) return null; | |
| 585 var typeArguments = futureType.typeArguments; | |
| 586 if (typeArguments.length != 1) return null; | |
| 587 var baseType = typeArguments[0]; | |
| 588 if (baseType.isDynamic) return null; | |
| 589 return _typeProvider.futureType.substitute4([ | |
| 590 _typeProvider.listType.substitute4([baseType]) | |
| 591 ]); | |
| 592 }; | |
| 593 | |
| 594 _genericList = [ | |
| 595 // Top-level methods | |
| 596 ['dart:math', 'max', 2, minmax], | |
| 597 ['dart:math', 'min', 2, minmax], | |
| 598 // Static methods | |
| 599 [_typeProvider.futureType, 'wait', 1, wait], | |
| 600 // Instance methods | |
| 601 [_typeProvider.iterableDynamicType, 'map', 1, map], | |
| 602 [_typeProvider.iterableDynamicType, 'fold', 2, fold], | |
| 603 [_typeProvider.futureDynamicType, 'then', 1, then], | |
| 604 ]; | |
| 605 } | |
| 606 | |
| 607 var targetType = node.target?.staticType; | |
| 608 var arguments = node.argumentList.arguments; | |
| 609 | |
| 610 for (var generic in _genericList) { | |
| 611 if (e?.name == generic[1]) { | |
| 612 if ((generic[0] is String && | |
| 613 element?.library.source.uri.toString() == generic[0]) || | |
| 614 (generic[0] is DartType && | |
| 615 targetType != null && | |
| 616 targetType.isSubtypeOf(generic[0]))) { | |
| 617 if (arguments.length == generic[2]) { | |
| 618 return Function.apply( | |
| 619 generic[3], arguments.map((arg) => arg.staticType).toList()); | |
| 620 } | |
| 621 } | |
| 622 } | |
| 623 } | |
| 624 | |
| 625 return null; | |
| 626 } | |
| 627 | |
| 628 @override // to propagate types to identifiers | |
| 629 visitMethodInvocation(MethodInvocation node) { | |
| 630 // TODO(jmesserly): we rely on having a staticType propagated to the | |
| 631 // methodName identifier. This shouldn't be necessary for method calls, so | |
| 632 // analyzer doesn't do it by default. Conceptually what we're doing here | |
| 633 // is asking for a tear off. We need this until we can fix #132, and rely | |
| 634 // on `node.staticElement == null` instead of `rules.isDynamicCall(node)`. | |
| 635 visitSimpleIdentifier(node.methodName); | |
| 636 | |
| 637 super.visitMethodInvocation(node); | |
| 638 | |
| 639 // Search for Object methods. | |
| 640 var name = node.methodName.name; | |
| 641 if (node.staticType.isDynamic && | |
| 642 _objectMembers.containsKey(name) && | |
| 643 isDynamicTarget(node.target)) { | |
| 644 var type = _objectMembers[name]; | |
| 645 if (type is FunctionType && | |
| 646 type.parameters.isEmpty && | |
| 647 node.argumentList.arguments.isEmpty) { | |
| 648 node.methodName.staticType = type; | |
| 649 // Only infer the type of the overall expression if we have an exact | |
| 650 // type - e.g., a sealed type. Otherwise, it may be too strict. | |
| 651 if (_isSealed(type.returnType)) { | |
| 652 node.staticType = type.returnType; | |
| 653 } | |
| 654 } | |
| 655 } | |
| 656 | |
| 657 var e = node.methodName.staticElement; | |
| 658 if (isInlineJS(e)) { | |
| 659 // Fix types for JS builtin calls. | |
| 660 // | |
| 661 // This code was taken from analyzer. It's not super sophisticated: | |
| 662 // only looks for the type name in dart:core, so we just copy it here. | |
| 663 // | |
| 664 // TODO(jmesserly): we'll likely need something that can handle a wider | |
| 665 // variety of types, especially when we get to JS interop. | |
| 666 var args = node.argumentList.arguments; | |
| 667 var first = args.isNotEmpty ? args.first : null; | |
| 668 if (first is SimpleStringLiteral) { | |
| 669 var typeStr = first.stringValue; | |
| 670 if (typeStr == '-dynamic') { | |
| 671 node.staticType = _typeProvider.bottomType; | |
| 672 } else { | |
| 673 var coreLib = _typeProvider.objectType.element.library; | |
| 674 var classElem = coreLib.getType(typeStr); | |
| 675 if (classElem != null) { | |
| 676 var type = fillDynamicTypeArgs(classElem.type, _typeProvider); | |
| 677 node.staticType = type; | |
| 678 } | |
| 679 } | |
| 680 } | |
| 681 } | |
| 682 | |
| 683 // Pretend dart:math's min and max are generic: | |
| 684 // | |
| 685 // T min<T extends num>(T x, T y); | |
| 686 // | |
| 687 // and infer T. In practice, this just means if the type of x and y are | |
| 688 // both double or both int, we treat that as the return type. | |
| 689 // | |
| 690 // The Dart spec has similar treatment for binary operations on numbers. | |
| 691 // | |
| 692 // TODO(jmesserly): remove this when we have a fix for | |
| 693 // https://github.com/dart-lang/dev_compiler/issues/28 | |
| 694 var inferred = _matchGeneric(node, e); | |
| 695 // TODO(vsm): If the inferred type is not a subtype, should we use a GLB ins
tead? | |
| 696 if (inferred != null && inferred.isSubtypeOf(node.staticType)) { | |
| 697 node.staticType = inferred; | |
| 698 } | |
| 699 } | |
| 700 | |
| 701 void _inferObjectAccess( | |
| 702 Expression node, Expression target, SimpleIdentifier id) { | |
| 703 // Search for Object accesses. | |
| 704 var name = id.name; | |
| 705 if (node.staticType.isDynamic && | |
| 706 _objectMembers.containsKey(name) && | |
| 707 isDynamicTarget(target)) { | |
| 708 var type = _objectMembers[name]; | |
| 709 id.staticType = type; | |
| 710 // Only infer the type of the overall expression if we have an exact | |
| 711 // type - e.g., a sealed type. Otherwise, it may be too strict. | |
| 712 if (_isSealed(type)) { | |
| 713 node.staticType = type; | |
| 714 } | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 @override | |
| 719 visitPropertyAccess(PropertyAccess node) { | |
| 720 super.visitPropertyAccess(node); | |
| 721 | |
| 722 _inferObjectAccess(node, node.target, node.propertyName); | |
| 723 } | |
| 724 | |
| 725 @override | |
| 726 visitPrefixedIdentifier(PrefixedIdentifier node) { | |
| 727 super.visitPrefixedIdentifier(node); | |
| 728 | |
| 729 _inferObjectAccess(node, node.prefix, node.identifier); | |
| 730 } | |
| 731 | |
| 732 @override | |
| 733 visitConditionalExpression(ConditionalExpression node) { | |
| 734 // TODO(vsm): The static type of a conditional should be the LUB of the | |
| 735 // then and else expressions. The analyzer appears to compute dynamic when | |
| 736 // one or the other is the null literal. Remove this fix once the | |
| 737 // corresponding analyzer bug is fixed: | |
| 738 // https://code.google.com/p/dart/issues/detail?id=22854 | |
| 739 super.visitConditionalExpression(node); | |
| 740 if (node.staticType.isDynamic) { | |
| 741 var thenExpr = node.thenExpression; | |
| 742 var elseExpr = node.elseExpression; | |
| 743 if (thenExpr.staticType.isBottom) { | |
| 744 node.staticType = elseExpr.staticType; | |
| 745 } else if (elseExpr.staticType.isBottom) { | |
| 746 node.staticType = thenExpr.staticType; | |
| 747 } | |
| 748 } | |
| 749 } | |
| 750 | |
| 751 // Review note: no longer need to override visitFunctionExpression, this is | |
| 752 // handled by the analyzer internally. | |
| 753 // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result? | |
| 754 // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression | |
| 755 // type in a (...) => expr or just the written type? | |
| 756 | |
| 757 } | |
| OLD | NEW |