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 |