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 InterfaceType _getCovariantUpperBound(TypeSystem rules, ClassElement iface) { | |
57 var upperBound = rules.instantiateToBounds(iface.type) as InterfaceType; | |
58 var typeArgs = upperBound.typeArguments; | |
59 // TODO(jmesserly): remove this. It is a workaround for fuzzy arrows. | |
60 // To prevent extra checks due to fuzzy arrows, we need to instantiate with | |
61 // `Object` rather than `dynamic`. Consider a case like: | |
62 // | |
63 // class C<T> { | |
64 // void forEach(f(T t)) {} | |
65 // } | |
66 // | |
67 // If we try `(dynamic) ~> void <: (T) ~> void` with fuzzy arrows, we will | |
68 // treat `dynamic` as `bottom` and get `(bottom) -> void <: (T) -> void` | |
69 // which indicates that a check is required on the parameter `f`. This check | |
70 // is not sufficient when `T` is `dynamic`, however, because calling a | |
71 // function with a fuzzy arrow type is not safe and requires a dynamic call. | |
72 // See: https://github.com/dart-lang/sdk/issues/29295 | |
73 // | |
74 // For all other values of T, the check is unnecessary: it is sound to pass | |
75 // a function that accepts any Object. | |
76 if (typeArgs.any((t) => t.isDynamic)) { | |
77 var newTypeArgs = typeArgs | |
78 .map((t) => t.isDynamic ? rules.typeProvider.objectType : t) | |
79 .toList(); | |
80 upperBound = iface.type.instantiate(newTypeArgs); | |
81 } | |
82 return upperBound; | |
83 } | |
84 | |
54 DartType _elementType(Element e) { | 85 DartType _elementType(Element e) { |
55 if (e == null) { | 86 if (e == null) { |
56 // Malformed code - just return dynamic. | 87 // Malformed code - just return dynamic. |
57 return DynamicTypeImpl.instance; | 88 return DynamicTypeImpl.instance; |
58 } | 89 } |
59 return (e as dynamic).type; | 90 return (e as dynamic).type; |
60 } | 91 } |
61 | 92 |
62 Element _getKnownElement(Expression expression) { | 93 Element _getKnownElement(Expression expression) { |
63 if (expression is ParenthesizedExpression) { | 94 if (expression is ParenthesizedExpression) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
96 field = setter.variable; | 127 field = setter.variable; |
97 } else { | 128 } else { |
98 return null; | 129 return null; |
99 } | 130 } |
100 if (field.isSynthetic) return null; | 131 if (field.isSynthetic) return null; |
101 return field; | 132 return field; |
102 } | 133 } |
103 | 134 |
104 /// Looks up the declaration that matches [member] in [type] and returns it's | 135 /// Looks up the declaration that matches [member] in [type] and returns it's |
105 /// declared type. | 136 /// declared type. |
106 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => | 137 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) { |
107 _memberTypeGetter(member)(type); | |
108 | |
109 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) { | |
110 String memberName = member.name; | 138 String memberName = member.name; |
111 final isGetter = member is PropertyAccessorElement && member.isGetter; | 139 final isGetter = member is PropertyAccessorElement && member.isGetter; |
112 final isSetter = member is PropertyAccessorElement && member.isSetter; | 140 final isSetter = member is PropertyAccessorElement && member.isSetter; |
113 | 141 |
114 FunctionType f(InterfaceType type) { | 142 ExecutableElement baseMethod; |
115 ExecutableElement baseMethod; | 143 if (member.isPrivate) { |
116 | 144 var subtypeLibrary = member.library; |
117 if (member.isPrivate) { | 145 var baseLibrary = type.element.library; |
118 var subtypeLibrary = member.library; | 146 if (baseLibrary != subtypeLibrary) { |
119 var baseLibrary = type.element.library; | 147 return null; |
120 if (baseLibrary != subtypeLibrary) { | |
121 return null; | |
122 } | |
123 } | 148 } |
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 } | 149 } |
141 | 150 |
142 return f; | 151 try { |
152 if (isGetter) { | |
153 assert(!isSetter); | |
154 // Look for getter or field. | |
155 baseMethod = type.getGetter(memberName); | |
156 } else if (isSetter) { | |
157 baseMethod = type.getSetter(memberName); | |
158 } else { | |
159 baseMethod = type.getMethod(memberName); | |
160 } | |
161 } catch (e) { | |
162 // TODO(sigmund): remove this try-catch block (see issue #48). | |
Leaf
2017/06/28 18:12:29
Looks like this can be removed now?
https://githu
Jennifer Messerly
2017/07/05 20:11:21
Done.
| |
163 } | |
164 if (baseMethod == null || baseMethod.isStatic) return null; | |
165 return baseMethod.type; | |
143 } | 166 } |
144 | 167 |
145 typedef FunctionType _MemberTypeGetter(InterfaceType type); | |
146 | |
147 /// Checks the body of functions and properties. | 168 /// Checks the body of functions and properties. |
148 class CodeChecker extends RecursiveAstVisitor { | 169 class CodeChecker extends RecursiveAstVisitor { |
149 final StrongTypeSystemImpl rules; | 170 final StrongTypeSystemImpl rules; |
150 final TypeProvider typeProvider; | 171 final TypeProvider typeProvider; |
151 final AnalysisErrorListener reporter; | 172 final AnalysisErrorListener reporter; |
152 final AnalysisOptionsImpl _options; | 173 final AnalysisOptionsImpl _options; |
153 _OverrideChecker _overrideChecker; | 174 _OverrideChecker _overrideChecker; |
154 | 175 |
155 bool _failure = false; | 176 bool _failure = false; |
156 bool _hasImplicitCasts; | 177 bool _hasImplicitCasts; |
178 ClassElement _currentClass; | |
179 HashSet<ExecutableElement> _covariantPrivateMembers; | |
157 | 180 |
158 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules, | 181 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules, |
159 AnalysisErrorListener reporter, this._options) | 182 AnalysisErrorListener reporter, this._options) |
160 : typeProvider = typeProvider, | 183 : typeProvider = typeProvider, |
161 rules = rules, | 184 rules = rules, |
162 reporter = reporter { | 185 reporter = reporter { |
163 _overrideChecker = new _OverrideChecker(this); | 186 _overrideChecker = new _OverrideChecker(this); |
164 } | 187 } |
165 | 188 |
166 bool get failure => _failure; | 189 bool get failure => _failure; |
(...skipping 27 matching lines...) Expand all Loading... | |
194 _checkImplicitCast(expr, type); | 217 _checkImplicitCast(expr, type); |
195 } | 218 } |
196 } | 219 } |
197 | 220 |
198 /// Analyzer checks boolean conversions, but we need to check too, because | 221 /// Analyzer checks boolean conversions, but we need to check too, because |
199 /// it uses the default assignability rules that allow `dynamic` and `Object` | 222 /// it uses the default assignability rules that allow `dynamic` and `Object` |
200 /// to be assigned to bool with no message. | 223 /// to be assigned to bool with no message. |
201 void checkBoolean(Expression expr) => | 224 void checkBoolean(Expression expr) => |
202 checkAssignment(expr, typeProvider.boolType); | 225 checkAssignment(expr, typeProvider.boolType); |
203 | 226 |
204 void checkFunctionApplication(InvocationExpression node) { | 227 void _checkFunctionApplication(InvocationExpression node) { |
205 var ft = _getTypeAsCaller(node); | 228 var ft = _getTypeAsCaller(node); |
206 | 229 |
207 if (_isDynamicCall(node, ft)) { | 230 if (_isDynamicCall(node, ft)) { |
208 // If f is Function and this is a method invocation, we should have | 231 // 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. | 232 // gotten an analyzer error, so no need to issue another error. |
210 _recordDynamicInvoke(node, node.function); | 233 _recordDynamicInvoke(node, node.function); |
211 } else { | 234 } else { |
235 _checkFunctionApplicationForCovariance(node); | |
212 checkArgumentList(node.argumentList, ft); | 236 checkArgumentList(node.argumentList, ft); |
213 } | 237 } |
214 } | 238 } |
215 | 239 |
216 DartType getType(TypeAnnotation type) { | 240 DartType getType(TypeAnnotation type) { |
217 return type?.type ?? DynamicTypeImpl.instance; | 241 return type?.type ?? DynamicTypeImpl.instance; |
218 } | 242 } |
219 | 243 |
220 void reset() { | 244 void reset() { |
221 _failure = false; | 245 _failure = false; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
286 default: | 310 default: |
287 assert(false); | 311 assert(false); |
288 } | 312 } |
289 } | 313 } |
290 node.visitChildren(this); | 314 node.visitChildren(this); |
291 } | 315 } |
292 | 316 |
293 @override | 317 @override |
294 void visitClassDeclaration(ClassDeclaration node) { | 318 void visitClassDeclaration(ClassDeclaration node) { |
295 _overrideChecker.check(node); | 319 _overrideChecker.check(node); |
320 var savedClass = _currentClass; | |
vsm
2017/06/26 22:14:20
Can these ever be nested? If not, maybe
assert(_
Jennifer Messerly
2017/07/05 20:11:20
Done.
| |
321 _currentClass = node.element; | |
296 super.visitClassDeclaration(node); | 322 super.visitClassDeclaration(node); |
323 _currentClass = savedClass; | |
297 } | 324 } |
298 | 325 |
299 @override | 326 @override |
300 void visitClassTypeAlias(ClassTypeAlias node) { | 327 void visitClassTypeAlias(ClassTypeAlias node) { |
301 _overrideChecker.check(node); | 328 _overrideChecker.check(node); |
302 super.visitClassTypeAlias(node); | 329 super.visitClassTypeAlias(node); |
303 } | 330 } |
304 | 331 |
305 @override | 332 @override |
306 void visitComment(Comment node) { | 333 void visitComment(Comment node) { |
307 // skip, no need to do typechecking inside comments (they may contain | 334 // skip, no need to do typechecking inside comments (they may contain |
308 // comment references which would require resolution). | 335 // comment references which would require resolution). |
309 } | 336 } |
310 | 337 |
311 @override | 338 @override |
312 void visitCompilationUnit(CompilationUnit node) { | 339 void visitCompilationUnit(CompilationUnit node) { |
313 _hasImplicitCasts = false; | 340 _hasImplicitCasts = false; |
341 _covariantPrivateMembers = new HashSet(); | |
314 node.visitChildren(this); | 342 node.visitChildren(this); |
315 setHasImplicitCasts(node, _hasImplicitCasts); | 343 setHasImplicitCasts(node, _hasImplicitCasts); |
344 setCovariantPrivateMembers(node, _covariantPrivateMembers); | |
316 } | 345 } |
317 | 346 |
318 @override | 347 @override |
319 void visitConditionalExpression(ConditionalExpression node) { | 348 void visitConditionalExpression(ConditionalExpression node) { |
320 checkBoolean(node.condition); | 349 checkBoolean(node.condition); |
321 node.visitChildren(this); | 350 node.visitChildren(this); |
322 } | 351 } |
323 | 352 |
324 /// Check constructor declaration to ensure correct super call placement. | 353 /// Check constructor declaration to ensure correct super call placement. |
325 @override | 354 @override |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
431 @override | 460 @override |
432 void visitForStatement(ForStatement node) { | 461 void visitForStatement(ForStatement node) { |
433 if (node.condition != null) { | 462 if (node.condition != null) { |
434 checkBoolean(node.condition); | 463 checkBoolean(node.condition); |
435 } | 464 } |
436 node.visitChildren(this); | 465 node.visitChildren(this); |
437 } | 466 } |
438 | 467 |
439 @override | 468 @override |
440 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | 469 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
441 checkFunctionApplication(node); | 470 _checkFunctionApplication(node); |
442 node.visitChildren(this); | 471 node.visitChildren(this); |
443 } | 472 } |
444 | 473 |
445 @override | 474 @override |
446 void visitIfStatement(IfStatement node) { | 475 void visitIfStatement(IfStatement node) { |
447 checkBoolean(node.condition); | 476 checkBoolean(node.condition); |
448 node.visitChildren(this); | 477 node.visitChildren(this); |
449 } | 478 } |
450 | 479 |
451 @override | 480 @override |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
558 // | 587 // |
559 // ... from case like: | 588 // ... from case like: |
560 // | 589 // |
561 // SomeType s; | 590 // SomeType s; |
562 // s.someDynamicField(...); // static get, followed by dynamic call. | 591 // s.someDynamicField(...); // static get, followed by dynamic call. |
563 // | 592 // |
564 // The first case is handled here, the second case is handled below when | 593 // The first case is handled here, the second case is handled below when |
565 // we call [checkFunctionApplication]. | 594 // we call [checkFunctionApplication]. |
566 setIsDynamicInvoke(node.methodName, true); | 595 setIsDynamicInvoke(node.methodName, true); |
567 } else { | 596 } else { |
568 checkFunctionApplication(node); | 597 _checkFunctionApplication(node); |
598 _trackPrivateMemberCovariance(target, node.methodName.staticElement); | |
569 } | 599 } |
570 node.visitChildren(this); | 600 node.visitChildren(this); |
571 } | 601 } |
572 | 602 |
573 @override | 603 @override |
574 void visitPostfixExpression(PostfixExpression node) { | 604 void visitPostfixExpression(PostfixExpression node) { |
575 _checkUnary(node.operand, node.operator, node.staticElement); | 605 _checkUnary(node.operand, node.operator, node.staticElement); |
576 node.visitChildren(this); | 606 node.visitChildren(this); |
577 } | 607 } |
578 | 608 |
(...skipping 30 matching lines...) Expand all Loading... | |
609 node.visitChildren(this); | 639 node.visitChildren(this); |
610 } | 640 } |
611 | 641 |
612 @override | 642 @override |
613 void visitReturnStatement(ReturnStatement node) { | 643 void visitReturnStatement(ReturnStatement node) { |
614 _checkReturnOrYield(node.expression, node); | 644 _checkReturnOrYield(node.expression, node); |
615 node.visitChildren(this); | 645 node.visitChildren(this); |
616 } | 646 } |
617 | 647 |
618 @override | 648 @override |
649 void visitSimpleIdentifier(SimpleIdentifier node) { | |
650 _trackPrivateMemberCovariance(null, node.staticElement); | |
651 } | |
652 | |
653 @override | |
619 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { | 654 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
620 var element = node.staticElement; | 655 var element = node.staticElement; |
621 if (element != null) { | 656 if (element != null) { |
622 var type = resolutionMap.staticElementForConstructorReference(node).type; | 657 var type = resolutionMap.staticElementForConstructorReference(node).type; |
623 checkArgumentList(node.argumentList, type); | 658 checkArgumentList(node.argumentList, type); |
624 } | 659 } |
625 node.visitChildren(this); | 660 node.visitChildren(this); |
626 } | 661 } |
627 | 662 |
628 @override | 663 @override |
(...skipping 28 matching lines...) Expand all Loading... | |
657 variableElement.kind == ElementKind.FIELD) { | 692 variableElement.kind == ElementKind.FIELD) { |
658 _validateTopLevelInitializer(variableElement.name, node.initializer); | 693 _validateTopLevelInitializer(variableElement.name, node.initializer); |
659 } | 694 } |
660 } | 695 } |
661 return super.visitVariableDeclaration(node); | 696 return super.visitVariableDeclaration(node); |
662 } | 697 } |
663 | 698 |
664 @override | 699 @override |
665 void visitVariableDeclarationList(VariableDeclarationList node) { | 700 void visitVariableDeclarationList(VariableDeclarationList node) { |
666 TypeAnnotation type = node.type; | 701 TypeAnnotation type = node.type; |
667 if (type == null) { | 702 |
668 // No checks are needed when the type is var. Although internally the | 703 for (VariableDeclaration variable in node.variables) { |
669 // typing rules may have inferred a more precise type for the variable | 704 var initializer = variable.initializer; |
670 // based on the initializer. | 705 if (initializer != null) { |
671 } else { | 706 if (type != null) { |
672 for (VariableDeclaration variable in node.variables) { | |
673 var initializer = variable.initializer; | |
674 if (initializer != null) { | |
675 checkAssignment(initializer, type.type); | 707 checkAssignment(initializer, type.type); |
708 } else { | |
709 // Most checks are not needed when the type is var, however, we still | |
710 // need to check for unsafe function types from the initialize due | |
711 // to covariance. | |
712 var toType = _getDefiniteType(initializer); | |
713 if (toType is FunctionType) { | |
714 _checkImplicitCastForCovariance(initializer, toType); | |
715 } | |
676 } | 716 } |
677 } | 717 } |
678 } | 718 } |
719 | |
679 node.visitChildren(this); | 720 node.visitChildren(this); |
680 } | 721 } |
681 | 722 |
682 @override | 723 @override |
683 void visitWhileStatement(WhileStatement node) { | 724 void visitWhileStatement(WhileStatement node) { |
684 checkBoolean(node.condition); | 725 checkBoolean(node.condition); |
685 node.visitChildren(this); | 726 node.visitChildren(this); |
686 } | 727 } |
687 | 728 |
688 @override | 729 @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: | 763 // back to it. So these two implicit casts are equivalent: |
723 // | 764 // |
724 // y = /*implicit cast*/(y + 42); | 765 // y = /*implicit cast*/(y + 42); |
725 // /*implicit assignment cast*/y += 42; | 766 // /*implicit assignment cast*/y += 42; |
726 // | 767 // |
727 _checkImplicitCast(expr.leftHandSide, lhsType, | 768 _checkImplicitCast(expr.leftHandSide, lhsType, |
728 from: returnType, opAssign: true); | 769 from: returnType, opAssign: true); |
729 } | 770 } |
730 } | 771 } |
731 | 772 |
732 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { | 773 void _checkFieldAccess( |
733 if (field.staticElement == null && | 774 AstNode node, Expression target, SimpleIdentifier field) { |
734 !typeProvider.isObjectMember(field.name)) { | 775 var element = field.staticElement; |
776 _trackPrivateMemberCovariance(target, element); | |
777 if (element == null && !typeProvider.isObjectMember(field.name)) { | |
735 _recordDynamicInvoke(node, target); | 778 _recordDynamicInvoke(node, target); |
736 } | 779 } |
737 node.visitChildren(this); | 780 node.visitChildren(this); |
738 } | 781 } |
739 | 782 |
740 /// Checks if an implicit cast of [expr] from [from] type to [to] type is | 783 /// Checks if an implicit cast of [expr] from [from] type to [to] type is |
741 /// needed, and if so records it. | 784 /// needed, and if so records it. |
742 /// | 785 /// |
743 /// If [from] is omitted, uses the static type of [expr]. | 786 /// If [from] is omitted, uses the static type of [expr]. |
744 /// | 787 /// |
745 /// If [expr] does not require an implicit cast because it is not related to | 788 /// If [expr] does not require an implicit cast because it is not related to |
746 /// [to] or is already a subtype of it, does nothing. | 789 /// [to] or is already a subtype of it, does nothing. |
747 void _checkImplicitCast(Expression expr, DartType to, | 790 void _checkImplicitCast(Expression expr, DartType to, |
748 {DartType from, bool opAssign: false}) { | 791 {DartType from, bool opAssign: false}) { |
749 from ??= _getDefiniteType(expr); | 792 from ??= _getDefiniteType(expr); |
750 | 793 |
751 if (_needsImplicitCast(expr, to, from: from) == true) { | 794 if (_needsImplicitCast(expr, to, from: from) == true) { |
752 _recordImplicitCast(expr, to, from: from, opAssign: opAssign); | 795 _recordImplicitCast(expr, to, from: from, opAssign: opAssign); |
796 } else if (to is FunctionType) { | |
797 _checkImplicitCastForCovariance(expr, to); | |
753 } | 798 } |
754 } | 799 } |
755 | 800 |
756 /// Checks if the assignment is valid with respect to non-nullable types. | 801 /// Checks if the assignment is valid with respect to non-nullable types. |
757 /// Returns `false` if a nullable expression is assigned to a variable of | 802 /// Returns `false` if a nullable expression is assigned to a variable of |
758 /// non-nullable type and `true` otherwise. | 803 /// non-nullable type and `true` otherwise. |
759 bool _checkNonNullAssignment( | 804 bool _checkNonNullAssignment( |
760 Expression expression, DartType to, DartType from) { | 805 Expression expression, DartType to, DartType from) { |
761 if (rules.isNonNullableType(to) && rules.isNullableType(from)) { | 806 if (rules.isNonNullableType(to) && rules.isNullableType(from)) { |
762 _recordMessage( | 807 _recordMessage( |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
819 // /*implicit assignment cast*/y++; | 864 // /*implicit assignment cast*/y++; |
820 // | 865 // |
821 _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true); | 866 _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true); |
822 } | 867 } |
823 } | 868 } |
824 } | 869 } |
825 | 870 |
826 DartType _getDefiniteType(Expression expr) => | 871 DartType _getDefiniteType(Expression expr) => |
827 getDefiniteType(expr, rules, typeProvider); | 872 getDefiniteType(expr, rules, typeProvider); |
828 | 873 |
874 /// This gets the return type that is safe to rely on, given that we | |
Leaf
2017/06/28 18:12:29
What does "This gets the return type" mean here?
Jennifer Messerly
2017/07/05 20:11:20
I've improved this comment, let me know if it help
| |
875 /// might be making a call on an (unsafe) covariant interface type. | |
876 /// | |
877 /// For example: | |
878 /// | |
879 /// typedef F<T>(T t); | |
880 /// class C<T> { | |
881 /// F<T> f; | |
882 /// C(this.f); | |
883 /// } | |
884 /// main() { | |
885 /// C<Object> c = new C<int>((int x) => x + 42)); | |
886 /// F<Object> f = c.f; // need an implicit cast here. | |
887 /// f('hello'); | |
888 /// } | |
889 /// | |
890 void _checkImplicitCastForCovariance(Expression node, FunctionType to, | |
Leaf
2017/06/28 18:12:29
Some high level comments here.
My original thin
Jennifer Messerly
2017/07/05 20:11:20
Did a big refactor to simplify this. We always add
| |
891 {DartType target}) { | |
892 while (node is NamedExpression) { | |
893 node = (node as NamedExpression).expression; | |
894 } | |
895 while (node is ParenthesizedExpression) { | |
896 node = (node as ParenthesizedExpression).expression; | |
897 } | |
898 | |
899 Element member; | |
900 bool isTearOff = false; | |
901 bool isMethodInvokeTarget = false; | |
902 if (node is PropertyAccess) { | |
903 target = node.realTarget.staticType; | |
904 member = node.propertyName.staticElement; | |
905 isTearOff = member is MethodElement; | |
906 if (target == null) return; | |
907 } else if (node is PrefixedIdentifier) { | |
908 target = node.prefix.staticType; | |
909 member = node.staticElement; | |
910 isTearOff = member is MethodElement; | |
911 if (target == null) return; | |
912 } else if (node is SimpleIdentifier) { | |
913 member = node.staticElement; | |
914 var parent = node.parent; | |
915 isMethodInvokeTarget = | |
916 parent is MethodInvocation && parent.methodName == node; | |
917 isTearOff = member is MethodElement; | |
918 } else if (node is MethodInvocation) { | |
919 target = node.realTarget?.staticType; | |
920 member = node.methodName.staticElement; | |
921 } else if (node is FunctionExpressionInvocation) { | |
922 member = node.staticElement; | |
923 } | |
924 | |
925 // If we're calling an instance method or getter, then we | |
926 // want to check the result type. | |
927 // | |
928 // We intentionally ignore method tear-offs, because those methods have | |
Leaf
2017/06/28 18:12:30
This comment confused me, until I read the code be
Jennifer Messerly
2017/07/05 20:11:20
(this is gone in the new code)
| |
929 // covariance checks for their parameters inside the method. | |
930 if (member is ExecutableElement && _isInstanceMember(member)) { | |
931 if (target == null) { | |
932 if (!_inCurrentClass(member)) return; | |
933 // Use implicit `this` as the target. | |
934 target = _currentClass?.type; | |
935 } | |
936 | |
937 if (target is InterfaceType && target.typeArguments.isNotEmpty) { | |
Leaf
2017/06/28 18:12:29
Not sure that this is sufficient? Consider:
clas
Jennifer Messerly
2017/07/05 20:11:20
in this case, `c` has type `C<Object>` and so it w
| |
938 // Get the lower bound of the declared return type (e.g. `F<Null>`) and | |
939 // see if it can be assigned to the expected type (e.g. `F<Object>`). | |
Leaf
2017/06/28 18:12:29
Clever... :)
Jennifer Messerly
2017/07/05 20:11:20
Acknowledged.
| |
940 // | |
941 // That way we can tell if any lower `T` will work or not. | |
942 var classType = target.element.type; | |
943 var classLowerBound = classType.instantiate(new List.filled( | |
944 classType.typeParameters.length, typeProvider.nullType)); | |
945 var memberLowerBound = _lookUpMember(classLowerBound, member).type; | |
946 | |
947 // If this is a tear-off, we can ignore the parameters (they're already | |
948 // checked) but we still need to check the return type. | |
949 var expectedType = isTearOff ? to.returnType : to; | |
950 if (!rules.isSubtypeOf(memberLowerBound.returnType, expectedType)) { | |
951 // We can't put an `as` expression inside of a MethodInvocation node, | |
952 // so we use the special cast annotation to indicate this. | |
953 if (isMethodInvokeTarget) { | |
954 setImplicitOperationCast(node, to); | |
955 } else { | |
956 setImplicitCast(node, to); | |
957 } | |
958 _hasImplicitCasts = true; | |
959 } | |
960 } | |
961 } | |
962 } | |
963 | |
964 /// Similar to [_checkImplicitCastForCovariance] but make sure the function | |
965 /// type we're applying arguments to is valid. | |
966 /// | |
967 /// For example: | |
968 /// | |
969 /// typedef F<T>(T t); | |
970 /// class C<T> { | |
971 /// F<T> f; | |
972 /// C(this.f); | |
973 /// } | |
974 /// main() { | |
975 /// C<Object> c = new C<int>((int x) => x + 42)); | |
976 /// Object obj = c.f; // no cast. | |
977 /// c.f('hello'); // implicit cast | |
978 /// } | |
979 void _checkFunctionApplicationForCovariance(InvocationExpression node) { | |
980 DartType target; | |
981 Element member; | |
982 if (node is MethodInvocation) { | |
983 target = node.realTarget?.staticType; | |
984 member = node.methodName.staticElement; | |
985 } else if (node is FunctionExpressionInvocation) { | |
986 member = node.staticElement; | |
987 } | |
988 // Caller side checks will take care of methods. | |
989 if (member is MethodElement) return; | |
990 | |
991 var type = _getDefiniteType(node.function); | |
992 if (type is FunctionType) { | |
993 _checkImplicitCastForCovariance(node.function, type, target: target); | |
994 } | |
995 } | |
996 | |
997 /// We can eliminate covariance checks on private members if they are only | |
998 /// accessed through something with a known generic type, such as `this`. | |
999 /// | |
1000 /// For these expressions, we will know the generic parameters exactly: | |
1001 /// | |
1002 /// - this | |
1003 /// - super | |
1004 /// - non-factory instance creation | |
1005 /// | |
1006 /// For example: | |
1007 /// | |
1008 /// class C<T> { | |
1009 /// T _t; | |
1010 /// } | |
1011 /// class D<T> extends C<T> { | |
1012 /// method<S extends T>(T t, C<T> c) { | |
1013 /// // implicit cast: t as T; | |
1014 /// // implicit cast: c as C<T>; | |
1015 /// | |
1016 /// // These do not need further checks. The type parameter `T` for | |
1017 /// // `this` must be the same as our `T` | |
1018 /// this._t = t; | |
1019 /// super._t = t; | |
1020 /// new C<T>()._t = t; // non-factory | |
1021 /// | |
1022 /// // This needs further checks. The type of `c` could be `C<S>` for | |
1023 /// // some `S <: T`. | |
1024 /// c._t = t; | |
1025 /// // factory statically returns `C<T>`, dynamically returns `C<S>`. | |
1026 /// new F<T, S>()._t = t; | |
1027 /// } | |
1028 /// } | |
1029 /// class F<T, S extends T> extends C<T> { | |
1030 /// factory F() => new C<S>(); | |
1031 /// } | |
1032 /// | |
1033 void _trackPrivateMemberCovariance(Expression target, Element e) { | |
1034 if (e is ExecutableElement && e.isPrivate && _isInstanceMember(e)) { | |
1035 if (target == null) return; // implicit this or invalid code. | |
1036 if (target is ThisExpression || target is SuperExpression) return; | |
1037 if (target is InstanceCreationExpression && | |
1038 target.staticElement?.isFactory == false) { | |
1039 return; | |
1040 } | |
1041 if (e is PropertyAccessorElement && e.isGetter) return; | |
1042 | |
1043 var type = target.staticType; | |
1044 if (type is InterfaceType && type.typeArguments.isNotEmpty) { | |
1045 _covariantPrivateMembers | |
1046 .add(e is Member ? (e as Member).baseElement : e); | |
1047 } | |
1048 } | |
1049 } | |
1050 | |
1051 bool _isInstanceMember(ExecutableElement e) => | |
1052 !e.isStatic && | |
1053 (e is MethodElement || | |
1054 e is PropertyAccessorElement && e.variable is FieldElement); | |
1055 | |
1056 bool _inCurrentClass(ExecutableElement e) { | |
1057 if (_currentClass == null) return false; | |
1058 var match = _lookUpMember(_currentClass.type, e); | |
1059 return e.enclosingElement == match.enclosingElement; | |
1060 } | |
1061 | |
1062 ExecutableElement _lookUpMember(InterfaceType type, ExecutableElement e) { | |
1063 var name = e.name; | |
1064 var library = e.library; | |
1065 return e is PropertyAccessorElement | |
1066 ? (e.isGetter | |
1067 ? type.lookUpInheritedGetter(name, library: library) | |
1068 : type.lookUpInheritedSetter(name, library: library)) | |
1069 : type.lookUpInheritedMethod(name, library: library); | |
1070 } | |
1071 | |
829 /// Gets the expected return type of the given function [body], either from | 1072 /// Gets the expected return type of the given function [body], either from |
830 /// a normal return/yield, or from a yield*. | 1073 /// a normal return/yield, or from a yield*. |
831 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | 1074 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
832 FunctionType functionType; | 1075 FunctionType functionType; |
833 var parent = body.parent; | 1076 var parent = body.parent; |
834 if (parent is Declaration) { | 1077 if (parent is Declaration) { |
835 functionType = _elementType(parent.element); | 1078 functionType = _elementType(parent.element); |
836 } else { | 1079 } else { |
837 assert(parent is FunctionExpression); | 1080 assert(parent is FunctionExpression); |
838 functionType = | 1081 functionType = |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1031 errorCode = StrongModeCode.DYNAMIC_CAST; | 1274 errorCode = StrongModeCode.DYNAMIC_CAST; |
1032 } else if (parent is VariableDeclaration && parent.initializer == expr) { | 1275 } else if (parent is VariableDeclaration && parent.initializer == expr) { |
1033 errorCode = StrongModeCode.ASSIGNMENT_CAST; | 1276 errorCode = StrongModeCode.ASSIGNMENT_CAST; |
1034 } else { | 1277 } else { |
1035 errorCode = opAssign | 1278 errorCode = opAssign |
1036 ? StrongModeCode.DOWN_CAST_IMPLICIT_ASSIGN | 1279 ? StrongModeCode.DOWN_CAST_IMPLICIT_ASSIGN |
1037 : StrongModeCode.DOWN_CAST_IMPLICIT; | 1280 : StrongModeCode.DOWN_CAST_IMPLICIT; |
1038 } | 1281 } |
1039 _recordMessage(expr, errorCode, [from, to]); | 1282 _recordMessage(expr, errorCode, [from, to]); |
1040 if (opAssign) { | 1283 if (opAssign) { |
1041 setImplicitAssignmentCast(expr, to); | 1284 setImplicitOperationCast(expr, to); |
1042 } else { | 1285 } else { |
1043 setImplicitCast(expr, to); | 1286 setImplicitCast(expr, to); |
1044 } | 1287 } |
1045 _hasImplicitCasts = true; | 1288 _hasImplicitCasts = true; |
1046 } | 1289 } |
1047 | 1290 |
1048 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { | 1291 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { |
1049 // Compute the right severity taking the analysis options into account. | 1292 // 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 | 1293 // We construct a dummy error to make the common case where we end up |
1051 // ignoring the strong mode message cheaper. | 1294 // ignoring the strong mode message cheaper. |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1242 | 1485 |
1243 void check(Declaration node) { | 1486 void check(Declaration node) { |
1244 var element = | 1487 var element = |
1245 resolutionMap.elementDeclaredByDeclaration(node) as ClassElement; | 1488 resolutionMap.elementDeclaredByDeclaration(node) as ClassElement; |
1246 if (element.type.isObject) { | 1489 if (element.type.isObject) { |
1247 return; | 1490 return; |
1248 } | 1491 } |
1249 _checkSuperOverrides(node, element); | 1492 _checkSuperOverrides(node, element); |
1250 _checkMixinApplicationOverrides(node, element); | 1493 _checkMixinApplicationOverrides(node, element); |
1251 _checkAllInterfaceOverrides(node, element); | 1494 _checkAllInterfaceOverrides(node, element); |
1495 _checkForCovariantGenerics(node, element); | |
1496 } | |
1497 | |
1498 void _checkForCovariantGenerics(Declaration node, ClassElement element) { | |
Leaf
2017/06/28 18:12:29
These functions really need some documentation at
Jennifer Messerly
2017/07/05 20:11:21
Done.
| |
1499 // Find all generic interfaces that are implemented, if any. | |
1500 // | |
1501 // Because we're going to instantiate these to their bounds, we don't have | |
1502 // to track type parameters. | |
1503 var allCovariant = _findCovariantSupertypes(element.type); | |
1504 if (allCovariant.isEmpty) return; | |
1505 | |
1506 // For each most-derived concrete member of the class, (which may be from a | |
1507 // superclasses or mixin), check it against all generic superinterfaces that | |
1508 // it was not already checked against. | |
1509 var seenConcreteMembers = new HashSet<String>(); | |
1510 var members = _getConcreteMembers(element.type, seenConcreteMembers); | |
1511 | |
1512 // For members on this class, check them against all interfaces. | |
1513 var checks = _findCovariantChecks(members, allCovariant); | |
1514 setClassCovariantParameters(node, checks); | |
1515 | |
1516 checks = _findSuperclassCovariantChecks( | |
1517 element, allCovariant, seenConcreteMembers); | |
1518 setSuperclassCovariantParameters(node, checks); | |
1519 } | |
1520 | |
1521 Set<Element> _findSuperclassCovariantChecks(ClassElement element, | |
Leaf
2017/06/28 18:12:30
Ok, I need some in person guidance on this code, I
Jennifer Messerly
2017/07/05 20:11:20
I've added a lot of comments -- do they help?
| |
1522 Set<ClassElement> allCovariant, HashSet<String> seenConcreteMembers) { | |
1523 var visited = new HashSet<ClassElement>()..add(element); | |
1524 var superChecks = new Set<Element>(); | |
1525 var existingChecks = new HashSet<Element>(); | |
1526 | |
1527 void visitImmediateSuper(InterfaceType type) { | |
1528 // For members of mixins/supertypes, check them against new interfaces, | |
1529 // and also record any existing checks they already had. | |
1530 var oldCovariant = _findCovariantSupertypes(type); | |
1531 var newCovariant = allCovariant.difference(oldCovariant); | |
1532 if (newCovariant.isEmpty) return; | |
1533 | |
1534 void visitSuper(InterfaceType type) { | |
1535 var element = type.element; | |
1536 if (visited.add(element)) { | |
1537 var members = _getConcreteMembers(type, seenConcreteMembers); | |
1538 _findCovariantChecks(members, newCovariant, superChecks); | |
1539 _findCovariantChecks(members, oldCovariant, existingChecks); | |
1540 element.mixins.reversed.forEach(visitSuper); | |
1541 var s = element.supertype; | |
1542 if (s != null) visitSuper(s); | |
1543 } | |
1544 } | |
1545 | |
1546 visitSuper(type); | |
1547 } | |
1548 | |
1549 element.mixins.reversed.forEach(visitImmediateSuper); | |
1550 var s = element.supertype; | |
1551 if (s != null) visitImmediateSuper(s); | |
1552 | |
1553 superChecks.removeAll(existingChecks); | |
1554 return superChecks; | |
1555 } | |
1556 | |
1557 /// Gets all concrete instance members declared on this type, skipping already | |
1558 /// [seenConcreteMembers] and adding any found ones to it. | |
1559 static List<ExecutableElement> _getConcreteMembers( | |
1560 InterfaceType type, HashSet<String> seenConcreteMembers) { | |
1561 var members = <ExecutableElement>[]; | |
1562 for (var declaredMembers in [type.accessors, type.methods]) { | |
1563 for (var member in declaredMembers) { | |
1564 // We only visit each most derived concrete member. | |
1565 // To avoid visiting an overridden superclass member, we skip members | |
1566 // we've seen, and visit starting from the class, then mixins in | |
1567 // reverse order, then superclasses. | |
1568 if (!member.isStatic && | |
1569 !member.isAbstract && | |
1570 seenConcreteMembers.add(member.name)) { | |
1571 members.add(member); | |
1572 } | |
1573 } | |
1574 } | |
1575 return members; | |
1576 } | |
1577 | |
1578 Set<Element> _findCovariantChecks(Iterable<ExecutableElement> members, | |
1579 Iterable<ClassElement> covariantInterfaces, | |
1580 [Set<Element> covariantChecks]) { | |
1581 covariantChecks ??= new Set(); | |
1582 if (members.isEmpty) return covariantChecks; | |
1583 | |
1584 for (var iface in covariantInterfaces) { | |
1585 var upperBound = _getCovariantUpperBound(rules, iface); | |
1586 | |
1587 for (var m in members) { | |
1588 var superMemberType = _getMemberType(upperBound, m); | |
1589 if (superMemberType == null) continue; | |
1590 | |
1591 void addCheck(Element e) { | |
1592 covariantChecks.add(e is Member ? e.baseElement : e); | |
1593 } | |
1594 | |
1595 rules.visitCovariantParameters( | |
1596 m.type, superMemberType, addCheck, addCheck); | |
1597 } | |
1598 } | |
1599 return covariantChecks; | |
1600 } | |
1601 | |
1602 static Set<ClassElement> _findCovariantSupertypes(InterfaceType type) { | |
1603 var visited = new HashSet<ClassElement>(); | |
1604 var genericSupertypes = new Set<ClassElement>(); | |
1605 | |
1606 void visitTypeAndSupertypes(InterfaceType type) { | |
1607 var element = type.element; | |
1608 if (visited.add(element)) { | |
1609 if (element.typeParameters.isNotEmpty) { | |
1610 genericSupertypes.add(element); | |
1611 } | |
1612 var supertype = element.supertype; | |
1613 if (supertype != null) visitTypeAndSupertypes(supertype); | |
1614 element.mixins.forEach(visitTypeAndSupertypes); | |
1615 element.interfaces.forEach(visitTypeAndSupertypes); | |
1616 } | |
1617 } | |
1618 | |
1619 visitTypeAndSupertypes(type); | |
1620 | |
1621 return genericSupertypes; | |
1252 } | 1622 } |
1253 | 1623 |
1254 /// Checks that implementations correctly override all reachable interfaces. | 1624 /// Checks that implementations correctly override all reachable interfaces. |
1255 /// In particular, we need to check these overrides for the definitions in | 1625 /// 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 | 1626 /// the class itself and each its superclasses. If a superclass is not |
1257 /// abstract, then we can skip its transitive interfaces. For example, in: | 1627 /// abstract, then we can skip its transitive interfaces. For example, in: |
1258 /// | 1628 /// |
1259 /// B extends C implements G | 1629 /// B extends C implements G |
1260 /// A extends B with E, F implements H, I | 1630 /// A extends B with E, F implements H, I |
1261 /// | 1631 /// |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1360 /// Check that individual methods and fields in [subType] correctly override | 1730 /// Check that individual methods and fields in [subType] correctly override |
1361 /// the declarations in [baseType]. | 1731 /// the declarations in [baseType]. |
1362 /// | 1732 /// |
1363 /// The [errorLocation] node indicates where errors are reported, see | 1733 /// The [errorLocation] node indicates where errors are reported, see |
1364 /// [_checkSingleOverride] for more details. | 1734 /// [_checkSingleOverride] for more details. |
1365 /// | 1735 /// |
1366 /// The set [seen] is used to avoid reporting overrides more than once. It | 1736 /// 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 | 1737 /// 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 | 1738 /// types in a class hierarchy. Errors are reported only the first time an |
1369 /// invalid override involving a specific member is encountered. | 1739 /// invalid override involving a specific member is encountered. |
1370 _checkIndividualOverridesFromType( | 1740 void _checkIndividualOverridesFromType( |
1371 InterfaceType subType, | 1741 InterfaceType subType, |
1372 InterfaceType baseType, | 1742 InterfaceType baseType, |
1373 AstNode errorLocation, | 1743 AstNode errorLocation, |
1374 Set<String> seen, | 1744 Set<String> seen, |
1375 bool isSubclass) { | 1745 bool isSubclass) { |
1376 void checkHelper(ExecutableElement e) { | 1746 void checkHelper(ExecutableElement e) { |
1377 if (e.isStatic) return; | 1747 if (e.isStatic) return; |
1378 if (seen.contains(e.name)) return; | 1748 if (seen.contains(e.name)) return; |
1379 if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) { | 1749 if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) { |
1380 seen.add(e.name); | 1750 seen.add(e.name); |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1495 /// ^ | 1865 /// ^ |
1496 /// | 1866 /// |
1497 /// When checking for overrides from a type and it's super types, [node] is | 1867 /// 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 | 1868 /// 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. | 1869 /// type of the element could be inferred from the types in the super classes. |
1500 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, | 1870 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, |
1501 AstNode node, AstNode errorLocation, bool isSubclass) { | 1871 AstNode node, AstNode errorLocation, bool isSubclass) { |
1502 assert(!element.isStatic); | 1872 assert(!element.isStatic); |
1503 | 1873 |
1504 FunctionType subType = _elementType(element); | 1874 FunctionType subType = _elementType(element); |
1505 // TODO(vsm): Test for generic | |
1506 FunctionType baseType = _getMemberType(type, element); | 1875 FunctionType baseType = _getMemberType(type, element); |
1507 if (baseType == null) return false; | 1876 if (baseType == null) return false; |
1508 | 1877 |
1509 if (isSubclass && element is PropertyAccessorElement) { | 1878 if (isSubclass && element is PropertyAccessorElement) { |
1510 // Disallow any overriding if the base class defines this member | 1879 // Disallow any overriding if the base class defines this member |
1511 // as a field. We effectively treat fields as final / non-virtual, | 1880 // as a field. We effectively treat fields as final / non-virtual, |
1512 // unless they are explicitly marked as @virtual | 1881 // unless they are explicitly marked as @virtual |
1513 var field = _getMemberField(type, element); | 1882 var field = _getMemberField(type, element); |
1514 if (field != null && !field.isVirtual) { | 1883 if (field != null && !field.isVirtual) { |
1515 _checker._recordMessage( | 1884 _checker._recordMessage( |
1516 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [ | 1885 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [ |
1517 element.enclosingElement.name, | 1886 element.enclosingElement.name, |
1518 element.name, | 1887 element.name, |
1519 subType, | 1888 subType, |
1520 type, | 1889 type, |
1521 baseType | 1890 baseType |
1522 ]); | 1891 ]); |
1523 } | 1892 } |
1524 } | 1893 } |
1894 | |
1525 if (!rules.isOverrideSubtypeOf(subType, baseType)) { | 1895 if (!rules.isOverrideSubtypeOf(subType, baseType)) { |
1526 ErrorCode errorCode; | 1896 ErrorCode errorCode; |
1527 var parent = errorLocation?.parent; | 1897 var parent = errorLocation?.parent; |
1528 if (errorLocation is ExtendsClause || | 1898 if (errorLocation is ExtendsClause || |
1529 parent is ClassTypeAlias && parent.superclass == errorLocation) { | 1899 parent is ClassTypeAlias && parent.superclass == errorLocation) { |
1530 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE; | 1900 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE; |
1531 } else if (parent is WithClause) { | 1901 } else if (parent is WithClause) { |
1532 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN; | 1902 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN; |
1533 } else { | 1903 } else { |
1534 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE; | 1904 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE; |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1600 } | 1970 } |
1601 | 1971 |
1602 /// If node is a [ClassDeclaration] returns its members, otherwise if node is | 1972 /// If node is a [ClassDeclaration] returns its members, otherwise if node is |
1603 /// a [ClassTypeAlias] this returns an empty list. | 1973 /// a [ClassTypeAlias] this returns an empty list. |
1604 WithClause _withClause(Declaration node) { | 1974 WithClause _withClause(Declaration node) { |
1605 return node is ClassDeclaration | 1975 return node is ClassDeclaration |
1606 ? node.withClause | 1976 ? node.withClause |
1607 : (node as ClassTypeAlias).withClause; | 1977 : (node as ClassTypeAlias).withClause; |
1608 } | 1978 } |
1609 } | 1979 } |
OLD | NEW |