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 'dart:collection'; |
9 import 'package:analyzer/analyzer.dart'; | 10 import 'package:analyzer/analyzer.dart'; |
10 import 'package:analyzer/dart/ast/ast.dart'; | 11 import 'package:analyzer/dart/ast/ast.dart'; |
11 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; | 12 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; |
12 import 'package:analyzer/dart/ast/token.dart' show TokenType; | 13 import 'package:analyzer/dart/ast/token.dart' show TokenType; |
13 import 'package:analyzer/dart/ast/token.dart'; | 14 import 'package:analyzer/dart/ast/token.dart'; |
14 import 'package:analyzer/dart/ast/visitor.dart'; | 15 import 'package:analyzer/dart/ast/visitor.dart'; |
15 import 'package:analyzer/dart/element/element.dart'; | 16 import 'package:analyzer/dart/element/element.dart'; |
16 import 'package:analyzer/dart/element/type.dart'; | 17 import 'package:analyzer/dart/element/type.dart'; |
17 import 'package:analyzer/source/error_processor.dart' show ErrorProcessor; | 18 import 'package:analyzer/source/error_processor.dart' show ErrorProcessor; |
18 import 'package:analyzer/src/dart/element/element.dart'; | 19 import 'package:analyzer/src/dart/element/element.dart'; |
| 20 import 'package:analyzer/src/dart/element/member.dart'; |
19 import 'package:analyzer/src/dart/element/type.dart'; | 21 import 'package:analyzer/src/dart/element/type.dart'; |
20 import 'package:analyzer/src/error/codes.dart' show StrongModeCode; | 22 import 'package:analyzer/src/error/codes.dart' show StrongModeCode; |
21 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; | 23 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; |
22 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 24 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
23 import 'package:analyzer/src/generated/type_system.dart'; | 25 import 'package:analyzer/src/generated/type_system.dart'; |
24 import 'package:analyzer/src/summary/idl.dart'; | 26 import 'package:analyzer/src/summary/idl.dart'; |
25 | 27 |
26 import 'ast_properties.dart'; | 28 import 'ast_properties.dart'; |
27 | 29 |
28 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider], | 30 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider], |
(...skipping 15 matching lines...) Expand all Loading... |
44 return typeSystem.functionTypeToConcreteType(type); | 46 return typeSystem.functionTypeToConcreteType(type); |
45 } | 47 } |
46 return type; | 48 return type; |
47 } | 49 } |
48 | 50 |
49 bool hasStrictArrow(Expression expression) { | 51 bool hasStrictArrow(Expression expression) { |
50 var element = _getKnownElement(expression); | 52 var element = _getKnownElement(expression); |
51 return element is FunctionElement || element is MethodElement; | 53 return element is FunctionElement || element is MethodElement; |
52 } | 54 } |
53 | 55 |
| 56 /// Given a generic class [element] find its covariant upper bound, using |
| 57 /// the type system [rules]. |
| 58 /// |
| 59 /// Unlike [TypeSystem.instantiateToBounds], this will change `dynamic` into |
| 60 /// `Object` to work around an issue with fuzzy arrows. |
| 61 InterfaceType _getCovariantUpperBound(TypeSystem rules, ClassElement element) { |
| 62 var upperBound = rules.instantiateToBounds(element.type) as InterfaceType; |
| 63 var typeArgs = upperBound.typeArguments; |
| 64 // TODO(jmesserly): remove this. It is a workaround for fuzzy arrows. |
| 65 // To prevent extra checks due to fuzzy arrows, we need to instantiate with |
| 66 // `Object` rather than `dynamic`. Consider a case like: |
| 67 // |
| 68 // class C<T> { |
| 69 // void forEach(f(T t)) {} |
| 70 // } |
| 71 // |
| 72 // If we try `(dynamic) ~> void <: (T) ~> void` with fuzzy arrows, we will |
| 73 // treat `dynamic` as `bottom` and get `(bottom) -> void <: (T) -> void` |
| 74 // which indicates that a check is required on the parameter `f`. This check |
| 75 // is not sufficient when `T` is `dynamic`, however, because calling a |
| 76 // function with a fuzzy arrow type is not safe and requires a dynamic call. |
| 77 // See: https://github.com/dart-lang/sdk/issues/29295 |
| 78 // |
| 79 // For all other values of T, the check is unnecessary: it is sound to pass |
| 80 // a function that accepts any Object. |
| 81 if (typeArgs.any((t) => t.isDynamic)) { |
| 82 var newTypeArgs = typeArgs |
| 83 .map((t) => t.isDynamic ? rules.typeProvider.objectType : t) |
| 84 .toList(); |
| 85 upperBound = element.type.instantiate(newTypeArgs); |
| 86 } |
| 87 return upperBound; |
| 88 } |
| 89 |
54 DartType _elementType(Element e) { | 90 DartType _elementType(Element e) { |
55 if (e == null) { | 91 if (e == null) { |
56 // Malformed code - just return dynamic. | 92 // Malformed code - just return dynamic. |
57 return DynamicTypeImpl.instance; | 93 return DynamicTypeImpl.instance; |
58 } | 94 } |
59 return (e as dynamic).type; | 95 return (e as dynamic).type; |
60 } | 96 } |
61 | 97 |
62 Element _getKnownElement(Expression expression) { | 98 Element _getKnownElement(Expression expression) { |
63 if (expression is ParenthesizedExpression) { | 99 if (expression is ParenthesizedExpression) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 field = setter.variable; | 132 field = setter.variable; |
97 } else { | 133 } else { |
98 return null; | 134 return null; |
99 } | 135 } |
100 if (field.isSynthetic) return null; | 136 if (field.isSynthetic) return null; |
101 return field; | 137 return field; |
102 } | 138 } |
103 | 139 |
104 /// Looks up the declaration that matches [member] in [type] and returns it's | 140 /// Looks up the declaration that matches [member] in [type] and returns it's |
105 /// declared type. | 141 /// declared type. |
106 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => | 142 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) { |
107 _memberTypeGetter(member)(type); | 143 if (member.isPrivate && type.element.library != member.library) { |
108 | 144 return null; |
109 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) { | |
110 String memberName = member.name; | |
111 final isGetter = member is PropertyAccessorElement && member.isGetter; | |
112 final isSetter = member is PropertyAccessorElement && member.isSetter; | |
113 | |
114 FunctionType f(InterfaceType type) { | |
115 ExecutableElement baseMethod; | |
116 | |
117 if (member.isPrivate) { | |
118 var subtypeLibrary = member.library; | |
119 var baseLibrary = type.element.library; | |
120 if (baseLibrary != subtypeLibrary) { | |
121 return null; | |
122 } | |
123 } | |
124 | |
125 try { | |
126 if (isGetter) { | |
127 assert(!isSetter); | |
128 // Look for getter or field. | |
129 baseMethod = type.getGetter(memberName); | |
130 } else if (isSetter) { | |
131 baseMethod = type.getSetter(memberName); | |
132 } else { | |
133 baseMethod = type.getMethod(memberName); | |
134 } | |
135 } catch (e) { | |
136 // TODO(sigmund): remove this try-catch block (see issue #48). | |
137 } | |
138 if (baseMethod == null || baseMethod.isStatic) return null; | |
139 return baseMethod.type; | |
140 } | 145 } |
141 | 146 |
142 return f; | 147 var name = member.name; |
| 148 var baseMember = member is PropertyAccessorElement |
| 149 ? (member.isGetter ? type.getGetter(name) : type.getSetter(name)) |
| 150 : type.getMethod(name); |
| 151 if (baseMember == null || baseMember.isStatic) return null; |
| 152 return baseMember.type; |
143 } | 153 } |
144 | 154 |
145 typedef FunctionType _MemberTypeGetter(InterfaceType type); | |
146 | |
147 /// Checks the body of functions and properties. | 155 /// Checks the body of functions and properties. |
148 class CodeChecker extends RecursiveAstVisitor { | 156 class CodeChecker extends RecursiveAstVisitor { |
149 final StrongTypeSystemImpl rules; | 157 final StrongTypeSystemImpl rules; |
150 final TypeProvider typeProvider; | 158 final TypeProvider typeProvider; |
151 final AnalysisErrorListener reporter; | 159 final AnalysisErrorListener reporter; |
152 final AnalysisOptionsImpl _options; | 160 final AnalysisOptionsImpl _options; |
153 _OverrideChecker _overrideChecker; | 161 _OverrideChecker _overrideChecker; |
154 | 162 |
155 bool _failure = false; | 163 bool _failure = false; |
156 bool _hasImplicitCasts; | 164 bool _hasImplicitCasts; |
| 165 HashSet<ExecutableElement> _covariantPrivateMembers; |
157 | 166 |
158 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules, | 167 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules, |
159 AnalysisErrorListener reporter, this._options) | 168 AnalysisErrorListener reporter, this._options) |
160 : typeProvider = typeProvider, | 169 : typeProvider = typeProvider, |
161 rules = rules, | 170 rules = rules, |
162 reporter = reporter { | 171 reporter = reporter { |
163 _overrideChecker = new _OverrideChecker(this); | 172 _overrideChecker = new _OverrideChecker(this); |
164 } | 173 } |
165 | 174 |
166 bool get failure => _failure; | 175 bool get failure => _failure; |
(...skipping 27 matching lines...) Expand all Loading... |
194 _checkImplicitCast(expr, type); | 203 _checkImplicitCast(expr, type); |
195 } | 204 } |
196 } | 205 } |
197 | 206 |
198 /// Analyzer checks boolean conversions, but we need to check too, because | 207 /// Analyzer checks boolean conversions, but we need to check too, because |
199 /// it uses the default assignability rules that allow `dynamic` and `Object` | 208 /// it uses the default assignability rules that allow `dynamic` and `Object` |
200 /// to be assigned to bool with no message. | 209 /// to be assigned to bool with no message. |
201 void checkBoolean(Expression expr) => | 210 void checkBoolean(Expression expr) => |
202 checkAssignment(expr, typeProvider.boolType); | 211 checkAssignment(expr, typeProvider.boolType); |
203 | 212 |
204 void checkFunctionApplication(InvocationExpression node) { | 213 void _checkFunctionApplication(InvocationExpression node) { |
205 var ft = _getTypeAsCaller(node); | 214 var ft = _getTypeAsCaller(node); |
206 | 215 |
207 if (_isDynamicCall(node, ft)) { | 216 if (_isDynamicCall(node, ft)) { |
208 // If f is Function and this is a method invocation, we should have | 217 // If f is Function and this is a method invocation, we should have |
209 // gotten an analyzer error, so no need to issue another error. | 218 // gotten an analyzer error, so no need to issue another error. |
210 _recordDynamicInvoke(node, node.function); | 219 _recordDynamicInvoke(node, node.function); |
211 } else { | 220 } else { |
212 checkArgumentList(node.argumentList, ft); | 221 checkArgumentList(node.argumentList, ft); |
213 } | 222 } |
214 } | 223 } |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 | 313 |
305 @override | 314 @override |
306 void visitComment(Comment node) { | 315 void visitComment(Comment node) { |
307 // skip, no need to do typechecking inside comments (they may contain | 316 // skip, no need to do typechecking inside comments (they may contain |
308 // comment references which would require resolution). | 317 // comment references which would require resolution). |
309 } | 318 } |
310 | 319 |
311 @override | 320 @override |
312 void visitCompilationUnit(CompilationUnit node) { | 321 void visitCompilationUnit(CompilationUnit node) { |
313 _hasImplicitCasts = false; | 322 _hasImplicitCasts = false; |
| 323 _covariantPrivateMembers = new HashSet(); |
314 node.visitChildren(this); | 324 node.visitChildren(this); |
315 setHasImplicitCasts(node, _hasImplicitCasts); | 325 setHasImplicitCasts(node, _hasImplicitCasts); |
| 326 setCovariantPrivateMembers(node, _covariantPrivateMembers); |
316 } | 327 } |
317 | 328 |
318 @override | 329 @override |
319 void visitConditionalExpression(ConditionalExpression node) { | 330 void visitConditionalExpression(ConditionalExpression node) { |
320 checkBoolean(node.condition); | 331 checkBoolean(node.condition); |
321 node.visitChildren(this); | 332 node.visitChildren(this); |
322 } | 333 } |
323 | 334 |
324 /// Check constructor declaration to ensure correct super call placement. | 335 /// Check constructor declaration to ensure correct super call placement. |
325 @override | 336 @override |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 @override | 442 @override |
432 void visitForStatement(ForStatement node) { | 443 void visitForStatement(ForStatement node) { |
433 if (node.condition != null) { | 444 if (node.condition != null) { |
434 checkBoolean(node.condition); | 445 checkBoolean(node.condition); |
435 } | 446 } |
436 node.visitChildren(this); | 447 node.visitChildren(this); |
437 } | 448 } |
438 | 449 |
439 @override | 450 @override |
440 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | 451 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
441 checkFunctionApplication(node); | 452 _checkFunctionApplication(node); |
442 node.visitChildren(this); | 453 node.visitChildren(this); |
443 } | 454 } |
444 | 455 |
445 @override | 456 @override |
446 void visitIfStatement(IfStatement node) { | 457 void visitIfStatement(IfStatement node) { |
447 checkBoolean(node.condition); | 458 checkBoolean(node.condition); |
448 node.visitChildren(this); | 459 node.visitChildren(this); |
449 } | 460 } |
450 | 461 |
451 @override | 462 @override |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
558 // | 569 // |
559 // ... from case like: | 570 // ... from case like: |
560 // | 571 // |
561 // SomeType s; | 572 // SomeType s; |
562 // s.someDynamicField(...); // static get, followed by dynamic call. | 573 // s.someDynamicField(...); // static get, followed by dynamic call. |
563 // | 574 // |
564 // The first case is handled here, the second case is handled below when | 575 // The first case is handled here, the second case is handled below when |
565 // we call [checkFunctionApplication]. | 576 // we call [checkFunctionApplication]. |
566 setIsDynamicInvoke(node.methodName, true); | 577 setIsDynamicInvoke(node.methodName, true); |
567 } else { | 578 } else { |
568 checkFunctionApplication(node); | 579 _checkImplicitCovarianceCast(node, target, element); |
| 580 _checkFunctionApplication(node); |
569 } | 581 } |
570 node.visitChildren(this); | 582 // Don't visit methodName, we already checked things related to the call. |
| 583 node.target?.accept(this); |
| 584 node.typeArguments?.accept(this); |
| 585 node.argumentList?.accept(this); |
571 } | 586 } |
572 | 587 |
573 @override | 588 @override |
574 void visitPostfixExpression(PostfixExpression node) { | 589 void visitPostfixExpression(PostfixExpression node) { |
575 _checkUnary(node.operand, node.operator, node.staticElement); | 590 _checkUnary(node.operand, node.operator, node.staticElement); |
576 node.visitChildren(this); | 591 node.visitChildren(this); |
577 } | 592 } |
578 | 593 |
579 @override | 594 @override |
580 void visitPrefixedIdentifier(PrefixedIdentifier node) { | 595 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
657 variableElement.kind == ElementKind.FIELD) { | 672 variableElement.kind == ElementKind.FIELD) { |
658 _validateTopLevelInitializer(variableElement.name, node.initializer); | 673 _validateTopLevelInitializer(variableElement.name, node.initializer); |
659 } | 674 } |
660 } | 675 } |
661 return super.visitVariableDeclaration(node); | 676 return super.visitVariableDeclaration(node); |
662 } | 677 } |
663 | 678 |
664 @override | 679 @override |
665 void visitVariableDeclarationList(VariableDeclarationList node) { | 680 void visitVariableDeclarationList(VariableDeclarationList node) { |
666 TypeAnnotation type = node.type; | 681 TypeAnnotation type = node.type; |
667 if (type == null) { | 682 |
668 // No checks are needed when the type is var. Although internally the | 683 for (VariableDeclaration variable in node.variables) { |
669 // typing rules may have inferred a more precise type for the variable | 684 var initializer = variable.initializer; |
670 // based on the initializer. | 685 if (initializer != null) { |
671 } else { | 686 if (type != null) { |
672 for (VariableDeclaration variable in node.variables) { | |
673 var initializer = variable.initializer; | |
674 if (initializer != null) { | |
675 checkAssignment(initializer, type.type); | 687 checkAssignment(initializer, type.type); |
676 } | 688 } |
677 } | 689 } |
678 } | 690 } |
| 691 |
679 node.visitChildren(this); | 692 node.visitChildren(this); |
680 } | 693 } |
681 | 694 |
682 @override | 695 @override |
683 void visitWhileStatement(WhileStatement node) { | 696 void visitWhileStatement(WhileStatement node) { |
684 checkBoolean(node.condition); | 697 checkBoolean(node.condition); |
685 node.visitChildren(this); | 698 node.visitChildren(this); |
686 } | 699 } |
687 | 700 |
688 @override | 701 @override |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
722 // back to it. So these two implicit casts are equivalent: | 735 // back to it. So these two implicit casts are equivalent: |
723 // | 736 // |
724 // y = /*implicit cast*/(y + 42); | 737 // y = /*implicit cast*/(y + 42); |
725 // /*implicit assignment cast*/y += 42; | 738 // /*implicit assignment cast*/y += 42; |
726 // | 739 // |
727 _checkImplicitCast(expr.leftHandSide, lhsType, | 740 _checkImplicitCast(expr.leftHandSide, lhsType, |
728 from: returnType, opAssign: true); | 741 from: returnType, opAssign: true); |
729 } | 742 } |
730 } | 743 } |
731 | 744 |
732 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { | 745 void _checkFieldAccess( |
733 if (field.staticElement == null && | 746 AstNode node, Expression target, SimpleIdentifier field) { |
734 !typeProvider.isObjectMember(field.name)) { | 747 var element = field.staticElement; |
| 748 _checkImplicitCovarianceCast(node, target, element); |
| 749 if (element == null && !typeProvider.isObjectMember(field.name)) { |
735 _recordDynamicInvoke(node, target); | 750 _recordDynamicInvoke(node, target); |
736 } | 751 } |
737 node.visitChildren(this); | 752 node.visitChildren(this); |
738 } | 753 } |
739 | 754 |
740 /// Checks if an implicit cast of [expr] from [from] type to [to] type is | 755 /// Checks if an implicit cast of [expr] from [from] type to [to] type is |
741 /// needed, and if so records it. | 756 /// needed, and if so records it. |
742 /// | 757 /// |
743 /// If [from] is omitted, uses the static type of [expr]. | 758 /// If [from] is omitted, uses the static type of [expr]. |
744 /// | 759 /// |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
819 // /*implicit assignment cast*/y++; | 834 // /*implicit assignment cast*/y++; |
820 // | 835 // |
821 _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true); | 836 _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true); |
822 } | 837 } |
823 } | 838 } |
824 } | 839 } |
825 | 840 |
826 DartType _getDefiniteType(Expression expr) => | 841 DartType _getDefiniteType(Expression expr) => |
827 getDefiniteType(expr, rules, typeProvider); | 842 getDefiniteType(expr, rules, typeProvider); |
828 | 843 |
| 844 /// If we're calling into [member] through the [target], we may need to |
| 845 /// insert a caller side check for soundness on the result of the expression |
| 846 /// [node]. |
| 847 /// |
| 848 /// This happens when [target] is an unsafe covariant interface, and [member] |
| 849 /// could return a type that is not a subtype of the expected static type |
| 850 /// given target's type. For example: |
| 851 /// |
| 852 /// typedef F<T>(T t); |
| 853 /// class C<T> { |
| 854 /// F<T> f; |
| 855 /// C(this.f); |
| 856 /// } |
| 857 /// test1() { |
| 858 /// C<Object> c = new C<int>((int x) => x + 42)); |
| 859 /// F<Object> f = c.f; // need an implicit cast here. |
| 860 /// f('hello'); |
| 861 /// } |
| 862 /// |
| 863 /// Here target is `c`, the target type is `C<Object>`, the member is |
| 864 /// `get f() -> F<T>`, and the expression node is `c.f`. When we call `c.f` |
| 865 /// the expected static result is `F<Object>`. However `c.f` actually returns |
| 866 /// `F<int>`, which is not a subtype of `F<Object>`. So this method will add |
| 867 /// an implicit cast `(c.f as F<Object>)` to guard against this case. |
| 868 /// |
| 869 /// Note that it is possible for the cast to succeed, for example: |
| 870 /// `new C<int>((Object x) => '$x'))`. It is safe to pass any object to that |
| 871 /// function, including an `int`. |
| 872 void _checkImplicitCovarianceCast( |
| 873 Expression node, Expression target, Element member) { |
| 874 // If we're calling an instance method or getter, then we |
| 875 // want to check the result type. |
| 876 // |
| 877 // We intentionally ignore method tear-offs, because those methods have |
| 878 // covariance checks for their parameters inside the method. |
| 879 var targetType = target?.staticType; |
| 880 if (member is ExecutableElement && |
| 881 _isInstanceMember(member) && |
| 882 targetType is InterfaceType && |
| 883 targetType.typeArguments.isNotEmpty && |
| 884 !_targetHasKnownGenericTypeArguments(target)) { |
| 885 // Track private setters/method calls. We can sometimes eliminate the |
| 886 // parameter check in code generation, if it was never needed. |
| 887 // This member will need a check, however, because we are calling through |
| 888 // an unsafe target. |
| 889 if (member.isPrivate && member.parameters.isNotEmpty) { |
| 890 _covariantPrivateMembers |
| 891 .add(member is ExecutableMember ? member.baseElement : member); |
| 892 } |
| 893 |
| 894 // Get the lower bound of the declared return type (e.g. `F<Null>`) and |
| 895 // see if it can be assigned to the expected type (e.g. `F<Object>`). |
| 896 // |
| 897 // That way we can tell if any lower `T` will work or not. |
| 898 var classType = targetType.element.type; |
| 899 var classLowerBound = classType.instantiate(new List.filled( |
| 900 classType.typeParameters.length, typeProvider.nullType)); |
| 901 var memberLowerBound = _lookUpMember(classLowerBound, member).type; |
| 902 var expectedType = member.returnType; |
| 903 |
| 904 if (!rules.isSubtypeOf(memberLowerBound.returnType, expectedType)) { |
| 905 if (node is MethodInvocation && member is! MethodElement) { |
| 906 // If `o.m` is not a method, we need to cast `o.m` before the call: |
| 907 // `(o.m as expectedType)(args)`. |
| 908 // This cannot be represented by an `as` node without changing the |
| 909 // Dart AST structure, so we record it as a special cast. |
| 910 setImplicitOperationCast(node, expectedType); |
| 911 } else { |
| 912 setImplicitCast(node, expectedType); |
| 913 } |
| 914 _hasImplicitCasts = true; |
| 915 } |
| 916 } |
| 917 } |
| 918 |
| 919 /// Returns true if we can safely skip the covariance checks because [target] |
| 920 /// has known type arguments, such as `this` `super` or a non-factory `new`. |
| 921 /// |
| 922 /// For example: |
| 923 /// |
| 924 /// class C<T> { |
| 925 /// T _t; |
| 926 /// } |
| 927 /// class D<T> extends C<T> { |
| 928 /// method<S extends T>(T t, C<T> c) { |
| 929 /// // implicit cast: t as T; |
| 930 /// // implicit cast: c as C<T>; |
| 931 /// |
| 932 /// // These do not need further checks. The type parameter `T` for |
| 933 /// // `this` must be the same as our `T` |
| 934 /// this._t = t; |
| 935 /// super._t = t; |
| 936 /// new C<T>()._t = t; // non-factory |
| 937 /// |
| 938 /// // This needs further checks. The type of `c` could be `C<S>` for |
| 939 /// // some `S <: T`. |
| 940 /// c._t = t; |
| 941 /// // factory statically returns `C<T>`, dynamically returns `C<S>`. |
| 942 /// new F<T, S>()._t = t; |
| 943 /// } |
| 944 /// } |
| 945 /// class F<T, S extends T> extends C<T> { |
| 946 /// factory F() => new C<S>(); |
| 947 /// } |
| 948 /// |
| 949 bool _targetHasKnownGenericTypeArguments(Expression target) { |
| 950 return target == null || // implicit this |
| 951 target is ThisExpression || |
| 952 target is SuperExpression || |
| 953 target is InstanceCreationExpression && |
| 954 target.staticElement?.isFactory == false; |
| 955 } |
| 956 |
| 957 bool _isInstanceMember(ExecutableElement e) => |
| 958 !e.isStatic && |
| 959 (e is MethodElement || |
| 960 e is PropertyAccessorElement && e.variable is FieldElement); |
| 961 |
| 962 ExecutableElement _lookUpMember(InterfaceType type, ExecutableElement e) { |
| 963 var name = e.name; |
| 964 var library = e.library; |
| 965 return e is PropertyAccessorElement |
| 966 ? (e.isGetter |
| 967 ? type.lookUpInheritedGetter(name, library: library) |
| 968 : type.lookUpInheritedSetter(name, library: library)) |
| 969 : type.lookUpInheritedMethod(name, library: library); |
| 970 } |
| 971 |
829 /// Gets the expected return type of the given function [body], either from | 972 /// Gets the expected return type of the given function [body], either from |
830 /// a normal return/yield, or from a yield*. | 973 /// a normal return/yield, or from a yield*. |
831 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | 974 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
832 FunctionType functionType; | 975 FunctionType functionType; |
833 var parent = body.parent; | 976 var parent = body.parent; |
834 if (parent is Declaration) { | 977 if (parent is Declaration) { |
835 functionType = _elementType(parent.element); | 978 functionType = _elementType(parent.element); |
836 } else { | 979 } else { |
837 assert(parent is FunctionExpression); | 980 assert(parent is FunctionExpression); |
838 functionType = | 981 functionType = |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1031 errorCode = StrongModeCode.DYNAMIC_CAST; | 1174 errorCode = StrongModeCode.DYNAMIC_CAST; |
1032 } else if (parent is VariableDeclaration && parent.initializer == expr) { | 1175 } else if (parent is VariableDeclaration && parent.initializer == expr) { |
1033 errorCode = StrongModeCode.ASSIGNMENT_CAST; | 1176 errorCode = StrongModeCode.ASSIGNMENT_CAST; |
1034 } else { | 1177 } else { |
1035 errorCode = opAssign | 1178 errorCode = opAssign |
1036 ? StrongModeCode.DOWN_CAST_IMPLICIT_ASSIGN | 1179 ? StrongModeCode.DOWN_CAST_IMPLICIT_ASSIGN |
1037 : StrongModeCode.DOWN_CAST_IMPLICIT; | 1180 : StrongModeCode.DOWN_CAST_IMPLICIT; |
1038 } | 1181 } |
1039 _recordMessage(expr, errorCode, [from, to]); | 1182 _recordMessage(expr, errorCode, [from, to]); |
1040 if (opAssign) { | 1183 if (opAssign) { |
1041 setImplicitAssignmentCast(expr, to); | 1184 setImplicitOperationCast(expr, to); |
1042 } else { | 1185 } else { |
1043 setImplicitCast(expr, to); | 1186 setImplicitCast(expr, to); |
1044 } | 1187 } |
1045 _hasImplicitCasts = true; | 1188 _hasImplicitCasts = true; |
1046 } | 1189 } |
1047 | 1190 |
1048 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { | 1191 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { |
1049 // Compute the right severity taking the analysis options into account. | 1192 // Compute the right severity taking the analysis options into account. |
1050 // We construct a dummy error to make the common case where we end up | 1193 // We construct a dummy error to make the common case where we end up |
1051 // ignoring the strong mode message cheaper. | 1194 // ignoring the strong mode message cheaper. |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1242 | 1385 |
1243 void check(Declaration node) { | 1386 void check(Declaration node) { |
1244 var element = | 1387 var element = |
1245 resolutionMap.elementDeclaredByDeclaration(node) as ClassElement; | 1388 resolutionMap.elementDeclaredByDeclaration(node) as ClassElement; |
1246 if (element.type.isObject) { | 1389 if (element.type.isObject) { |
1247 return; | 1390 return; |
1248 } | 1391 } |
1249 _checkSuperOverrides(node, element); | 1392 _checkSuperOverrides(node, element); |
1250 _checkMixinApplicationOverrides(node, element); | 1393 _checkMixinApplicationOverrides(node, element); |
1251 _checkAllInterfaceOverrides(node, element); | 1394 _checkAllInterfaceOverrides(node, element); |
| 1395 _checkForCovariantGenerics(node, element); |
| 1396 } |
| 1397 |
| 1398 /// Finds implicit casts that we need on parameters and type formals to |
| 1399 /// ensure soundness of covariant generics, and records them on the [node]. |
| 1400 /// |
| 1401 /// The parameter checks can be retrived using [getClassCovariantParameters] |
| 1402 /// and [getSuperclassCovariantParameters]. |
| 1403 /// |
| 1404 /// For each member of this class and non-overridden inherited member, we |
| 1405 /// check to see if any generic super interface permits an unsound call to the |
| 1406 /// concrete member. For example: |
| 1407 /// |
| 1408 /// class C<T> { |
| 1409 /// add(T t) {} // C<Object>.add is unsafe, need a check on `t` |
| 1410 /// } |
| 1411 /// class D extends C<int> { |
| 1412 /// add(int t) {} // C<Object>.add is unsafe, need a check on `t` |
| 1413 /// } |
| 1414 /// class E extends C<int> { |
| 1415 /// add(Object t) {} // no check needed, C<Object>.add is safe |
| 1416 /// } |
| 1417 /// |
| 1418 void _checkForCovariantGenerics(Declaration node, ClassElement element) { |
| 1419 // Find all generic interfaces that could be used to call into members of |
| 1420 // this class. This will help us identify which parameters need checks |
| 1421 // for soundness. |
| 1422 var allCovariant = _findAllGenericInterfaces(element.type); |
| 1423 if (allCovariant.isEmpty) return; |
| 1424 |
| 1425 var seenConcreteMembers = new HashSet<String>(); |
| 1426 var members = _getConcreteMembers(element.type, seenConcreteMembers); |
| 1427 |
| 1428 // For members on this class, check them against all generic interfaces. |
| 1429 var checks = _findCovariantChecks(members, allCovariant); |
| 1430 // Store those checks on the class declaration. |
| 1431 setClassCovariantParameters(node, checks); |
| 1432 |
| 1433 // For members of the superclass, we may need to add checks because this |
| 1434 // class adds a new unsafe interface. Collect those checks. |
| 1435 checks = _findSuperclassCovariantChecks( |
| 1436 element, allCovariant, seenConcreteMembers); |
| 1437 // Store the checks on the class declaration, it will need to ensure the |
| 1438 // inherited members are appropriately guarded to ensure soundness. |
| 1439 setSuperclassCovariantParameters(node, checks); |
| 1440 } |
| 1441 |
| 1442 /// For each member of this class and non-overridden inherited member, we |
| 1443 /// check to see if any generic super interface permits an unsound call to the |
| 1444 /// concrete member. For example: |
| 1445 /// |
| 1446 /// We must check non-overridden inherited members because this class could |
| 1447 /// contain a new interface that permits unsound access to that member. In |
| 1448 /// those cases, the class is expected to insert stub that checks the type |
| 1449 /// before calling `super`. For example: |
| 1450 /// |
| 1451 /// class C<T> { |
| 1452 /// add(T t) {} |
| 1453 /// } |
| 1454 /// class D { |
| 1455 /// add(int t) {} |
| 1456 /// } |
| 1457 /// class E extends D implements C<int> { |
| 1458 /// // C<Object>.add is unsafe, and D.m is marked for a check. |
| 1459 /// // |
| 1460 /// // one way to implement this is to generate a stub method: |
| 1461 /// // add(t) => super.add(t as int); |
| 1462 /// } |
| 1463 /// |
| 1464 Set<Element> _findSuperclassCovariantChecks(ClassElement element, |
| 1465 Set<ClassElement> allCovariant, HashSet<String> seenConcreteMembers) { |
| 1466 var visited = new HashSet<ClassElement>()..add(element); |
| 1467 var superChecks = new Set<Element>(); |
| 1468 var existingChecks = new HashSet<Element>(); |
| 1469 |
| 1470 void visitImmediateSuper(InterfaceType type) { |
| 1471 // For members of mixins/supertypes, check them against new interfaces, |
| 1472 // and also record any existing checks they already had. |
| 1473 var oldCovariant = _findAllGenericInterfaces(type); |
| 1474 var newCovariant = allCovariant.difference(oldCovariant); |
| 1475 if (newCovariant.isEmpty) return; |
| 1476 |
| 1477 void visitSuper(InterfaceType type) { |
| 1478 var element = type.element; |
| 1479 if (visited.add(element)) { |
| 1480 var members = _getConcreteMembers(type, seenConcreteMembers); |
| 1481 _findCovariantChecks(members, newCovariant, superChecks); |
| 1482 _findCovariantChecks(members, oldCovariant, existingChecks); |
| 1483 element.mixins.reversed.forEach(visitSuper); |
| 1484 var s = element.supertype; |
| 1485 if (s != null) visitSuper(s); |
| 1486 } |
| 1487 } |
| 1488 |
| 1489 visitSuper(type); |
| 1490 } |
| 1491 |
| 1492 element.mixins.reversed.forEach(visitImmediateSuper); |
| 1493 var s = element.supertype; |
| 1494 if (s != null) visitImmediateSuper(s); |
| 1495 |
| 1496 superChecks.removeAll(existingChecks); |
| 1497 return superChecks; |
| 1498 } |
| 1499 |
| 1500 /// Gets all concrete instance members declared on this type, skipping already |
| 1501 /// [seenConcreteMembers] and adding any found ones to it. |
| 1502 /// |
| 1503 /// By tracking the set of seen members, we can visit superclasses and mixins |
| 1504 /// and ultimately collect every most-derived member exposed by a given type. |
| 1505 static List<ExecutableElement> _getConcreteMembers( |
| 1506 InterfaceType type, HashSet<String> seenConcreteMembers) { |
| 1507 var members = <ExecutableElement>[]; |
| 1508 for (var declaredMembers in [type.accessors, type.methods]) { |
| 1509 for (var member in declaredMembers) { |
| 1510 // We only visit each most derived concrete member. |
| 1511 // To avoid visiting an overridden superclass member, we skip members |
| 1512 // we've seen, and visit starting from the class, then mixins in |
| 1513 // reverse order, then superclasses. |
| 1514 if (!member.isStatic && |
| 1515 !member.isAbstract && |
| 1516 seenConcreteMembers.add(member.name)) { |
| 1517 members.add(member); |
| 1518 } |
| 1519 } |
| 1520 } |
| 1521 return members; |
| 1522 } |
| 1523 |
| 1524 /// Find all covariance checks on parameters/type parameters needed for |
| 1525 /// soundness given a set of concrete [members] and a set of unsafe generic |
| 1526 /// [covariantInterfaces] that may allow those members to be called in an |
| 1527 /// unsound way. |
| 1528 /// |
| 1529 /// See [_findCovariantChecksForMember] for more information and an exmaple. |
| 1530 Set<Element> _findCovariantChecks(Iterable<ExecutableElement> members, |
| 1531 Iterable<ClassElement> covariantInterfaces, |
| 1532 [Set<Element> covariantChecks]) { |
| 1533 covariantChecks ??= new Set(); |
| 1534 if (members.isEmpty) return covariantChecks; |
| 1535 |
| 1536 for (var iface in covariantInterfaces) { |
| 1537 var unsafeSupertype = _getCovariantUpperBound(rules, iface); |
| 1538 for (var m in members) { |
| 1539 _findCovariantChecksForMember(m, unsafeSupertype, covariantChecks); |
| 1540 } |
| 1541 } |
| 1542 return covariantChecks; |
| 1543 } |
| 1544 |
| 1545 /// Given a [member] and a covariant [unsafeSupertype], determine if any |
| 1546 /// type formals or parameters of this member need a check because of the |
| 1547 /// unsoundness in the unsafe covariant supertype. |
| 1548 /// |
| 1549 /// For example: |
| 1550 /// |
| 1551 /// class C<T> { |
| 1552 /// m(T t) {} |
| 1553 /// g<S extends T>() => <S>[]; |
| 1554 /// } |
| 1555 /// class D extends C<num> { |
| 1556 /// m(num n) {} |
| 1557 /// g<R extends num>() => <R>[]; |
| 1558 /// } |
| 1559 /// main() { |
| 1560 /// C<Object> c = new C<int>(); |
| 1561 /// c.m('hi'); // must throw for soundness |
| 1562 /// c.g<String>(); // must throw for soundness |
| 1563 /// |
| 1564 /// c = new D(); |
| 1565 /// c.m('hi'); // must throw for soundness |
| 1566 /// c.g<String>(); // must throw for soundness |
| 1567 /// } |
| 1568 /// |
| 1569 /// We've already found `C<Object>` is a potentially unsafe covariant generic |
| 1570 /// supertpe, and we call this method to see if any members need a check |
| 1571 /// because of `C<Object>`. |
| 1572 /// |
| 1573 /// In this example, we will call this method with: |
| 1574 /// - `C<T>.m` and `C<Object>`, finding that `t` needs a check. |
| 1575 /// - `C<T>.g` and `C<Object>`, finding that `S` needs a check. |
| 1576 /// - `D.m` and `C<Object>`, finding that `n` needs a check. |
| 1577 /// - `D.g` and `C<Object>`, finding that `R` needs a check. |
| 1578 /// |
| 1579 /// Given `C<T>.m` and `C<Object>`, we search for covariance checks like this |
| 1580 /// (`*` short for `dynamic`): |
| 1581 /// - get the type of `C<Object>.m`: `(Object) -> *` |
| 1582 /// - get the type of `C<T>.m`: `(T) -> *` |
| 1583 /// - perform a subtype check `(T) -> * <: (Object) -> *`, |
| 1584 /// and record any parameters/type formals that violate soundess. |
| 1585 /// - that checks `Object <: T`, which is false, thus we need a check on |
| 1586 /// parameter `t` of `C<T>.m` |
| 1587 /// |
| 1588 /// Another example is `D.g` and `C<Object>`: |
| 1589 /// - get the type of `C<Object>.m`: `<S extends Object>() -> *` |
| 1590 /// - get the type of `D.g`: `<R extends num>() -> *` |
| 1591 /// - perform a subtype check |
| 1592 /// `<S extends Object>() -> * <: <R extends num>() -> *`, |
| 1593 /// and record any parameters/type formals that violate soundess. |
| 1594 /// - that checks the type formal bound of `S` and `R` asserting |
| 1595 /// `Object <: num`, which is false, thus we need a check on type formal `R` |
| 1596 /// of `D.g`. |
| 1597 void _findCovariantChecksForMember(ExecutableElement member, |
| 1598 InterfaceType unsafeSupertype, Set<Element> covariantChecks) { |
| 1599 var f2 = _getMemberType(unsafeSupertype, member); |
| 1600 if (f2 == null) return; |
| 1601 var f1 = member.type; |
| 1602 |
| 1603 // Find parameter or type formal checks that we need to ensure `f2 <: f1`. |
| 1604 // |
| 1605 // The static type system allows this subtyping, but it is not sound without |
| 1606 // these runtime checks. |
| 1607 void addCheck(Element e) { |
| 1608 covariantChecks.add(e is Member ? e.baseElement : e); |
| 1609 } |
| 1610 |
| 1611 var fresh = FunctionTypeImpl.relateTypeFormals(f1, f2, (b2, b1, p2, p1) { |
| 1612 if (!rules.isSubtypeOf(b2, b1)) addCheck(p1); |
| 1613 return true; |
| 1614 }); |
| 1615 if (fresh != null) { |
| 1616 f1 = f1.instantiate(fresh); |
| 1617 f2 = f2.instantiate(fresh); |
| 1618 } |
| 1619 FunctionTypeImpl.relateParameters(f1.parameters, f2.parameters, (p1, p2) { |
| 1620 if (!rules.isOverrideSubtypeOfParameter(p1, p2)) addCheck(p1); |
| 1621 return true; |
| 1622 }); |
| 1623 } |
| 1624 |
| 1625 /// Find all generic interfaces that are implemented by [type], including |
| 1626 /// [type] itself if it is generic. |
| 1627 /// |
| 1628 /// This represents the complete set of unsafe covariant interfaces that could |
| 1629 /// be used to call members of [type]. |
| 1630 /// |
| 1631 /// Because we're going to instantiate these to their upper bound, we don't |
| 1632 /// have to track type parameters. |
| 1633 static Set<ClassElement> _findAllGenericInterfaces(InterfaceType type) { |
| 1634 var visited = new HashSet<ClassElement>(); |
| 1635 var genericSupertypes = new Set<ClassElement>(); |
| 1636 |
| 1637 void visitTypeAndSupertypes(InterfaceType type) { |
| 1638 var element = type.element; |
| 1639 if (visited.add(element)) { |
| 1640 if (element.typeParameters.isNotEmpty) { |
| 1641 genericSupertypes.add(element); |
| 1642 } |
| 1643 var supertype = element.supertype; |
| 1644 if (supertype != null) visitTypeAndSupertypes(supertype); |
| 1645 element.mixins.forEach(visitTypeAndSupertypes); |
| 1646 element.interfaces.forEach(visitTypeAndSupertypes); |
| 1647 } |
| 1648 } |
| 1649 |
| 1650 visitTypeAndSupertypes(type); |
| 1651 |
| 1652 return genericSupertypes; |
1252 } | 1653 } |
1253 | 1654 |
1254 /// Checks that implementations correctly override all reachable interfaces. | 1655 /// Checks that implementations correctly override all reachable interfaces. |
1255 /// In particular, we need to check these overrides for the definitions in | 1656 /// In particular, we need to check these overrides for the definitions in |
1256 /// the class itself and each its superclasses. If a superclass is not | 1657 /// the class itself and each its superclasses. If a superclass is not |
1257 /// abstract, then we can skip its transitive interfaces. For example, in: | 1658 /// abstract, then we can skip its transitive interfaces. For example, in: |
1258 /// | 1659 /// |
1259 /// B extends C implements G | 1660 /// B extends C implements G |
1260 /// A extends B with E, F implements H, I | 1661 /// A extends B with E, F implements H, I |
1261 /// | 1662 /// |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1360 /// Check that individual methods and fields in [subType] correctly override | 1761 /// Check that individual methods and fields in [subType] correctly override |
1361 /// the declarations in [baseType]. | 1762 /// the declarations in [baseType]. |
1362 /// | 1763 /// |
1363 /// The [errorLocation] node indicates where errors are reported, see | 1764 /// The [errorLocation] node indicates where errors are reported, see |
1364 /// [_checkSingleOverride] for more details. | 1765 /// [_checkSingleOverride] for more details. |
1365 /// | 1766 /// |
1366 /// The set [seen] is used to avoid reporting overrides more than once. It | 1767 /// The set [seen] is used to avoid reporting overrides more than once. It |
1367 /// is used when invoking this function multiple times when checking several | 1768 /// is used when invoking this function multiple times when checking several |
1368 /// types in a class hierarchy. Errors are reported only the first time an | 1769 /// types in a class hierarchy. Errors are reported only the first time an |
1369 /// invalid override involving a specific member is encountered. | 1770 /// invalid override involving a specific member is encountered. |
1370 _checkIndividualOverridesFromType( | 1771 void _checkIndividualOverridesFromType( |
1371 InterfaceType subType, | 1772 InterfaceType subType, |
1372 InterfaceType baseType, | 1773 InterfaceType baseType, |
1373 AstNode errorLocation, | 1774 AstNode errorLocation, |
1374 Set<String> seen, | 1775 Set<String> seen, |
1375 bool isSubclass) { | 1776 bool isSubclass) { |
1376 void checkHelper(ExecutableElement e) { | 1777 void checkHelper(ExecutableElement e) { |
1377 if (e.isStatic) return; | 1778 if (e.isStatic) return; |
1378 if (seen.contains(e.name)) return; | 1779 if (seen.contains(e.name)) return; |
1379 if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) { | 1780 if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) { |
1380 seen.add(e.name); | 1781 seen.add(e.name); |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1495 /// ^ | 1896 /// ^ |
1496 /// | 1897 /// |
1497 /// When checking for overrides from a type and it's super types, [node] is | 1898 /// When checking for overrides from a type and it's super types, [node] is |
1498 /// the AST node that defines [element]. This is used to determine whether the | 1899 /// the AST node that defines [element]. This is used to determine whether the |
1499 /// type of the element could be inferred from the types in the super classes. | 1900 /// type of the element could be inferred from the types in the super classes. |
1500 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, | 1901 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, |
1501 AstNode node, AstNode errorLocation, bool isSubclass) { | 1902 AstNode node, AstNode errorLocation, bool isSubclass) { |
1502 assert(!element.isStatic); | 1903 assert(!element.isStatic); |
1503 | 1904 |
1504 FunctionType subType = _elementType(element); | 1905 FunctionType subType = _elementType(element); |
1505 // TODO(vsm): Test for generic | |
1506 FunctionType baseType = _getMemberType(type, element); | 1906 FunctionType baseType = _getMemberType(type, element); |
1507 if (baseType == null) return false; | 1907 if (baseType == null) return false; |
1508 | 1908 |
1509 if (isSubclass && element is PropertyAccessorElement) { | 1909 if (isSubclass && element is PropertyAccessorElement) { |
1510 // Disallow any overriding if the base class defines this member | 1910 // Disallow any overriding if the base class defines this member |
1511 // as a field. We effectively treat fields as final / non-virtual, | 1911 // as a field. We effectively treat fields as final / non-virtual, |
1512 // unless they are explicitly marked as @virtual | 1912 // unless they are explicitly marked as @virtual |
1513 var field = _getMemberField(type, element); | 1913 var field = _getMemberField(type, element); |
1514 if (field != null && !field.isVirtual) { | 1914 if (field != null && !field.isVirtual) { |
1515 _checker._recordMessage( | 1915 _checker._recordMessage( |
1516 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [ | 1916 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [ |
1517 element.enclosingElement.name, | 1917 element.enclosingElement.name, |
1518 element.name, | 1918 element.name, |
1519 subType, | 1919 subType, |
1520 type, | 1920 type, |
1521 baseType | 1921 baseType |
1522 ]); | 1922 ]); |
1523 } | 1923 } |
1524 } | 1924 } |
| 1925 |
1525 if (!rules.isOverrideSubtypeOf(subType, baseType)) { | 1926 if (!rules.isOverrideSubtypeOf(subType, baseType)) { |
1526 ErrorCode errorCode; | 1927 ErrorCode errorCode; |
1527 var parent = errorLocation?.parent; | 1928 var parent = errorLocation?.parent; |
1528 if (errorLocation is ExtendsClause || | 1929 if (errorLocation is ExtendsClause || |
1529 parent is ClassTypeAlias && parent.superclass == errorLocation) { | 1930 parent is ClassTypeAlias && parent.superclass == errorLocation) { |
1530 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE; | 1931 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE; |
1531 } else if (parent is WithClause) { | 1932 } else if (parent is WithClause) { |
1532 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN; | 1933 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN; |
1533 } else { | 1934 } else { |
1534 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE; | 1935 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE; |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1600 } | 2001 } |
1601 | 2002 |
1602 /// If node is a [ClassDeclaration] returns its members, otherwise if node is | 2003 /// If node is a [ClassDeclaration] returns its members, otherwise if node is |
1603 /// a [ClassTypeAlias] this returns an empty list. | 2004 /// a [ClassTypeAlias] this returns an empty list. |
1604 WithClause _withClause(Declaration node) { | 2005 WithClause _withClause(Declaration node) { |
1605 return node is ClassDeclaration | 2006 return node is ClassDeclaration |
1606 ? node.withClause | 2007 ? node.withClause |
1607 : (node as ClassTypeAlias).withClause; | 2008 : (node as ClassTypeAlias).withClause; |
1608 } | 2009 } |
1609 } | 2010 } |
OLD | NEW |