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 library dev_compiler.src.checker.checker; | 5 library dev_compiler.src.checker.checker; |
6 | 6 |
7 import 'package:analyzer/analyzer.dart'; | 7 import 'package:analyzer/analyzer.dart'; |
8 import 'package:analyzer/src/generated/ast.dart'; | 8 import 'package:analyzer/src/generated/ast.dart'; |
9 import 'package:analyzer/src/generated/element.dart'; | 9 import 'package:analyzer/src/generated/element.dart'; |
10 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; | 10 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; |
(...skipping 527 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
538 arg = (arg as NamedExpression).expression; | 538 arg = (arg as NamedExpression).expression; |
539 } | 539 } |
540 checkAssignment(arg, expectedType); | 540 checkAssignment(arg, expectedType); |
541 } | 541 } |
542 | 542 |
543 void checkFunctionApplication( | 543 void checkFunctionApplication( |
544 Expression node, Expression f, ArgumentList list) { | 544 Expression node, Expression f, ArgumentList list) { |
545 if (_rules.isDynamicCall(f)) { | 545 if (_rules.isDynamicCall(f)) { |
546 // If f is Function and this is a method invocation, we should have | 546 // If f is Function and this is a method invocation, we should have |
547 // gotten an analyzer error, so no need to issue another error. | 547 // gotten an analyzer error, so no need to issue another error. |
548 _recordDynamicInvoke(node); | 548 _recordDynamicInvoke(node, f); |
549 } else { | 549 } else { |
550 checkArgumentList(list, _rules.getTypeAsCaller(f)); | 550 checkArgumentList(list, _rules.getTypeAsCaller(f)); |
551 } | 551 } |
552 } | 552 } |
553 | 553 |
554 @override | 554 @override |
555 void visitMethodInvocation(MethodInvocation node) { | 555 visitMethodInvocation(MethodInvocation node) { |
556 checkFunctionApplication(node, node.methodName, node.argumentList); | 556 var target = node.realTarget; |
| 557 if (_rules.isDynamicTarget(target)) { |
| 558 _recordDynamicInvoke(node, target); |
| 559 |
| 560 // Mark the tear-off as being dynamic, too. This lets us distinguish |
| 561 // cases like: |
| 562 // |
| 563 // dynamic d; |
| 564 // d.someMethod(...); // the whole method call must be a dynamic send. |
| 565 // |
| 566 // ... from case like: |
| 567 // |
| 568 // SomeType s; |
| 569 // s.someDynamicField(...); // static get, followed by dynamic call. |
| 570 // |
| 571 // The first case is handled here, the second case is handled below when |
| 572 // we call [checkFunctionApplication]. |
| 573 DynamicInvoke.set(node.methodName, true); |
| 574 } else { |
| 575 checkFunctionApplication(node, node.methodName, node.argumentList); |
| 576 } |
557 node.visitChildren(this); | 577 node.visitChildren(this); |
558 } | 578 } |
559 | 579 |
560 @override | 580 @override |
561 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | 581 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
562 checkFunctionApplication(node, node.function, node.argumentList); | 582 checkFunctionApplication(node, node.function, node.argumentList); |
563 node.visitChildren(this); | 583 node.visitChildren(this); |
564 } | 584 } |
565 | 585 |
566 @override | 586 @override |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
615 } | 635 } |
616 | 636 |
617 @override | 637 @override |
618 void visitReturnStatement(ReturnStatement node) { | 638 void visitReturnStatement(ReturnStatement node) { |
619 _checkReturn(node.expression, node); | 639 _checkReturn(node.expression, node); |
620 node.visitChildren(this); | 640 node.visitChildren(this); |
621 } | 641 } |
622 | 642 |
623 @override | 643 @override |
624 void visitPropertyAccess(PropertyAccess node) { | 644 void visitPropertyAccess(PropertyAccess node) { |
625 if (node.staticType.isDynamic && _rules.isDynamicTarget(node.realTarget)) { | 645 var target = node.realTarget; |
626 _recordDynamicInvoke(node); | 646 if (_rules.isDynamicTarget(target)) { |
| 647 _recordDynamicInvoke(node, target); |
627 } | 648 } |
628 node.visitChildren(this); | 649 node.visitChildren(this); |
629 } | 650 } |
630 | 651 |
631 @override | 652 @override |
632 void visitPrefixedIdentifier(PrefixedIdentifier node) { | 653 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
633 final target = node.prefix; | 654 final target = node.prefix; |
634 if (_rules.isDynamicTarget(target)) { | 655 if (_rules.isDynamicTarget(target)) { |
635 _recordDynamicInvoke(node); | 656 _recordDynamicInvoke(node, target); |
636 } | 657 } |
637 node.visitChildren(this); | 658 node.visitChildren(this); |
638 } | 659 } |
639 | 660 |
640 @override | 661 @override |
641 void visitDefaultFormalParameter(DefaultFormalParameter node) { | 662 void visitDefaultFormalParameter(DefaultFormalParameter node) { |
642 _visitMaybeConst(node, (node) { | 663 _visitMaybeConst(node, (node) { |
643 // Check that defaults have the proper subtype. | 664 // Check that defaults have the proper subtype. |
644 var parameter = node.parameter; | 665 var parameter = node.parameter; |
645 var parameterType = _rules.elementType(parameter.element); | 666 var parameterType = _rules.elementType(parameter.element); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
767 _checkUnary(node); | 788 _checkUnary(node); |
768 node.visitChildren(this); | 789 node.visitChildren(this); |
769 } | 790 } |
770 | 791 |
771 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { | 792 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { |
772 var op = node.operator; | 793 var op = node.operator; |
773 if (op.isUserDefinableOperator || | 794 if (op.isUserDefinableOperator || |
774 op.type == TokenType.PLUS_PLUS || | 795 op.type == TokenType.PLUS_PLUS || |
775 op.type == TokenType.MINUS_MINUS) { | 796 op.type == TokenType.MINUS_MINUS) { |
776 if (_rules.isDynamicTarget(node.operand)) { | 797 if (_rules.isDynamicTarget(node.operand)) { |
777 _recordDynamicInvoke(node); | 798 _recordDynamicInvoke(node, node.operand); |
778 } | 799 } |
779 // For ++ and --, even if it is not dynamic, we still need to check | 800 // For ++ and --, even if it is not dynamic, we still need to check |
780 // that the user defined method accepts an `int` as the RHS. | 801 // that the user defined method accepts an `int` as the RHS. |
781 // We assume Analyzer has done this already. | 802 // We assume Analyzer has done this already. |
782 } | 803 } |
783 } | 804 } |
784 | 805 |
785 @override | 806 @override |
786 void visitBinaryExpression(BinaryExpression node) { | 807 void visitBinaryExpression(BinaryExpression node) { |
787 var op = node.operator; | 808 var op = node.operator; |
788 if (op.isUserDefinableOperator) { | 809 if (op.isUserDefinableOperator) { |
789 if (_rules.isDynamicTarget(node.leftOperand)) { | 810 if (_rules.isDynamicTarget(node.leftOperand)) { |
790 // Dynamic invocation | 811 // Dynamic invocation |
791 // TODO(vsm): Move this logic to the resolver? | 812 // TODO(vsm): Move this logic to the resolver? |
792 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { | 813 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { |
793 _recordDynamicInvoke(node); | 814 _recordDynamicInvoke(node, node.leftOperand); |
794 } | 815 } |
795 } else { | 816 } else { |
796 var element = node.staticElement; | 817 var element = node.staticElement; |
797 // Method invocation. | 818 // Method invocation. |
798 if (element is MethodElement) { | 819 if (element is MethodElement) { |
799 var type = element.type as FunctionType; | 820 var type = element.type as FunctionType; |
800 // Analyzer should enforce number of parameter types, but check in | 821 // Analyzer should enforce number of parameter types, but check in |
801 // case we have erroneous input. | 822 // case we have erroneous input. |
802 if (type.normalParameterTypes.isNotEmpty) { | 823 if (type.normalParameterTypes.isNotEmpty) { |
803 checkArgument(node.rightOperand, type.normalParameterTypes[0]); | 824 checkArgument(node.rightOperand, type.normalParameterTypes[0]); |
(...skipping 20 matching lines...) Expand all Loading... |
824 } | 845 } |
825 | 846 |
826 @override | 847 @override |
827 void visitConditionalExpression(ConditionalExpression node) { | 848 void visitConditionalExpression(ConditionalExpression node) { |
828 checkBoolean(node.condition); | 849 checkBoolean(node.condition); |
829 node.visitChildren(this); | 850 node.visitChildren(this); |
830 } | 851 } |
831 | 852 |
832 @override | 853 @override |
833 void visitIndexExpression(IndexExpression node) { | 854 void visitIndexExpression(IndexExpression node) { |
834 if (_rules.isDynamicTarget(node.target)) { | 855 var target = node.realTarget; |
835 _recordDynamicInvoke(node); | 856 if (_rules.isDynamicTarget(target)) { |
| 857 _recordDynamicInvoke(node, target); |
836 } else { | 858 } else { |
837 var element = node.staticElement; | 859 var element = node.staticElement; |
838 if (element is MethodElement) { | 860 if (element is MethodElement) { |
839 var type = element.type as FunctionType; | 861 var type = element.type as FunctionType; |
840 // Analyzer should enforce number of parameter types, but check in | 862 // Analyzer should enforce number of parameter types, but check in |
841 // case we have erroneous input. | 863 // case we have erroneous input. |
842 if (type.normalParameterTypes.isNotEmpty) { | 864 if (type.normalParameterTypes.isNotEmpty) { |
843 checkArgument(node.index, type.normalParameterTypes[0]); | 865 checkArgument(node.index, type.normalParameterTypes[0]); |
844 } | 866 } |
845 } else { | 867 } else { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
890 } | 912 } |
891 return normalReturnType; | 913 return normalReturnType; |
892 } | 914 } |
893 | 915 |
894 void _checkCompoundAssignment(AssignmentExpression expr) { | 916 void _checkCompoundAssignment(AssignmentExpression expr) { |
895 var op = expr.operator.type; | 917 var op = expr.operator.type; |
896 assert(op.isAssignmentOperator && op != TokenType.EQ); | 918 assert(op.isAssignmentOperator && op != TokenType.EQ); |
897 var methodElement = expr.staticElement; | 919 var methodElement = expr.staticElement; |
898 if (methodElement == null) { | 920 if (methodElement == null) { |
899 // Dynamic invocation | 921 // Dynamic invocation |
900 _recordDynamicInvoke(expr); | 922 _recordDynamicInvoke(expr, expr.leftHandSide); |
901 } else { | 923 } else { |
902 // Sanity check the operator | 924 // Sanity check the operator |
903 assert(methodElement.isOperator); | 925 assert(methodElement.isOperator); |
904 var functionType = methodElement.type; | 926 var functionType = methodElement.type; |
905 var paramTypes = functionType.normalParameterTypes; | 927 var paramTypes = functionType.normalParameterTypes; |
906 assert(paramTypes.length == 1); | 928 assert(paramTypes.length == 1); |
907 assert(functionType.namedParameterTypes.isEmpty); | 929 assert(functionType.namedParameterTypes.isEmpty); |
908 assert(functionType.optionalParameterTypes.isEmpty); | 930 assert(functionType.optionalParameterTypes.isEmpty); |
909 | 931 |
910 // Check the lhs type | 932 // Check the lhs type |
(...skipping 23 matching lines...) Expand all Loading... |
934 // Check the rhs type | 956 // Check the rhs type |
935 if (staticInfo is! CoercionInfo) { | 957 if (staticInfo is! CoercionInfo) { |
936 var paramType = paramTypes.first; | 958 var paramType = paramTypes.first; |
937 staticInfo = _rules.checkAssignment( | 959 staticInfo = _rules.checkAssignment( |
938 expr.rightHandSide, paramType, _constantContext); | 960 expr.rightHandSide, paramType, _constantContext); |
939 _recordMessage(staticInfo); | 961 _recordMessage(staticInfo); |
940 } | 962 } |
941 } | 963 } |
942 } | 964 } |
943 | 965 |
944 void _recordDynamicInvoke(AstNode node) { | 966 void _recordDynamicInvoke(AstNode node, AstNode target) { |
945 _reporter.log(new DynamicInvoke(_rules, node)); | 967 var dinvoke = new DynamicInvoke(_rules, node); |
| 968 _reporter.log(dinvoke); |
| 969 // TODO(jmesserly): we may eventually want to record if the whole operation |
| 970 // (node) was dynamic, rather than the target, but this is an easier fit |
| 971 // with what we used to do. |
| 972 DynamicInvoke.set(target, true); |
946 } | 973 } |
947 | 974 |
948 void _recordMessage(StaticInfo info) { | 975 void _recordMessage(StaticInfo info) { |
949 if (info == null) return; | 976 if (info == null) return; |
950 if (info.level >= logger.Level.SEVERE) _failure = true; | 977 if (info.level >= logger.Level.SEVERE) _failure = true; |
951 _reporter.log(info); | 978 _reporter.log(info); |
952 if (info is CoercionInfo) { | 979 if (info is CoercionInfo) { |
953 assert(CoercionInfo.get(info.node) == null); | 980 assert(CoercionInfo.get(info.node) == null); |
954 CoercionInfo.set(info.node, info); | 981 CoercionInfo.set(info.node, info); |
955 } | 982 } |
956 } | 983 } |
957 } | 984 } |
OLD | NEW |