Chromium Code Reviews| 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 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be | 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be |
| 6 // refactored to fit into analyzer. | 6 // refactored to fit into analyzer. |
| 7 library analyzer.src.task.strong.checker; | 7 library analyzer.src.task.strong.checker; |
| 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/resolver.dart' show TypeProvider; | |
| 12 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; | 13 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; |
| 14 import 'package:analyzer/src/generated/type_system.dart'; | |
| 13 | 15 |
| 14 import 'info.dart'; | 16 import 'info.dart'; |
| 15 import 'rules.dart'; | |
| 16 | 17 |
| 17 /// Checks for overriding declarations of fields and methods. This is used to | 18 /// Checks for overriding declarations of fields and methods. This is used to |
| 18 /// check overrides between classes and superclasses, interfaces, and mixin | 19 /// check overrides between classes and superclasses, interfaces, and mixin |
| 19 /// applications. | 20 /// applications. |
| 20 class _OverrideChecker { | 21 class _OverrideChecker { |
| 21 bool _failure = false; | 22 bool _failure = false; |
| 22 final TypeRules _rules; | 23 final StrongTypeSystemImpl rules; |
| 23 final AnalysisErrorListener _reporter; | 24 final AnalysisErrorListener _reporter; |
| 24 | 25 |
| 25 _OverrideChecker(this._rules, this._reporter); | 26 _OverrideChecker(this.rules, this._reporter); |
| 26 | 27 |
| 27 void check(ClassDeclaration node) { | 28 void check(ClassDeclaration node) { |
| 28 if (node.element.type.isObject) return; | 29 if (node.element.type.isObject) return; |
| 29 _checkSuperOverrides(node); | 30 _checkSuperOverrides(node); |
| 30 _checkMixinApplicationOverrides(node); | 31 _checkMixinApplicationOverrides(node); |
| 31 _checkAllInterfaceOverrides(node); | 32 _checkAllInterfaceOverrides(node); |
| 32 } | 33 } |
| 33 | 34 |
| 34 /// Check overrides from mixin applications themselves. For example, in: | 35 /// Check overrides from mixin applications themselves. For example, in: |
| 35 /// | 36 /// |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 292 /// class A extends B with C implements E { ... } | 293 /// class A extends B with C implements E { ... } |
| 293 /// ^ | 294 /// ^ |
| 294 /// | 295 /// |
| 295 /// When checking for overrides from a type and it's super types, [node] is | 296 /// When checking for overrides from a type and it's super types, [node] is |
| 296 /// the AST node that defines [element]. This is used to determine whether the | 297 /// the AST node that defines [element]. This is used to determine whether the |
| 297 /// type of the element could be inferred from the types in the super classes. | 298 /// type of the element could be inferred from the types in the super classes. |
| 298 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, | 299 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, |
| 299 AstNode node, AstNode errorLocation, bool isSubclass) { | 300 AstNode node, AstNode errorLocation, bool isSubclass) { |
| 300 assert(!element.isStatic); | 301 assert(!element.isStatic); |
| 301 | 302 |
| 302 FunctionType subType = _rules.elementType(element); | 303 FunctionType subType = _elementType(element); |
| 303 // TODO(vsm): Test for generic | 304 // TODO(vsm): Test for generic |
| 304 FunctionType baseType = _getMemberType(type, element); | 305 FunctionType baseType = _getMemberType(type, element); |
| 305 if (baseType == null) return false; | 306 if (baseType == null) return false; |
| 306 | 307 |
| 307 if (isSubclass && element is PropertyAccessorElement) { | 308 if (isSubclass && element is PropertyAccessorElement) { |
| 308 // Disallow any overriding if the base class defines this member | 309 // Disallow any overriding if the base class defines this member |
| 309 // as a field. We effectively treat fields as final / non-virtual. | 310 // as a field. We effectively treat fields as final / non-virtual. |
| 310 PropertyInducingElement field = _getMemberField(type, element); | 311 PropertyInducingElement field = _getMemberField(type, element); |
| 311 if (field != null) { | 312 if (field != null) { |
| 312 _recordMessage(new InvalidFieldOverride( | 313 _recordMessage(new InvalidFieldOverride( |
| 313 errorLocation, element, type, subType, baseType)); | 314 errorLocation, element, type, subType, baseType)); |
| 314 } | 315 } |
| 315 } | 316 } |
| 316 if (!_rules.isAssignable(subType, baseType)) { | 317 if (!rules.isSubtypeOf(subType, baseType)) { |
| 317 // See whether non-assignable cases fit one of our common patterns: | 318 // See whether non-subtype cases fit one of our common patterns: |
| 318 // | 319 // |
| 319 // Common pattern 1: Inferable return type (on getters and methods) | 320 // Common pattern 1: Inferable return type (on getters and methods) |
| 320 // class A { | 321 // class A { |
| 321 // int get foo => ...; | 322 // int get foo => ...; |
| 322 // String toString() { ... } | 323 // String toString() { ... } |
| 323 // } | 324 // } |
| 324 // class B extends A { | 325 // class B extends A { |
| 325 // get foo => e; // no type specified. | 326 // get foo => e; // no type specified. |
| 326 // toString() { ... } // no return type specified. | 327 // toString() { ... } // no return type specified. |
| 327 // } | 328 // } |
| 328 _recordMessage(new InvalidMethodOverride( | 329 _recordMessage(new InvalidMethodOverride( |
| 329 errorLocation, element, type, subType, baseType)); | 330 errorLocation, element, type, subType, baseType)); |
| 330 } | 331 } |
| 331 return true; | 332 return true; |
| 332 } | 333 } |
| 333 | 334 |
| 334 void _recordMessage(StaticInfo info) { | 335 void _recordMessage(StaticInfo info) { |
| 335 if (info == null) return; | 336 if (info == null) return; |
| 336 var error = info.toAnalysisError(); | 337 var error = info.toAnalysisError(); |
| 337 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; | 338 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; |
| 338 _reporter.onError(error); | 339 _reporter.onError(error); |
| 339 } | 340 } |
| 340 } | 341 } |
| 341 | 342 |
| 342 /// Checks the body of functions and properties. | 343 /// Checks the body of functions and properties. |
| 343 class CodeChecker extends RecursiveAstVisitor { | 344 class CodeChecker extends RecursiveAstVisitor { |
| 344 final TypeRules rules; | 345 final StrongTypeSystemImpl rules; |
| 346 final TypeProvider typeProvider; | |
| 345 final AnalysisErrorListener reporter; | 347 final AnalysisErrorListener reporter; |
| 346 final _OverrideChecker _overrideChecker; | 348 final _OverrideChecker _overrideChecker; |
| 347 final bool _hints; | 349 final bool _hints; |
| 348 | 350 |
| 349 bool _failure = false; | 351 bool _failure = false; |
| 350 bool get failure => _failure || _overrideChecker._failure; | 352 bool get failure => _failure || _overrideChecker._failure; |
| 351 | 353 |
| 352 void reset() { | 354 void reset() { |
| 353 _failure = false; | 355 _failure = false; |
| 354 _overrideChecker._failure = false; | 356 _overrideChecker._failure = false; |
| 355 } | 357 } |
| 356 | 358 |
| 357 CodeChecker(TypeRules rules, AnalysisErrorListener reporter, | 359 CodeChecker(this.typeProvider, StrongTypeSystemImpl rules, AnalysisErrorListen er reporter, |
| 358 {bool hints: false}) | 360 {bool hints: false}) |
| 359 : rules = rules, | 361 : rules = rules, |
| 360 reporter = reporter, | 362 reporter = reporter, |
| 361 _hints = hints, | 363 _hints = hints, |
| 362 _overrideChecker = new _OverrideChecker(rules, reporter); | 364 _overrideChecker = new _OverrideChecker(rules, reporter); |
| 363 | 365 |
| 364 @override | 366 @override |
| 365 void visitComment(Comment node) { | 367 void visitComment(Comment node) { |
| 366 // skip, no need to do typechecking inside comments (they may contain | 368 // skip, no need to do typechecking inside comments (they may contain |
| 367 // comment references which would require resolution). | 369 // comment references which would require resolution). |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 396 if (node is SuperConstructorInvocation) { | 398 if (node is SuperConstructorInvocation) { |
| 397 _recordMessage(new InvalidSuperInvocation(node)); | 399 _recordMessage(new InvalidSuperInvocation(node)); |
| 398 } | 400 } |
| 399 } | 401 } |
| 400 } | 402 } |
| 401 | 403 |
| 402 @override | 404 @override |
| 403 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | 405 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 404 var field = node.fieldName; | 406 var field = node.fieldName; |
| 405 var element = field.staticElement; | 407 var element = field.staticElement; |
| 406 DartType staticType = rules.elementType(element); | 408 DartType staticType = _elementType(element); |
| 407 checkAssignment(node.expression, staticType); | 409 checkAssignment(node.expression, staticType); |
| 408 node.visitChildren(this); | 410 node.visitChildren(this); |
| 409 } | 411 } |
| 410 | 412 |
| 411 @override | 413 @override |
| 412 void visitForEachStatement(ForEachStatement node) { | 414 void visitForEachStatement(ForEachStatement node) { |
| 413 // Check that the expression is an Iterable. | 415 // Check that the expression is an Iterable. |
| 414 var expr = node.iterable; | 416 var expr = node.iterable; |
| 415 var iterableType = node.awaitKeyword != null | 417 var iterableType = node.awaitKeyword != null |
| 416 ? rules.provider.streamType | 418 ? typeProvider.streamType |
| 417 : rules.provider.iterableType; | 419 : typeProvider.iterableType; |
| 418 var loopVariable = node.identifier != null | 420 var loopVariable = node.identifier != null |
| 419 ? node.identifier | 421 ? node.identifier |
| 420 : node.loopVariable?.identifier; | 422 : node.loopVariable?.identifier; |
| 421 if (loopVariable != null) { | 423 if (loopVariable != null) { |
| 422 var iteratorType = loopVariable.staticType; | 424 var iteratorType = loopVariable.staticType; |
| 423 var checkedType = iterableType.substitute4([iteratorType]); | 425 var checkedType = iterableType.substitute4([iteratorType]); |
| 424 checkAssignment(expr, checkedType); | 426 checkAssignment(expr, checkedType); |
| 425 } | 427 } |
| 426 node.visitChildren(this); | 428 node.visitChildren(this); |
| 427 } | 429 } |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 456 void visitSwitchStatement(SwitchStatement node) { | 458 void visitSwitchStatement(SwitchStatement node) { |
| 457 // SwitchStatement defines a boolean conversion to check the result of the | 459 // SwitchStatement defines a boolean conversion to check the result of the |
| 458 // case value == the switch value, but in dev_compiler we require a boolean | 460 // case value == the switch value, but in dev_compiler we require a boolean |
| 459 // return type from an overridden == operator (because Object.==), so | 461 // return type from an overridden == operator (because Object.==), so |
| 460 // checking in SwitchStatement shouldn't be necessary. | 462 // checking in SwitchStatement shouldn't be necessary. |
| 461 node.visitChildren(this); | 463 node.visitChildren(this); |
| 462 } | 464 } |
| 463 | 465 |
| 464 @override | 466 @override |
| 465 void visitListLiteral(ListLiteral node) { | 467 void visitListLiteral(ListLiteral node) { |
| 466 var type = rules.provider.dynamicType; | 468 var type = DynamicTypeImpl.instance; |
| 467 if (node.typeArguments != null) { | 469 if (node.typeArguments != null) { |
| 468 var targs = node.typeArguments.arguments; | 470 var targs = node.typeArguments.arguments; |
| 469 if (targs.length > 0) type = targs[0].type; | 471 if (targs.length > 0) type = targs[0].type; |
| 470 } else if (node.staticType is InterfaceType) { | 472 } else if (node.staticType is InterfaceType) { |
| 471 InterfaceType listT = node.staticType; | 473 InterfaceType listT = node.staticType; |
| 472 var targs = listT.typeArguments; | 474 var targs = listT.typeArguments; |
| 473 if (targs != null && targs.length > 0) type = targs[0]; | 475 if (targs != null && targs.length > 0) type = targs[0]; |
| 474 } | 476 } |
| 475 var elements = node.elements; | 477 var elements = node.elements; |
| 476 for (int i = 0; i < elements.length; i++) { | 478 for (int i = 0; i < elements.length; i++) { |
| 477 checkArgument(elements[i], type); | 479 checkArgument(elements[i], type); |
| 478 } | 480 } |
| 479 super.visitListLiteral(node); | 481 super.visitListLiteral(node); |
| 480 } | 482 } |
| 481 | 483 |
| 482 @override | 484 @override |
| 483 void visitMapLiteral(MapLiteral node) { | 485 void visitMapLiteral(MapLiteral node) { |
| 484 var ktype = rules.provider.dynamicType; | 486 var ktype = DynamicTypeImpl.instance; |
| 485 var vtype = rules.provider.dynamicType; | 487 var vtype = DynamicTypeImpl.instance; |
| 486 if (node.typeArguments != null) { | 488 if (node.typeArguments != null) { |
| 487 var targs = node.typeArguments.arguments; | 489 var targs = node.typeArguments.arguments; |
| 488 if (targs.length > 0) ktype = targs[0].type; | 490 if (targs.length > 0) ktype = targs[0].type; |
| 489 if (targs.length > 1) vtype = targs[1].type; | 491 if (targs.length > 1) vtype = targs[1].type; |
| 490 } else if (node.staticType is InterfaceType) { | 492 } else if (node.staticType is InterfaceType) { |
| 491 InterfaceType mapT = node.staticType; | 493 InterfaceType mapT = node.staticType; |
| 492 var targs = mapT.typeArguments; | 494 var targs = mapT.typeArguments; |
| 493 if (targs != null) { | 495 if (targs != null) { |
| 494 if (targs.length > 0) ktype = targs[0]; | 496 if (targs.length > 0) ktype = targs[0]; |
| 495 if (targs.length > 1) vtype = targs[1]; | 497 if (targs.length > 1) vtype = targs[1]; |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 514 if (element == null) { | 516 if (element == null) { |
| 515 if (type.parameters.length < len) { | 517 if (type.parameters.length < len) { |
| 516 // We found an argument mismatch, the analyzer will report this too, | 518 // We found an argument mismatch, the analyzer will report this too, |
| 517 // so no need to insert an error for this here. | 519 // so no need to insert an error for this here. |
| 518 continue; | 520 continue; |
| 519 } | 521 } |
| 520 element = type.parameters[i]; | 522 element = type.parameters[i]; |
| 521 // TODO(vsm): When can this happen? | 523 // TODO(vsm): When can this happen? |
| 522 assert(element != null); | 524 assert(element != null); |
| 523 } | 525 } |
| 524 DartType expectedType = rules.elementType(element); | 526 DartType expectedType = _elementType(element); |
| 525 if (expectedType == null) expectedType = rules.provider.dynamicType; | 527 if (expectedType == null) expectedType = DynamicTypeImpl.instance; |
| 526 checkArgument(arg, expectedType); | 528 checkArgument(arg, expectedType); |
| 527 } | 529 } |
| 528 } | 530 } |
| 529 | 531 |
| 530 void checkArgument(Expression arg, DartType expectedType) { | 532 void checkArgument(Expression arg, DartType expectedType) { |
| 531 // Preserve named argument structure, so their immediate parent is the | 533 // Preserve named argument structure, so their immediate parent is the |
| 532 // method invocation. | 534 // method invocation. |
| 533 if (arg is NamedExpression) { | 535 if (arg is NamedExpression) { |
| 534 arg = (arg as NamedExpression).expression; | 536 arg = (arg as NamedExpression).expression; |
| 535 } | 537 } |
| 536 checkAssignment(arg, expectedType); | 538 checkAssignment(arg, expectedType); |
| 537 } | 539 } |
| 538 | 540 |
| 539 void checkFunctionApplication( | 541 void checkFunctionApplication( |
| 540 Expression node, Expression f, ArgumentList list) { | 542 Expression node, Expression f, ArgumentList list) { |
| 541 if (rules.isDynamicCall(f)) { | 543 if (_isDynamicCall(f)) { |
| 542 // If f is Function and this is a method invocation, we should have | 544 // If f is Function and this is a method invocation, we should have |
| 543 // gotten an analyzer error, so no need to issue another error. | 545 // gotten an analyzer error, so no need to issue another error. |
| 544 _recordDynamicInvoke(node, f); | 546 _recordDynamicInvoke(node, f); |
| 545 } else { | 547 } else { |
| 546 checkArgumentList(list, rules.getTypeAsCaller(f)); | 548 checkArgumentList(list, _getTypeAsCaller(f)); |
| 547 } | 549 } |
| 548 } | 550 } |
| 549 | 551 |
| 550 @override | 552 @override |
| 551 visitMethodInvocation(MethodInvocation node) { | 553 visitMethodInvocation(MethodInvocation node) { |
| 552 var target = node.realTarget; | 554 var target = node.realTarget; |
| 553 if (rules.isDynamicTarget(target) && | 555 if (_isDynamicTarget(target) && |
| 554 !_isObjectMethod(node, node.methodName)) { | 556 !_isObjectMethod(node, node.methodName)) { |
| 555 _recordDynamicInvoke(node, target); | 557 _recordDynamicInvoke(node, target); |
| 556 | 558 |
| 557 // Mark the tear-off as being dynamic, too. This lets us distinguish | 559 // Mark the tear-off as being dynamic, too. This lets us distinguish |
| 558 // cases like: | 560 // cases like: |
| 559 // | 561 // |
| 560 // dynamic d; | 562 // dynamic d; |
| 561 // d.someMethod(...); // the whole method call must be a dynamic send. | 563 // d.someMethod(...); // the whole method call must be a dynamic send. |
| 562 // | 564 // |
| 563 // ... from case like: | 565 // ... from case like: |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 599 if (element != null) { | 601 if (element != null) { |
| 600 var type = node.staticElement.type; | 602 var type = node.staticElement.type; |
| 601 checkArgumentList(node.argumentList, type); | 603 checkArgumentList(node.argumentList, type); |
| 602 } | 604 } |
| 603 node.visitChildren(this); | 605 node.visitChildren(this); |
| 604 } | 606 } |
| 605 | 607 |
| 606 void _checkReturnOrYield(Expression expression, AstNode node, | 608 void _checkReturnOrYield(Expression expression, AstNode node, |
| 607 {bool yieldStar: false}) { | 609 {bool yieldStar: false}) { |
| 608 var body = node.getAncestor((n) => n is FunctionBody); | 610 var body = node.getAncestor((n) => n is FunctionBody); |
| 609 var type = rules.getExpectedReturnType(body, yieldStar: yieldStar); | 611 var type = _getExpectedReturnType(body, yieldStar: yieldStar); |
| 610 if (type == null) { | 612 if (type == null) { |
| 611 // We have a type mismatch: the async/async*/sync* modifier does | 613 // We have a type mismatch: the async/async*/sync* modifier does |
| 612 // not match the return or yield type. We should have already gotten an | 614 // not match the return or yield type. We should have already gotten an |
| 613 // analyzer error in this case. | 615 // analyzer error in this case. |
| 614 return; | 616 return; |
| 615 } | 617 } |
| 616 InterfaceType futureType = rules.provider.futureType; | 618 InterfaceType futureType = typeProvider.futureType; |
| 617 DartType actualType = expression.staticType; | 619 DartType actualType = expression.staticType; |
| 618 if (body.isAsynchronous && | 620 if (body.isAsynchronous && |
| 619 !body.isGenerator && | 621 !body.isGenerator && |
| 620 actualType is InterfaceType && | 622 actualType is InterfaceType && |
| 621 actualType.element == futureType.element) { | 623 actualType.element == futureType.element) { |
| 622 type = futureType.substitute4([type]); | 624 type = futureType.substitute4([type]); |
| 623 } | 625 } |
| 624 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. | 626 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. |
| 625 if (expression != null) checkAssignment(expression, type); | 627 if (expression != null) checkAssignment(expression, type); |
| 626 } | 628 } |
| 627 | 629 |
| 630 /// Gets the expected return type of the given function [body], either from | |
| 631 /// a normal return/yield, or from a yield*. | |
| 632 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | |
| 633 FunctionType functionType; | |
| 634 var parent = body.parent; | |
| 635 if (parent is Declaration) { | |
| 636 functionType = _elementType(parent.element); | |
| 637 } else { | |
| 638 assert(parent is FunctionExpression); | |
| 639 functionType = parent.staticType ?? DynamicTypeImpl.instance; | |
| 640 } | |
| 641 | |
| 642 var type = functionType.returnType; | |
| 643 | |
| 644 InterfaceType expectedType = null; | |
| 645 if (body.isAsynchronous) { | |
| 646 if (body.isGenerator) { | |
| 647 // Stream<T> -> T | |
| 648 expectedType = typeProvider.streamType; | |
| 649 } else { | |
| 650 // Future<T> -> T | |
| 651 // TODO(vsm): Revisit with issue #228. | |
| 652 expectedType = typeProvider.futureType; | |
| 653 } | |
| 654 } else { | |
| 655 if (body.isGenerator) { | |
| 656 // Iterable<T> -> T | |
| 657 expectedType = typeProvider.iterableType; | |
| 658 } else { | |
| 659 // T -> T | |
| 660 return type; | |
| 661 } | |
| 662 } | |
| 663 if (yieldStar) { | |
| 664 if (type.isDynamic) { | |
| 665 // Ensure it's at least a Stream / Iterable. | |
| 666 return expectedType.substitute4([typeProvider.dynamicType]); | |
| 667 } else { | |
| 668 // Analyzer will provide a separate error if expected type | |
| 669 // is not compatible with type. | |
| 670 return type; | |
| 671 } | |
| 672 } | |
| 673 if (type.isDynamic) { | |
| 674 return type; | |
| 675 } else if (type is InterfaceType && type.element == expectedType.element) { | |
| 676 return type.typeArguments[0]; | |
| 677 } else { | |
| 678 // Malformed type - fallback on analyzer error. | |
| 679 return null; | |
| 680 } | |
| 681 } | |
| 682 | |
| 628 @override | 683 @override |
| 629 void visitExpressionFunctionBody(ExpressionFunctionBody node) { | 684 void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 630 _checkReturnOrYield(node.expression, node); | 685 _checkReturnOrYield(node.expression, node); |
| 631 node.visitChildren(this); | 686 node.visitChildren(this); |
| 632 } | 687 } |
| 633 | 688 |
| 634 @override | 689 @override |
| 635 void visitReturnStatement(ReturnStatement node) { | 690 void visitReturnStatement(ReturnStatement node) { |
| 636 _checkReturnOrYield(node.expression, node); | 691 _checkReturnOrYield(node.expression, node); |
| 637 node.visitChildren(this); | 692 node.visitChildren(this); |
| 638 } | 693 } |
| 639 | 694 |
| 640 @override | 695 @override |
| 641 void visitYieldStatement(YieldStatement node) { | 696 void visitYieldStatement(YieldStatement node) { |
| 642 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); | 697 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); |
| 643 node.visitChildren(this); | 698 node.visitChildren(this); |
| 644 } | 699 } |
| 645 | 700 |
| 646 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { | 701 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { |
| 647 if ((rules.isDynamicTarget(target) || field.staticElement == null) && | 702 if ((_isDynamicTarget(target) || field.staticElement == null) && |
| 648 !_isObjectProperty(target, field)) { | 703 !_isObjectProperty(target, field)) { |
| 649 _recordDynamicInvoke(node, target); | 704 _recordDynamicInvoke(node, target); |
| 650 } | 705 } |
| 651 node.visitChildren(this); | 706 node.visitChildren(this); |
| 652 } | 707 } |
| 653 | 708 |
| 654 @override | 709 @override |
| 655 void visitPropertyAccess(PropertyAccess node) { | 710 void visitPropertyAccess(PropertyAccess node) { |
| 656 _checkFieldAccess(node, node.realTarget, node.propertyName); | 711 _checkFieldAccess(node, node.realTarget, node.propertyName); |
| 657 } | 712 } |
| 658 | 713 |
| 659 @override | 714 @override |
| 660 void visitPrefixedIdentifier(PrefixedIdentifier node) { | 715 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 661 _checkFieldAccess(node, node.prefix, node.identifier); | 716 _checkFieldAccess(node, node.prefix, node.identifier); |
| 662 } | 717 } |
| 663 | 718 |
| 664 @override | 719 @override |
| 665 void visitDefaultFormalParameter(DefaultFormalParameter node) { | 720 void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 666 // Check that defaults have the proper subtype. | 721 // Check that defaults have the proper subtype. |
| 667 var parameter = node.parameter; | 722 var parameter = node.parameter; |
| 668 var parameterType = rules.elementType(parameter.element); | 723 var parameterType = _elementType(parameter.element); |
| 669 assert(parameterType != null); | 724 assert(parameterType != null); |
| 670 var defaultValue = node.defaultValue; | 725 var defaultValue = node.defaultValue; |
| 671 if (defaultValue != null) { | 726 if (defaultValue != null) { |
| 672 checkAssignment(defaultValue, parameterType); | 727 checkAssignment(defaultValue, parameterType); |
| 673 } | 728 } |
| 674 | 729 |
| 675 node.visitChildren(this); | 730 node.visitChildren(this); |
| 676 } | 731 } |
| 677 | 732 |
| 678 @override | 733 @override |
| 679 void visitFieldFormalParameter(FieldFormalParameter node) { | 734 void visitFieldFormalParameter(FieldFormalParameter node) { |
| 680 var element = node.element; | 735 var element = node.element; |
| 681 var typeName = node.type; | 736 var typeName = node.type; |
| 682 if (typeName != null) { | 737 if (typeName != null) { |
| 683 var type = rules.elementType(element); | 738 var type = _elementType(element); |
| 684 var fieldElement = | 739 var fieldElement = |
| 685 node.identifier.staticElement as FieldFormalParameterElement; | 740 node.identifier.staticElement as FieldFormalParameterElement; |
| 686 var fieldType = rules.elementType(fieldElement.field); | 741 var fieldType = _elementType(fieldElement.field); |
| 687 if (!rules.isSubTypeOf(type, fieldType)) { | 742 if (!rules.isSubtypeOf(type, fieldType)) { |
| 688 var staticInfo = | 743 var staticInfo = |
| 689 new InvalidParameterDeclaration(rules, node, fieldType); | 744 new InvalidParameterDeclaration(rules, node, fieldType); |
| 690 _recordMessage(staticInfo); | 745 _recordMessage(staticInfo); |
| 691 } | 746 } |
| 692 } | 747 } |
| 693 node.visitChildren(this); | 748 node.visitChildren(this); |
| 694 } | 749 } |
| 695 | 750 |
| 696 @override | 751 @override |
| 697 void visitInstanceCreationExpression(InstanceCreationExpression node) { | 752 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 698 var arguments = node.argumentList; | 753 var arguments = node.argumentList; |
| 699 var element = node.staticElement; | 754 var element = node.staticElement; |
| 700 if (element != null) { | 755 if (element != null) { |
| 701 var type = rules.elementType(node.staticElement); | 756 var type = _elementType(node.staticElement); |
| 702 checkArgumentList(arguments, type); | 757 checkArgumentList(arguments, type); |
| 703 } | 758 } |
| 704 node.visitChildren(this); | 759 node.visitChildren(this); |
| 705 } | 760 } |
| 706 | 761 |
| 707 @override | 762 @override |
| 708 void visitVariableDeclarationList(VariableDeclarationList node) { | 763 void visitVariableDeclarationList(VariableDeclarationList node) { |
| 709 TypeName type = node.type; | 764 TypeName type = node.type; |
| 710 if (type == null) { | 765 if (type == null) { |
| 711 // No checks are needed when the type is var. Although internally the | 766 // No checks are needed when the type is var. Although internally the |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 758 void visitPostfixExpression(PostfixExpression node) { | 813 void visitPostfixExpression(PostfixExpression node) { |
| 759 _checkUnary(node); | 814 _checkUnary(node); |
| 760 node.visitChildren(this); | 815 node.visitChildren(this); |
| 761 } | 816 } |
| 762 | 817 |
| 763 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { | 818 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { |
| 764 var op = node.operator; | 819 var op = node.operator; |
| 765 if (op.isUserDefinableOperator || | 820 if (op.isUserDefinableOperator || |
| 766 op.type == TokenType.PLUS_PLUS || | 821 op.type == TokenType.PLUS_PLUS || |
| 767 op.type == TokenType.MINUS_MINUS) { | 822 op.type == TokenType.MINUS_MINUS) { |
| 768 if (rules.isDynamicTarget(node.operand)) { | 823 if (_isDynamicTarget(node.operand)) { |
| 769 _recordDynamicInvoke(node, node.operand); | 824 _recordDynamicInvoke(node, node.operand); |
| 770 } | 825 } |
| 771 // For ++ and --, even if it is not dynamic, we still need to check | 826 // For ++ and --, even if it is not dynamic, we still need to check |
| 772 // that the user defined method accepts an `int` as the RHS. | 827 // that the user defined method accepts an `int` as the RHS. |
| 773 // We assume Analyzer has done this already. | 828 // We assume Analyzer has done this already. |
| 774 } | 829 } |
| 775 } | 830 } |
| 776 | 831 |
| 777 @override | 832 @override |
| 778 void visitBinaryExpression(BinaryExpression node) { | 833 void visitBinaryExpression(BinaryExpression node) { |
| 779 var op = node.operator; | 834 var op = node.operator; |
| 780 if (op.isUserDefinableOperator) { | 835 if (op.isUserDefinableOperator) { |
| 781 if (rules.isDynamicTarget(node.leftOperand)) { | 836 if (_isDynamicTarget(node.leftOperand)) { |
| 782 // Dynamic invocation | 837 // Dynamic invocation |
| 783 // TODO(vsm): Move this logic to the resolver? | 838 // TODO(vsm): Move this logic to the resolver? |
| 784 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { | 839 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { |
| 785 _recordDynamicInvoke(node, node.leftOperand); | 840 _recordDynamicInvoke(node, node.leftOperand); |
| 786 } | 841 } |
| 787 } else { | 842 } else { |
| 788 var element = node.staticElement; | 843 var element = node.staticElement; |
| 789 // Method invocation. | 844 // Method invocation. |
| 790 if (element is MethodElement) { | 845 if (element is MethodElement) { |
| 791 var type = element.type; | 846 var type = element.type; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 819 | 874 |
| 820 @override | 875 @override |
| 821 void visitConditionalExpression(ConditionalExpression node) { | 876 void visitConditionalExpression(ConditionalExpression node) { |
| 822 checkBoolean(node.condition); | 877 checkBoolean(node.condition); |
| 823 node.visitChildren(this); | 878 node.visitChildren(this); |
| 824 } | 879 } |
| 825 | 880 |
| 826 @override | 881 @override |
| 827 void visitIndexExpression(IndexExpression node) { | 882 void visitIndexExpression(IndexExpression node) { |
| 828 var target = node.realTarget; | 883 var target = node.realTarget; |
| 829 if (rules.isDynamicTarget(target)) { | 884 if (_isDynamicTarget(target)) { |
| 830 _recordDynamicInvoke(node, target); | 885 _recordDynamicInvoke(node, target); |
| 831 } else { | 886 } else { |
| 832 var element = node.staticElement; | 887 var element = node.staticElement; |
| 833 if (element is MethodElement) { | 888 if (element is MethodElement) { |
| 834 var type = element.type; | 889 var type = element.type; |
| 835 // Analyzer should enforce number of parameter types, but check in | 890 // Analyzer should enforce number of parameter types, but check in |
| 836 // case we have erroneous input. | 891 // case we have erroneous input. |
| 837 if (type.normalParameterTypes.isNotEmpty) { | 892 if (type.normalParameterTypes.isNotEmpty) { |
| 838 checkArgument(node.index, type.normalParameterTypes[0]); | 893 checkArgument(node.index, type.normalParameterTypes[0]); |
| 839 } | 894 } |
| 840 } else { | 895 } else { |
| 841 // TODO(vsm): Assert that the analyzer found an error here? | 896 // TODO(vsm): Assert that the analyzer found an error here? |
| 842 } | 897 } |
| 843 } | 898 } |
| 844 node.visitChildren(this); | 899 node.visitChildren(this); |
| 845 } | 900 } |
| 846 | 901 |
| 847 DartType getType(TypeName name) { | 902 DartType getType(TypeName name) { |
| 848 return (name == null) ? rules.provider.dynamicType : name.type; | 903 return (name == null) ? DynamicTypeImpl.instance : name.type; |
| 849 } | 904 } |
| 850 | 905 |
| 851 /// Analyzer checks boolean conversions, but we need to check too, because | 906 /// Analyzer checks boolean conversions, but we need to check too, because |
| 852 /// it uses the default assignability rules that allow `dynamic` and `Object` | 907 /// it uses the default assignability rules that allow `dynamic` and `Object` |
| 853 /// to be assigned to bool with no message. | 908 /// to be assigned to bool with no message. |
| 854 void checkBoolean(Expression expr) => | 909 void checkBoolean(Expression expr) => |
| 855 checkAssignment(expr, rules.provider.boolType); | 910 checkAssignment(expr, typeProvider.boolType); |
| 856 | 911 |
| 857 void checkAssignment(Expression expr, DartType type) { | 912 void checkAssignment(Expression expr, DartType type) { |
| 858 if (expr is ParenthesizedExpression) { | 913 if (expr is ParenthesizedExpression) { |
| 859 checkAssignment(expr.expression, type); | 914 checkAssignment(expr.expression, type); |
| 860 } else { | 915 } else { |
| 861 _recordMessage(rules.checkAssignment(expr, type)); | 916 _recordMessage(_checkAssignment(expr, type)); |
| 862 } | 917 } |
| 863 } | 918 } |
| 864 | 919 |
| 920 StaticInfo _checkAssignment(Expression expr, DartType toT) { | |
| 921 final fromT = expr.staticType ?? DynamicTypeImpl.instance; | |
| 922 final Coercion c = _coerceTo(fromT, toT); | |
| 923 if (c is Identity) return null; | |
| 924 if (c is CoercionError) return new StaticTypeError(rules, expr, toT); | |
| 925 var reason = null; | |
| 926 | |
| 927 var errors = <String>[]; | |
| 928 | |
| 929 // Don't cast top level expressions, only sub-expressions | |
| 930 var ok = _inferExpression(expr, toT, errors, cast: false); | |
| 931 if (ok) return InferredType.create(rules, expr, toT); | |
| 932 reason = (errors.isNotEmpty) ? errors.first : null; | |
| 933 | |
| 934 if (c is Cast) return DownCast.create(rules, expr, c, reason: reason); | |
| 935 assert(false); | |
| 936 return null; | |
| 937 } | |
| 938 | |
| 939 /// Downward inference | |
| 940 bool _inferExpression(Expression e, DartType t, List<String> errors, | |
| 941 {cast: true}) { | |
|
Jennifer Messerly
2015/12/08 01:07:11
I could remove this as well. WDYT?
Leaf
2015/12/08 01:12:11
sgtm
| |
| 942 DartType staticType = e.staticType ?? DynamicTypeImpl.instance; | |
| 943 if (rules.isSubtypeOf(staticType, t)) { | |
| 944 return true; | |
| 945 } | |
| 946 if (cast && staticType.isDynamic) { | |
| 947 return true; | |
| 948 } | |
| 949 errors.add("$e cannot be typed as $t"); | |
| 950 return false; | |
| 951 } | |
| 952 | |
| 953 // Produce a coercion which coerces something of type fromT | |
| 954 // to something of type toT. | |
| 955 // Returns the error coercion if the types cannot be coerced | |
| 956 // according to our current criteria. | |
| 957 Coercion _coerceTo(DartType fromT, DartType toT) { | |
| 958 // We can use anything as void | |
| 959 if (toT.isVoid) return Coercion.identity(toT); | |
| 960 | |
| 961 // fromT <: toT, no coercion needed | |
| 962 if (rules.isSubtypeOf(fromT, toT)) return Coercion.identity(toT); | |
| 963 | |
| 964 // TODO(vsm): We can get rid of the second clause if we disallow | |
| 965 // all sideways casts - see TODO below. | |
| 966 // ------- | |
| 967 // Note: a function type is never assignable to a class per the Dart | |
| 968 // spec - even if it has a compatible call method. We disallow as | |
| 969 // well for consistency. | |
| 970 if ((fromT is FunctionType && rules.getCallMethodType(toT) != null) || | |
| 971 (toT is FunctionType && rules.getCallMethodType(fromT) != null)) { | |
| 972 return Coercion.error(); | |
| 973 } | |
| 974 | |
| 975 // Downcast if toT <: fromT | |
| 976 if (rules.isSubtypeOf(toT, fromT)) return Coercion.cast(fromT, toT); | |
| 977 | |
| 978 // TODO(vsm): Once we have generic methods, we should delete this | |
| 979 // workaround. These sideways casts are always ones we warn about | |
| 980 // - i.e., we think they are likely to fail at runtime. | |
| 981 // ------- | |
| 982 // Downcast if toT <===> fromT | |
| 983 // The intention here is to allow casts that are sideways in the restricted | |
| 984 // type system, but allowed in the regular dart type system, since these | |
| 985 // are likely to succeed. The canonical example is List<dynamic> and | |
| 986 // Iterable<T> for some concrete T (e.g. Object). These are unrelated | |
| 987 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. | |
| 988 if (fromT.isAssignableTo(toT)) { | |
| 989 return Coercion.cast(fromT, toT); | |
| 990 } | |
| 991 | |
| 992 return Coercion.error(); | |
| 993 } | |
| 994 | |
| 865 DartType _specializedBinaryReturnType( | 995 DartType _specializedBinaryReturnType( |
| 866 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { | 996 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { |
| 867 // This special cases binary return types as per 16.26 and 16.27 of the | 997 // This special cases binary return types as per 16.26 and 16.27 of the |
| 868 // Dart language spec. | 998 // Dart language spec. |
| 869 switch (op) { | 999 switch (op) { |
| 870 case TokenType.PLUS: | 1000 case TokenType.PLUS: |
| 871 case TokenType.MINUS: | 1001 case TokenType.MINUS: |
| 872 case TokenType.STAR: | 1002 case TokenType.STAR: |
| 873 case TokenType.TILDE_SLASH: | 1003 case TokenType.TILDE_SLASH: |
| 874 case TokenType.PERCENT: | 1004 case TokenType.PERCENT: |
| 875 case TokenType.PLUS_EQ: | 1005 case TokenType.PLUS_EQ: |
| 876 case TokenType.MINUS_EQ: | 1006 case TokenType.MINUS_EQ: |
| 877 case TokenType.STAR_EQ: | 1007 case TokenType.STAR_EQ: |
| 878 case TokenType.TILDE_SLASH_EQ: | 1008 case TokenType.TILDE_SLASH_EQ: |
| 879 case TokenType.PERCENT_EQ: | 1009 case TokenType.PERCENT_EQ: |
| 880 if (t1 == rules.provider.intType && | 1010 if (t1 == typeProvider.intType && |
| 881 t2 == rules.provider.intType) return t1; | 1011 t2 == typeProvider.intType) return t1; |
| 882 if (t1 == rules.provider.doubleType && | 1012 if (t1 == typeProvider.doubleType && |
| 883 t2 == rules.provider.doubleType) return t1; | 1013 t2 == typeProvider.doubleType) return t1; |
| 884 // This particular combo is not spelled out in the spec, but all | 1014 // This particular combo is not spelled out in the spec, but all |
| 885 // implementations and analyzer seem to follow this. | 1015 // implementations and analyzer seem to follow this. |
| 886 if (t1 == rules.provider.doubleType && | 1016 if (t1 == typeProvider.doubleType && |
| 887 t2 == rules.provider.intType) return t1; | 1017 t2 == typeProvider.intType) return t1; |
| 888 } | 1018 } |
| 889 return normalReturnType; | 1019 return normalReturnType; |
| 890 } | 1020 } |
| 891 | 1021 |
| 892 void _checkCompoundAssignment(AssignmentExpression expr) { | 1022 void _checkCompoundAssignment(AssignmentExpression expr) { |
| 893 var op = expr.operator.type; | 1023 var op = expr.operator.type; |
| 894 assert(op.isAssignmentOperator && op != TokenType.EQ); | 1024 assert(op.isAssignmentOperator && op != TokenType.EQ); |
| 895 var methodElement = expr.staticElement; | 1025 var methodElement = expr.staticElement; |
| 896 if (methodElement == null) { | 1026 if (methodElement == null) { |
| 897 // Dynamic invocation | 1027 // Dynamic invocation |
| 898 _recordDynamicInvoke(expr, expr.leftHandSide); | 1028 _recordDynamicInvoke(expr, expr.leftHandSide); |
| 899 } else { | 1029 } else { |
| 900 // Sanity check the operator | 1030 // Sanity check the operator |
| 901 assert(methodElement.isOperator); | 1031 assert(methodElement.isOperator); |
| 902 var functionType = methodElement.type; | 1032 var functionType = methodElement.type; |
| 903 var paramTypes = functionType.normalParameterTypes; | 1033 var paramTypes = functionType.normalParameterTypes; |
| 904 assert(paramTypes.length == 1); | 1034 assert(paramTypes.length == 1); |
| 905 assert(functionType.namedParameterTypes.isEmpty); | 1035 assert(functionType.namedParameterTypes.isEmpty); |
| 906 assert(functionType.optionalParameterTypes.isEmpty); | 1036 assert(functionType.optionalParameterTypes.isEmpty); |
| 907 | 1037 |
| 908 // Check the lhs type | 1038 // Check the lhs type |
| 909 var staticInfo; | 1039 var staticInfo; |
| 910 var rhsType = _getStaticType(expr.rightHandSide); | 1040 var rhsType = _getStaticType(expr.rightHandSide); |
| 911 var lhsType = _getStaticType(expr.leftHandSide); | 1041 var lhsType = _getStaticType(expr.leftHandSide); |
| 912 var returnType = _specializedBinaryReturnType( | 1042 var returnType = _specializedBinaryReturnType( |
| 913 op, lhsType, rhsType, functionType.returnType); | 1043 op, lhsType, rhsType, functionType.returnType); |
| 914 | 1044 |
| 915 if (!rules.isSubTypeOf(returnType, lhsType)) { | 1045 if (!rules.isSubtypeOf(returnType, lhsType)) { |
| 916 final numType = rules.provider.numType; | 1046 final numType = typeProvider.numType; |
| 917 // Try to fix up the numerical case if possible. | 1047 // Try to fix up the numerical case if possible. |
| 918 if (rules.isSubTypeOf(lhsType, numType) && | 1048 if (rules.isSubtypeOf(lhsType, numType) && |
| 919 rules.isSubTypeOf(lhsType, rhsType)) { | 1049 rules.isSubtypeOf(lhsType, rhsType)) { |
| 920 // This is also slightly different from spec, but allows us to keep | 1050 // This is also slightly different from spec, but allows us to keep |
| 921 // compound operators in the int += num and num += dynamic cases. | 1051 // compound operators in the int += num and num += dynamic cases. |
| 922 staticInfo = DownCast.create( | 1052 staticInfo = DownCast.create( |
| 923 rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType)); | 1053 rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType)); |
| 924 rhsType = lhsType; | 1054 rhsType = lhsType; |
| 925 } else { | 1055 } else { |
| 926 // Static type error | 1056 // Static type error |
| 927 staticInfo = new StaticTypeError(rules, expr, lhsType); | 1057 staticInfo = new StaticTypeError(rules, expr, lhsType); |
| 928 } | 1058 } |
| 929 _recordMessage(staticInfo); | 1059 _recordMessage(staticInfo); |
| 930 } | 1060 } |
| 931 | 1061 |
| 932 // Check the rhs type | 1062 // Check the rhs type |
| 933 if (staticInfo is! CoercionInfo) { | 1063 if (staticInfo is! CoercionInfo) { |
| 934 var paramType = paramTypes.first; | 1064 var paramType = paramTypes.first; |
| 935 staticInfo = rules.checkAssignment(expr.rightHandSide, paramType); | 1065 staticInfo = _checkAssignment(expr.rightHandSide, paramType); |
| 936 _recordMessage(staticInfo); | 1066 _recordMessage(staticInfo); |
| 937 } | 1067 } |
| 938 } | 1068 } |
| 939 } | 1069 } |
| 940 | 1070 |
| 941 bool _isObjectGetter(Expression target, SimpleIdentifier id) { | 1071 bool _isObjectGetter(Expression target, SimpleIdentifier id) { |
| 942 PropertyAccessorElement element = | 1072 PropertyAccessorElement element = |
| 943 rules.provider.objectType.element.getGetter(id.name); | 1073 typeProvider.objectType.element.getGetter(id.name); |
| 944 return (element != null && !element.isStatic); | 1074 return (element != null && !element.isStatic); |
| 945 } | 1075 } |
| 946 | 1076 |
| 947 bool _isObjectMethod(Expression target, SimpleIdentifier id) { | 1077 bool _isObjectMethod(Expression target, SimpleIdentifier id) { |
| 948 MethodElement element = | 1078 MethodElement element = |
| 949 rules.provider.objectType.element.getMethod(id.name); | 1079 typeProvider.objectType.element.getMethod(id.name); |
| 950 return (element != null && !element.isStatic); | 1080 return (element != null && !element.isStatic); |
| 951 } | 1081 } |
| 952 | 1082 |
| 953 bool _isObjectProperty(Expression target, SimpleIdentifier id) { | 1083 bool _isObjectProperty(Expression target, SimpleIdentifier id) { |
| 954 return _isObjectGetter(target, id) || _isObjectMethod(target, id); | 1084 return _isObjectGetter(target, id) || _isObjectMethod(target, id); |
| 955 } | 1085 } |
| 956 | 1086 |
| 957 DartType _getStaticType(Expression expr) { | 1087 DartType _getStaticType(Expression expr) { |
| 958 return expr.staticType ?? rules.provider.dynamicType; | 1088 return expr.staticType ?? DynamicTypeImpl.instance; |
| 959 } | 1089 } |
| 960 | 1090 |
| 961 void _recordDynamicInvoke(AstNode node, AstNode target) { | 1091 void _recordDynamicInvoke(AstNode node, AstNode target) { |
| 962 if (_hints) { | 1092 if (_hints) { |
| 963 reporter.onError(new DynamicInvoke(rules, node).toAnalysisError()); | 1093 reporter.onError(new DynamicInvoke(rules, node).toAnalysisError()); |
| 964 } | 1094 } |
| 965 // TODO(jmesserly): we may eventually want to record if the whole operation | 1095 // TODO(jmesserly): we may eventually want to record if the whole operation |
| 966 // (node) was dynamic, rather than the target, but this is an easier fit | 1096 // (node) was dynamic, rather than the target, but this is an easier fit |
| 967 // with what we used to do. | 1097 // with what we used to do. |
| 968 DynamicInvoke.set(target, true); | 1098 DynamicInvoke.set(target, true); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 979 | 1109 |
| 980 if (info is CoercionInfo) { | 1110 if (info is CoercionInfo) { |
| 981 // TODO(jmesserly): if we're run again on the same AST, we'll produce the | 1111 // TODO(jmesserly): if we're run again on the same AST, we'll produce the |
| 982 // same annotations. This should be harmless. This might go away once | 1112 // same annotations. This should be harmless. This might go away once |
| 983 // CodeChecker is integrated better with analyzer, as it will know that | 1113 // CodeChecker is integrated better with analyzer, as it will know that |
| 984 // checking has already been performed. | 1114 // checking has already been performed. |
| 985 // assert(CoercionInfo.get(info.node) == null); | 1115 // assert(CoercionInfo.get(info.node) == null); |
| 986 CoercionInfo.set(info.node, info); | 1116 CoercionInfo.set(info.node, info); |
| 987 } | 1117 } |
| 988 } | 1118 } |
| 1119 | |
| 1120 bool _isLibraryPrefix(Expression node) => | |
| 1121 node is SimpleIdentifier && node.staticElement is PrefixElement; | |
| 1122 | |
| 1123 /// Returns `true` if the target expression is dynamic. | |
| 1124 bool _isDynamicTarget(Expression node) { | |
| 1125 if (node == null) return false; | |
| 1126 | |
| 1127 if (_isLibraryPrefix(node)) return false; | |
| 1128 | |
| 1129 // Null type happens when we have unknown identifiers, like a dart: import | |
| 1130 // that doesn't resolve. | |
| 1131 var type = node.staticType; | |
| 1132 return type == null || type.isDynamic; | |
| 1133 } | |
| 1134 | |
| 1135 /// Returns `true` if the expression is a dynamic function call or method | |
| 1136 /// invocation. | |
| 1137 bool _isDynamicCall(Expression call) { | |
| 1138 var ft = _getTypeAsCaller(call); | |
| 1139 // TODO(leafp): This will currently return true if t is Function | |
| 1140 // This is probably the most correct thing to do for now, since | |
| 1141 // this code is also used by the back end. Maybe revisit at some | |
| 1142 // point? | |
| 1143 if (ft == null) return true; | |
| 1144 // Dynamic as the parameter type is treated as bottom. A function with | |
| 1145 // a dynamic parameter type requires a dynamic call in general. | |
| 1146 // However, as an optimization, if we have an original definition, we know | |
| 1147 // dynamic is reified as Object - in this case a regular call is fine. | |
| 1148 if (call is SimpleIdentifier) { | |
| 1149 var element = call.staticElement; | |
| 1150 if (element is FunctionElement || element is MethodElement) { | |
| 1151 // An original declaration. | |
| 1152 return false; | |
| 1153 } | |
| 1154 } | |
| 1155 | |
| 1156 return rules.anyParameterType(ft, (pt) => pt.isDynamic); | |
| 1157 } | |
| 1158 | |
| 1159 /// Given an expression, return its type assuming it is | |
| 1160 /// in the caller position of a call (that is, accounting | |
| 1161 /// for the possibility of a call method). Returns null | |
| 1162 /// if expression is not statically callable. | |
| 1163 FunctionType _getTypeAsCaller(Expression applicand) { | |
| 1164 var t = applicand.staticType ?? DynamicTypeImpl.instance; | |
| 1165 if (t is InterfaceType) { | |
| 1166 return rules.getCallMethodType(t); | |
| 1167 } | |
| 1168 if (t is FunctionType) return t; | |
| 1169 return null; | |
| 1170 } | |
| 989 } | 1171 } |
| 990 | 1172 |
| 991 // Return the field on type corresponding to member, or null if none | 1173 // Return the field on type corresponding to member, or null if none |
| 992 // exists or the "field" is actually a getter/setter. | 1174 // exists or the "field" is actually a getter/setter. |
| 993 PropertyInducingElement _getMemberField( | 1175 PropertyInducingElement _getMemberField( |
| 994 InterfaceType type, PropertyAccessorElement member) { | 1176 InterfaceType type, PropertyAccessorElement member) { |
| 995 String memberName = member.name; | 1177 String memberName = member.name; |
| 996 PropertyInducingElement field; | 1178 PropertyInducingElement field; |
| 997 if (member.isGetter) { | 1179 if (member.isGetter) { |
| 998 // The subclass member is an explicit getter or a field | 1180 // The subclass member is an explicit getter or a field |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1047 baseMethod = type.getSetter(memberName); | 1229 baseMethod = type.getSetter(memberName); |
| 1048 } else { | 1230 } else { |
| 1049 baseMethod = type.getMethod(memberName); | 1231 baseMethod = type.getMethod(memberName); |
| 1050 } | 1232 } |
| 1051 } catch (e) { | 1233 } catch (e) { |
| 1052 // TODO(sigmund): remove this try-catch block (see issue #48). | 1234 // TODO(sigmund): remove this try-catch block (see issue #48). |
| 1053 } | 1235 } |
| 1054 if (baseMethod == null || baseMethod.isStatic) return null; | 1236 if (baseMethod == null || baseMethod.isStatic) return null; |
| 1055 return baseMethod.type; | 1237 return baseMethod.type; |
| 1056 } | 1238 } |
| 1057 ; | |
| 1058 return f; | 1239 return f; |
| 1059 } | 1240 } |
| 1241 | |
| 1242 | |
| 1243 DartType _elementType(Element e) { | |
| 1244 if (e == null) { | |
| 1245 // Malformed code - just return dynamic. | |
| 1246 return DynamicTypeImpl.instance; | |
| 1247 } | |
| 1248 return (e as dynamic).type; | |
| 1249 } | |
| OLD | NEW |