Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(188)

Side by Side Diff: pkg/analyzer/lib/src/task/strong/checker.dart

Issue 2954523002: fix #27259, implement covariance checking for strong mode and DDC (Closed)
Patch Set: rebase Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698