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

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

Powered by Google App Engine
This is Rietveld 408576698