| 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/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
| 11 import 'package:analyzer/dart/ast/resolution_accessors.dart'; |
| 11 import 'package:analyzer/dart/ast/token.dart' show TokenType; | 12 import 'package:analyzer/dart/ast/token.dart' show TokenType; |
| 12 import 'package:analyzer/dart/ast/token.dart'; | 13 import 'package:analyzer/dart/ast/token.dart'; |
| 13 import 'package:analyzer/dart/ast/visitor.dart'; | 14 import 'package:analyzer/dart/ast/visitor.dart'; |
| 14 import 'package:analyzer/dart/element/element.dart'; | 15 import 'package:analyzer/dart/element/element.dart'; |
| 15 import 'package:analyzer/dart/element/type.dart'; | 16 import 'package:analyzer/dart/element/type.dart'; |
| 16 import 'package:analyzer/src/dart/element/type.dart'; | 17 import 'package:analyzer/src/dart/element/type.dart'; |
| 17 import 'package:analyzer/src/error/codes.dart' show StrongModeCode; | 18 import 'package:analyzer/src/error/codes.dart' show StrongModeCode; |
| 18 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; | 19 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; |
| 19 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 20 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 20 import 'package:analyzer/src/generated/type_system.dart'; | 21 import 'package:analyzer/src/generated/type_system.dart'; |
| (...skipping 574 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 595 } | 596 } |
| 596 | 597 |
| 597 @override | 598 @override |
| 598 void visitPropertyAccess(PropertyAccess node) { | 599 void visitPropertyAccess(PropertyAccess node) { |
| 599 _checkFieldAccess(node, node.realTarget, node.propertyName); | 600 _checkFieldAccess(node, node.realTarget, node.propertyName); |
| 600 } | 601 } |
| 601 | 602 |
| 602 @override | 603 @override |
| 603 void visitRedirectingConstructorInvocation( | 604 void visitRedirectingConstructorInvocation( |
| 604 RedirectingConstructorInvocation node) { | 605 RedirectingConstructorInvocation node) { |
| 605 var type = node.staticElement?.type; | 606 var type = staticElementForConstructorReference(node)?.type; |
| 606 // TODO(leafp): There's a TODO in visitRedirectingConstructorInvocation | 607 // TODO(leafp): There's a TODO in visitRedirectingConstructorInvocation |
| 607 // in the element_resolver to handle the case that the element is null | 608 // in the element_resolver to handle the case that the element is null |
| 608 // and emit an error. In the meantime, just be defensive here. | 609 // and emit an error. In the meantime, just be defensive here. |
| 609 if (type != null) { | 610 if (type != null) { |
| 610 checkArgumentList(node.argumentList, type); | 611 checkArgumentList(node.argumentList, type); |
| 611 } | 612 } |
| 612 node.visitChildren(this); | 613 node.visitChildren(this); |
| 613 } | 614 } |
| 614 | 615 |
| 615 @override | 616 @override |
| 616 void visitReturnStatement(ReturnStatement node) { | 617 void visitReturnStatement(ReturnStatement node) { |
| 617 _checkReturnOrYield(node.expression, node); | 618 _checkReturnOrYield(node.expression, node); |
| 618 node.visitChildren(this); | 619 node.visitChildren(this); |
| 619 } | 620 } |
| 620 | 621 |
| 621 @override | 622 @override |
| 622 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { | 623 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| 623 var element = node.staticElement; | 624 var element = node.staticElement; |
| 624 if (element != null) { | 625 if (element != null) { |
| 625 var type = node.staticElement.type; | 626 var type = staticElementForConstructorReference(node).type; |
| 626 checkArgumentList(node.argumentList, type); | 627 checkArgumentList(node.argumentList, type); |
| 627 } | 628 } |
| 628 node.visitChildren(this); | 629 node.visitChildren(this); |
| 629 } | 630 } |
| 630 | 631 |
| 631 @override | 632 @override |
| 632 void visitSwitchStatement(SwitchStatement node) { | 633 void visitSwitchStatement(SwitchStatement node) { |
| 633 // SwitchStatement defines a boolean conversion to check the result of the | 634 // SwitchStatement defines a boolean conversion to check the result of the |
| 634 // case value == the switch value, but in dev_compiler we require a boolean | 635 // case value == the switch value, but in dev_compiler we require a boolean |
| 635 // return type from an overridden == operator (because Object.==), so | 636 // return type from an overridden == operator (because Object.==), so |
| 636 // checking in SwitchStatement shouldn't be necessary. | 637 // checking in SwitchStatement shouldn't be necessary. |
| 637 node.visitChildren(this); | 638 node.visitChildren(this); |
| 638 } | 639 } |
| 639 | 640 |
| 640 @override | 641 @override |
| 641 Object visitVariableDeclaration(VariableDeclaration node) { | 642 Object visitVariableDeclaration(VariableDeclaration node) { |
| 643 VariableElement variableElement = |
| 644 node == null ? null : elementForVariableDeclaration(node); |
| 642 if (!node.isConst && | 645 if (!node.isConst && |
| 643 !node.isFinal && | 646 !node.isFinal && |
| 644 node.initializer == null && | 647 node.initializer == null && |
| 645 rules.isNonNullableType(node?.element?.type)) { | 648 rules.isNonNullableType(variableElement?.type)) { |
| 646 _recordMessage( | 649 _recordMessage( |
| 647 node, | 650 node, |
| 648 StaticTypeWarningCode.NON_NULLABLE_FIELD_NOT_INITIALIZED, | 651 StaticTypeWarningCode.NON_NULLABLE_FIELD_NOT_INITIALIZED, |
| 649 [node.name, node?.element?.type]); | 652 [node.name, variableElement?.type]); |
| 650 } | 653 } |
| 651 return super.visitVariableDeclaration(node); | 654 return super.visitVariableDeclaration(node); |
| 652 } | 655 } |
| 653 | 656 |
| 654 @override | 657 @override |
| 655 void visitVariableDeclarationList(VariableDeclarationList node) { | 658 void visitVariableDeclarationList(VariableDeclarationList node) { |
| 656 TypeName type = node.type; | 659 TypeName type = node.type; |
| 657 if (type == null) { | 660 if (type == null) { |
| 658 // No checks are needed when the type is var. Although internally the | 661 // No checks are needed when the type is var. Although internally the |
| 659 // typing rules may have inferred a more precise type for the variable | 662 // typing rules may have inferred a more precise type for the variable |
| (...skipping 17 matching lines...) Expand all Loading... |
| 677 | 680 |
| 678 @override | 681 @override |
| 679 void visitYieldStatement(YieldStatement node) { | 682 void visitYieldStatement(YieldStatement node) { |
| 680 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); | 683 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); |
| 681 node.visitChildren(this); | 684 node.visitChildren(this); |
| 682 } | 685 } |
| 683 | 686 |
| 684 void _checkCompoundAssignment(AssignmentExpression expr) { | 687 void _checkCompoundAssignment(AssignmentExpression expr) { |
| 685 var op = expr.operator.type; | 688 var op = expr.operator.type; |
| 686 assert(op.isAssignmentOperator && op != TokenType.EQ); | 689 assert(op.isAssignmentOperator && op != TokenType.EQ); |
| 687 var methodElement = expr.staticElement; | 690 var methodElement = staticElementForMethodReference(expr); |
| 688 if (methodElement == null) { | 691 if (methodElement == null) { |
| 689 // Dynamic invocation. | 692 // Dynamic invocation. |
| 690 _recordDynamicInvoke(expr, expr.leftHandSide); | 693 _recordDynamicInvoke(expr, expr.leftHandSide); |
| 691 } else { | 694 } else { |
| 692 // Sanity check the operator. | 695 // Sanity check the operator. |
| 693 assert(methodElement.isOperator); | 696 assert(methodElement.isOperator); |
| 694 var functionType = methodElement.type; | 697 var functionType = methodElement.type; |
| 695 var paramTypes = functionType.normalParameterTypes; | 698 var paramTypes = functionType.normalParameterTypes; |
| 696 assert(paramTypes.length == 1); | 699 assert(paramTypes.length == 1); |
| 697 assert(functionType.namedParameterTypes.isEmpty); | 700 assert(functionType.namedParameterTypes.isEmpty); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 712 // back to it. So these two implicit casts are equivalent: | 715 // back to it. So these two implicit casts are equivalent: |
| 713 // | 716 // |
| 714 // y = /*implicit cast*/(y + 42); | 717 // y = /*implicit cast*/(y + 42); |
| 715 // /*implicit assignment cast*/y += 42; | 718 // /*implicit assignment cast*/y += 42; |
| 716 // | 719 // |
| 717 _checkImplicitCast(expr.leftHandSide, lhsType, | 720 _checkImplicitCast(expr.leftHandSide, lhsType, |
| 718 from: returnType, opAssign: true); | 721 from: returnType, opAssign: true); |
| 719 } | 722 } |
| 720 } | 723 } |
| 721 | 724 |
| 722 /// Returns true if we need an implicit cast of [expr] from [from] type to | |
| 723 /// [to] type, otherwise returns false. | |
| 724 /// | |
| 725 /// If [from] is omitted, uses the static type of [expr]. | |
| 726 bool _needsImplicitCast(Expression expr, DartType to, {DartType from}) { | |
| 727 from ??= _getDefiniteType(expr); | |
| 728 | |
| 729 if (!_checkNonNullAssignment(expr, to, from)) return false; | |
| 730 | |
| 731 // We can use anything as void. | |
| 732 if (to.isVoid) return false; | |
| 733 | |
| 734 // fromT <: toT, no coercion needed. | |
| 735 if (rules.isSubtypeOf(from, to)) return false; | |
| 736 | |
| 737 // Note: a function type is never assignable to a class per the Dart | |
| 738 // spec - even if it has a compatible call method. We disallow as | |
| 739 // well for consistency. | |
| 740 if (from is FunctionType && rules.getCallMethodType(to) != null) { | |
| 741 return false; | |
| 742 } | |
| 743 | |
| 744 // Downcast if toT <: fromT | |
| 745 if (rules.isSubtypeOf(to, from)) { | |
| 746 return true; | |
| 747 } | |
| 748 | |
| 749 // Anything else is an illegal sideways cast. | |
| 750 // However, these will have been reported already in error_verifier, so we | |
| 751 // don't need to report them again. | |
| 752 return false; | |
| 753 } | |
| 754 | |
| 755 /// Checks if an implicit cast of [expr] from [from] type to [to] type is | |
| 756 /// needed, and if so records it. | |
| 757 /// | |
| 758 /// If [from] is omitted, uses the static type of [expr]. | |
| 759 /// | |
| 760 /// If [expr] does not require an implicit cast because it is not related to | |
| 761 /// [to] or is already a subtype of it, does nothing. | |
| 762 void _checkImplicitCast(Expression expr, DartType to, | |
| 763 {DartType from, bool opAssign: false}) { | |
| 764 from ??= _getDefiniteType(expr); | |
| 765 | |
| 766 if (_needsImplicitCast(expr, to, from: from)) { | |
| 767 _recordImplicitCast(expr, to, from: from, opAssign: opAssign); | |
| 768 } | |
| 769 } | |
| 770 | |
| 771 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { | 725 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { |
| 772 if (field.staticElement == null && | 726 if (field.staticElement == null && |
| 773 !typeProvider.isObjectMember(field.name)) { | 727 !typeProvider.isObjectMember(field.name)) { |
| 774 _recordDynamicInvoke(node, target); | 728 _recordDynamicInvoke(node, target); |
| 775 } | 729 } |
| 776 node.visitChildren(this); | 730 node.visitChildren(this); |
| 777 } | 731 } |
| 778 | 732 |
| 779 /** | 733 /** |
| 780 * Check if the closure [node] is unsafe due to dartbug.com/26947. If so, | 734 * Check if the closure [node] is unsafe due to dartbug.com/26947. If so, |
| 781 * issue a warning. | 735 * issue a warning. |
| 782 * | 736 * |
| 783 * TODO(paulberry): eliminate this once dartbug.com/26947 is fixed. | 737 * TODO(paulberry): eliminate this once dartbug.com/26947 is fixed. |
| 784 */ | 738 */ |
| 785 void _checkForUnsafeBlockClosureInference(FunctionExpression node) { | 739 void _checkForUnsafeBlockClosureInference(FunctionExpression node) { |
| 786 if (node.body is! BlockFunctionBody) { | 740 if (node.body is! BlockFunctionBody) { |
| 787 return; | 741 return; |
| 788 } | 742 } |
| 789 if (node.element.returnType.isDynamic) { | 743 if (elementForFunctionExpression(node).returnType.isDynamic) { |
| 790 return; | 744 return; |
| 791 } | 745 } |
| 792 // Find the enclosing variable declaration whose inferred type might depend | 746 // Find the enclosing variable declaration whose inferred type might depend |
| 793 // on the inferred return type of the block closure (if any). | 747 // on the inferred return type of the block closure (if any). |
| 794 AstNode prevAncestor = node; | 748 AstNode prevAncestor = node; |
| 795 AstNode ancestor = node.parent; | 749 AstNode ancestor = node.parent; |
| 796 while (ancestor != null && ancestor is! VariableDeclaration) { | 750 while (ancestor != null && ancestor is! VariableDeclaration) { |
| 797 if (ancestor is BlockFunctionBody) { | 751 if (ancestor is BlockFunctionBody) { |
| 798 // node is inside another block function body; if that block | 752 // node is inside another block function body; if that block |
| 799 // function body is unsafe, we've already warned about it. | 753 // function body is unsafe, we've already warned about it. |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 868 return; | 822 return; |
| 869 } | 823 } |
| 870 if (declElement.enclosingElement is ExecutableElement) { | 824 if (declElement.enclosingElement is ExecutableElement) { |
| 871 // Variable declaration is inside a function or method, so it's safe. | 825 // Variable declaration is inside a function or method, so it's safe. |
| 872 return; | 826 return; |
| 873 } | 827 } |
| 874 _recordMessage(node, StrongModeCode.UNSAFE_BLOCK_CLOSURE_INFERENCE, | 828 _recordMessage(node, StrongModeCode.UNSAFE_BLOCK_CLOSURE_INFERENCE, |
| 875 [declElement.name]); | 829 [declElement.name]); |
| 876 } | 830 } |
| 877 | 831 |
| 832 /// Checks if an implicit cast of [expr] from [from] type to [to] type is |
| 833 /// needed, and if so records it. |
| 834 /// |
| 835 /// If [from] is omitted, uses the static type of [expr]. |
| 836 /// |
| 837 /// If [expr] does not require an implicit cast because it is not related to |
| 838 /// [to] or is already a subtype of it, does nothing. |
| 839 void _checkImplicitCast(Expression expr, DartType to, |
| 840 {DartType from, bool opAssign: false}) { |
| 841 from ??= _getDefiniteType(expr); |
| 842 |
| 843 if (_needsImplicitCast(expr, to, from: from)) { |
| 844 _recordImplicitCast(expr, to, from: from, opAssign: opAssign); |
| 845 } |
| 846 } |
| 847 |
| 878 /// Checks if the assignment is valid with respect to non-nullable types. | 848 /// Checks if the assignment is valid with respect to non-nullable types. |
| 879 /// Returns `false` if a nullable expression is assigned to a variable of | 849 /// Returns `false` if a nullable expression is assigned to a variable of |
| 880 /// non-nullable type and `true` otherwise. | 850 /// non-nullable type and `true` otherwise. |
| 881 bool _checkNonNullAssignment( | 851 bool _checkNonNullAssignment( |
| 882 Expression expression, DartType to, DartType from) { | 852 Expression expression, DartType to, DartType from) { |
| 883 if (rules.isNonNullableType(to) && rules.isNullableType(from)) { | 853 if (rules.isNonNullableType(to) && rules.isNullableType(from)) { |
| 884 _recordMessage( | 854 _recordMessage( |
| 885 expression, StaticTypeWarningCode.INVALID_ASSIGNMENT, [from, to]); | 855 expression, StaticTypeWarningCode.INVALID_ASSIGNMENT, [from, to]); |
| 886 return false; | 856 return false; |
| 887 } | 857 } |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1027 // Dynamic as the parameter type is treated as bottom. A function with | 997 // Dynamic as the parameter type is treated as bottom. A function with |
| 1028 // a dynamic parameter type requires a dynamic call in general. | 998 // a dynamic parameter type requires a dynamic call in general. |
| 1029 // However, as an optimization, if we have an original definition, we know | 999 // However, as an optimization, if we have an original definition, we know |
| 1030 // dynamic is reified as Object - in this case a regular call is fine. | 1000 // dynamic is reified as Object - in this case a regular call is fine. |
| 1031 if (_hasStrictArrow(call.function)) { | 1001 if (_hasStrictArrow(call.function)) { |
| 1032 return false; | 1002 return false; |
| 1033 } | 1003 } |
| 1034 return rules.anyParameterType(ft, (pt) => pt.isDynamic); | 1004 return rules.anyParameterType(ft, (pt) => pt.isDynamic); |
| 1035 } | 1005 } |
| 1036 | 1006 |
| 1007 /// Returns true if we need an implicit cast of [expr] from [from] type to |
| 1008 /// [to] type, otherwise returns false. |
| 1009 /// |
| 1010 /// If [from] is omitted, uses the static type of [expr]. |
| 1011 bool _needsImplicitCast(Expression expr, DartType to, {DartType from}) { |
| 1012 from ??= _getDefiniteType(expr); |
| 1013 |
| 1014 if (!_checkNonNullAssignment(expr, to, from)) return false; |
| 1015 |
| 1016 // We can use anything as void. |
| 1017 if (to.isVoid) return false; |
| 1018 |
| 1019 // fromT <: toT, no coercion needed. |
| 1020 if (rules.isSubtypeOf(from, to)) return false; |
| 1021 |
| 1022 // Note: a function type is never assignable to a class per the Dart |
| 1023 // spec - even if it has a compatible call method. We disallow as |
| 1024 // well for consistency. |
| 1025 if (from is FunctionType && rules.getCallMethodType(to) != null) { |
| 1026 return false; |
| 1027 } |
| 1028 |
| 1029 // Downcast if toT <: fromT |
| 1030 if (rules.isSubtypeOf(to, from)) { |
| 1031 return true; |
| 1032 } |
| 1033 |
| 1034 // Anything else is an illegal sideways cast. |
| 1035 // However, these will have been reported already in error_verifier, so we |
| 1036 // don't need to report them again. |
| 1037 return false; |
| 1038 } |
| 1039 |
| 1037 void _recordDynamicInvoke(AstNode node, Expression target) { | 1040 void _recordDynamicInvoke(AstNode node, Expression target) { |
| 1038 _recordMessage(node, StrongModeCode.DYNAMIC_INVOKE, [node]); | 1041 _recordMessage(node, StrongModeCode.DYNAMIC_INVOKE, [node]); |
| 1039 // TODO(jmesserly): we may eventually want to record if the whole operation | 1042 // TODO(jmesserly): we may eventually want to record if the whole operation |
| 1040 // (node) was dynamic, rather than the target, but this is an easier fit | 1043 // (node) was dynamic, rather than the target, but this is an easier fit |
| 1041 // with what we used to do. | 1044 // with what we used to do. |
| 1042 if (target != null) setIsDynamicInvoke(target, true); | 1045 if (target != null) setIsDynamicInvoke(target, true); |
| 1043 } | 1046 } |
| 1044 | 1047 |
| 1045 /// Records an implicit cast for the [expr] from [from] to [to]. | 1048 /// Records an implicit cast for the [expr] from [from] to [to]. |
| 1046 /// | 1049 /// |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1134 } | 1137 } |
| 1135 | 1138 |
| 1136 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { | 1139 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { |
| 1137 var severity = errorCode.errorSeverity; | 1140 var severity = errorCode.errorSeverity; |
| 1138 if (severity == ErrorSeverity.ERROR) _failure = true; | 1141 if (severity == ErrorSeverity.ERROR) _failure = true; |
| 1139 if (severity != ErrorSeverity.INFO || _options.strongModeHints) { | 1142 if (severity != ErrorSeverity.INFO || _options.strongModeHints) { |
| 1140 int begin = node is AnnotatedNode | 1143 int begin = node is AnnotatedNode |
| 1141 ? node.firstTokenAfterCommentAndMetadata.offset | 1144 ? node.firstTokenAfterCommentAndMetadata.offset |
| 1142 : node.offset; | 1145 : node.offset; |
| 1143 int length = node.end - begin; | 1146 int length = node.end - begin; |
| 1144 var source = (node.root as CompilationUnit).element.source; | 1147 var source = |
| 1148 elementForCompilationUnit(node.root as CompilationUnit).source; |
| 1145 var error = | 1149 var error = |
| 1146 new AnalysisError(source, begin, length, errorCode, arguments); | 1150 new AnalysisError(source, begin, length, errorCode, arguments); |
| 1147 reporter.onError(error); | 1151 reporter.onError(error); |
| 1148 } | 1152 } |
| 1149 } | 1153 } |
| 1150 } | 1154 } |
| 1151 | 1155 |
| 1152 /// Checks for overriding declarations of fields and methods. This is used to | 1156 /// Checks for overriding declarations of fields and methods. This is used to |
| 1153 /// check overrides between classes and superclasses, interfaces, and mixin | 1157 /// check overrides between classes and superclasses, interfaces, and mixin |
| 1154 /// applications. | 1158 /// applications. |
| 1155 class _OverrideChecker { | 1159 class _OverrideChecker { |
| 1156 final StrongTypeSystemImpl rules; | 1160 final StrongTypeSystemImpl rules; |
| 1157 final CodeChecker _checker; | 1161 final CodeChecker _checker; |
| 1158 | 1162 |
| 1159 _OverrideChecker(CodeChecker checker) | 1163 _OverrideChecker(CodeChecker checker) |
| 1160 : _checker = checker, | 1164 : _checker = checker, |
| 1161 rules = checker.rules; | 1165 rules = checker.rules; |
| 1162 | 1166 |
| 1163 void check(ClassDeclaration node) { | 1167 void check(ClassDeclaration node) { |
| 1164 if (node.element.type.isObject) return; | 1168 if (elementForClassDeclaration(node).type.isObject) return; |
| 1165 _checkSuperOverrides(node); | 1169 _checkSuperOverrides(node); |
| 1166 _checkMixinApplicationOverrides(node); | 1170 _checkMixinApplicationOverrides(node); |
| 1167 _checkAllInterfaceOverrides(node); | 1171 _checkAllInterfaceOverrides(node); |
| 1168 } | 1172 } |
| 1169 | 1173 |
| 1170 /// Checks that implementations correctly override all reachable interfaces. | 1174 /// Checks that implementations correctly override all reachable interfaces. |
| 1171 /// In particular, we need to check these overrides for the definitions in | 1175 /// In particular, we need to check these overrides for the definitions in |
| 1172 /// the class itself and each its superclasses. If a superclass is not | 1176 /// the class itself and each its superclasses. If a superclass is not |
| 1173 /// abstract, then we can skip its transitive interfaces. For example, in: | 1177 /// abstract, then we can skip its transitive interfaces. For example, in: |
| 1174 /// | 1178 /// |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1190 if (result.contains(interfaceType)) return; | 1194 if (result.contains(interfaceType)) return; |
| 1191 result.add(interfaceType); | 1195 result.add(interfaceType); |
| 1192 find(interfaceType.superclass, result); | 1196 find(interfaceType.superclass, result); |
| 1193 interfaceType.mixins.forEach((i) => find(i, result)); | 1197 interfaceType.mixins.forEach((i) => find(i, result)); |
| 1194 interfaceType.interfaces.forEach((i) => find(i, result)); | 1198 interfaceType.interfaces.forEach((i) => find(i, result)); |
| 1195 } | 1199 } |
| 1196 | 1200 |
| 1197 // Check all interfaces reachable from the `implements` clause in the | 1201 // Check all interfaces reachable from the `implements` clause in the |
| 1198 // current class against definitions here and in superclasses. | 1202 // current class against definitions here and in superclasses. |
| 1199 var localInterfaces = new Set<InterfaceType>(); | 1203 var localInterfaces = new Set<InterfaceType>(); |
| 1200 var type = node.element.type; | 1204 var type = elementForClassDeclaration(node).type; |
| 1201 type.interfaces.forEach((i) => find(i, localInterfaces)); | 1205 type.interfaces.forEach((i) => find(i, localInterfaces)); |
| 1202 _checkInterfacesOverrides(node, localInterfaces, seen, | 1206 _checkInterfacesOverrides(node, localInterfaces, seen, |
| 1203 includeParents: true); | 1207 includeParents: true); |
| 1204 | 1208 |
| 1205 // Check also how we override locally the interfaces from parent classes if | 1209 // Check also how we override locally the interfaces from parent classes if |
| 1206 // the parent class is abstract. Otherwise, these will be checked as | 1210 // the parent class is abstract. Otherwise, these will be checked as |
| 1207 // overrides on the concrete superclass. | 1211 // overrides on the concrete superclass. |
| 1208 var superInterfaces = new Set<InterfaceType>(); | 1212 var superInterfaces = new Set<InterfaceType>(); |
| 1209 var parent = type.superclass; | 1213 var parent = type.superclass; |
| 1210 // TODO(sigmund): we don't seem to be reporting the analyzer error that a | 1214 // TODO(sigmund): we don't seem to be reporting the analyzer error that a |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1247 found = true; | 1251 found = true; |
| 1248 } | 1252 } |
| 1249 if (found) { | 1253 if (found) { |
| 1250 seen.add(name); | 1254 seen.add(name); |
| 1251 } | 1255 } |
| 1252 } | 1256 } |
| 1253 } else if (member is MethodDeclaration) { | 1257 } else if (member is MethodDeclaration) { |
| 1254 if (member.isStatic) { | 1258 if (member.isStatic) { |
| 1255 continue; | 1259 continue; |
| 1256 } | 1260 } |
| 1257 var method = member.element; | 1261 var method = elementForMethodDeclaration(member); |
| 1258 if (seen.contains(method.name)) { | 1262 if (seen.contains(method.name)) { |
| 1259 continue; | 1263 continue; |
| 1260 } | 1264 } |
| 1261 if (_checkSingleOverride( | 1265 if (_checkSingleOverride( |
| 1262 method, baseType, member.name, member, isSubclass)) { | 1266 method, baseType, member.name, member, isSubclass)) { |
| 1263 seen.add(method.name); | 1267 seen.add(method.name); |
| 1264 } | 1268 } |
| 1265 } else { | 1269 } else { |
| 1266 assert(member is ConstructorDeclaration); | 1270 assert(member is ConstructorDeclaration); |
| 1267 } | 1271 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1303 /// | 1307 /// |
| 1304 /// [cls] can be either a [ClassDeclaration] or a [InterfaceType]. For | 1308 /// [cls] can be either a [ClassDeclaration] or a [InterfaceType]. For |
| 1305 /// [ClassDeclaration]s errors are reported on the member that contains the | 1309 /// [ClassDeclaration]s errors are reported on the member that contains the |
| 1306 /// invalid override, for [InterfaceType]s we use [errorLocation] instead. | 1310 /// invalid override, for [InterfaceType]s we use [errorLocation] instead. |
| 1307 void _checkInterfacesOverrides( | 1311 void _checkInterfacesOverrides( |
| 1308 cls, Iterable<InterfaceType> interfaces, Set<String> seen, | 1312 cls, Iterable<InterfaceType> interfaces, Set<String> seen, |
| 1309 {Set<InterfaceType> visited, | 1313 {Set<InterfaceType> visited, |
| 1310 bool includeParents: true, | 1314 bool includeParents: true, |
| 1311 AstNode errorLocation}) { | 1315 AstNode errorLocation}) { |
| 1312 var node = cls is ClassDeclaration ? cls : null; | 1316 var node = cls is ClassDeclaration ? cls : null; |
| 1313 var type = cls is InterfaceType ? cls : node.element.type; | 1317 var type = |
| 1318 cls is InterfaceType ? cls : elementForClassDeclaration(node).type; |
| 1314 | 1319 |
| 1315 if (visited == null) { | 1320 if (visited == null) { |
| 1316 visited = new Set<InterfaceType>(); | 1321 visited = new Set<InterfaceType>(); |
| 1317 } else if (visited.contains(type)) { | 1322 } else if (visited.contains(type)) { |
| 1318 // Malformed type. | 1323 // Malformed type. |
| 1319 return; | 1324 return; |
| 1320 } else { | 1325 } else { |
| 1321 visited.add(type); | 1326 visited.add(type); |
| 1322 } | 1327 } |
| 1323 | 1328 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1358 | 1363 |
| 1359 /// Check overrides from mixin applications themselves. For example, in: | 1364 /// Check overrides from mixin applications themselves. For example, in: |
| 1360 /// | 1365 /// |
| 1361 /// A extends B with E, F | 1366 /// A extends B with E, F |
| 1362 /// | 1367 /// |
| 1363 /// we check: | 1368 /// we check: |
| 1364 /// | 1369 /// |
| 1365 /// B & E against B (equivalently how E overrides B) | 1370 /// B & E against B (equivalently how E overrides B) |
| 1366 /// B & E & F against B & E (equivalently how F overrides both B and E) | 1371 /// B & E & F against B & E (equivalently how F overrides both B and E) |
| 1367 void _checkMixinApplicationOverrides(ClassDeclaration node) { | 1372 void _checkMixinApplicationOverrides(ClassDeclaration node) { |
| 1368 var type = node.element.type; | 1373 var type = elementForClassDeclaration(node).type; |
| 1369 var parent = type.superclass; | 1374 var parent = type.superclass; |
| 1370 var mixins = type.mixins; | 1375 var mixins = type.mixins; |
| 1371 | 1376 |
| 1372 // Check overrides from applying mixins | 1377 // Check overrides from applying mixins |
| 1373 for (int i = 0; i < mixins.length; i++) { | 1378 for (int i = 0; i < mixins.length; i++) { |
| 1374 var seen = new Set<String>(); | 1379 var seen = new Set<String>(); |
| 1375 var current = mixins[i]; | 1380 var current = mixins[i]; |
| 1376 var errorLocation = node.withClause.mixinTypes[i]; | 1381 var errorLocation = node.withClause.mixinTypes[i]; |
| 1377 for (int j = i - 1; j >= 0; j--) { | 1382 for (int j = i - 1; j >= 0; j--) { |
| 1378 _checkIndividualOverridesFromType( | 1383 _checkIndividualOverridesFromType( |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1477 /// m(A a) {} | 1482 /// m(A a) {} |
| 1478 /// } | 1483 /// } |
| 1479 /// class Parent extends Grandparent { | 1484 /// class Parent extends Grandparent { |
| 1480 /// m(A a) {} | 1485 /// m(A a) {} |
| 1481 /// } | 1486 /// } |
| 1482 /// class Test extends Parent { | 1487 /// class Test extends Parent { |
| 1483 /// m(B a) {} // invalid override | 1488 /// m(B a) {} // invalid override |
| 1484 /// } | 1489 /// } |
| 1485 void _checkSuperOverrides(ClassDeclaration node) { | 1490 void _checkSuperOverrides(ClassDeclaration node) { |
| 1486 var seen = new Set<String>(); | 1491 var seen = new Set<String>(); |
| 1487 var current = node.element.type; | 1492 var current = elementForClassDeclaration(node).type; |
| 1488 var visited = new Set<InterfaceType>(); | 1493 var visited = new Set<InterfaceType>(); |
| 1489 do { | 1494 do { |
| 1490 visited.add(current); | 1495 visited.add(current); |
| 1491 current.mixins.reversed.forEach( | 1496 current.mixins.reversed.forEach( |
| 1492 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); | 1497 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); |
| 1493 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); | 1498 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); |
| 1494 current = current.superclass; | 1499 current = current.superclass; |
| 1495 } while (!current.isObject && !visited.contains(current)); | 1500 } while (!current.isObject && !visited.contains(current)); |
| 1496 } | 1501 } |
| 1497 } | 1502 } |
| OLD | NEW |