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

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: add more comments 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 ClassElement _currentClass;
166 HashSet<ExecutableElement> _covariantPrivateMembers;
157 167
158 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules, 168 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules,
159 AnalysisErrorListener reporter, this._options) 169 AnalysisErrorListener reporter, this._options)
160 : typeProvider = typeProvider, 170 : typeProvider = typeProvider,
161 rules = rules, 171 rules = rules,
162 reporter = reporter { 172 reporter = reporter {
163 _overrideChecker = new _OverrideChecker(this); 173 _overrideChecker = new _OverrideChecker(this);
164 } 174 }
165 175
166 bool get failure => _failure; 176 bool get failure => _failure;
(...skipping 27 matching lines...) Expand all
194 _checkImplicitCast(expr, type); 204 _checkImplicitCast(expr, type);
195 } 205 }
196 } 206 }
197 207
198 /// Analyzer checks boolean conversions, but we need to check too, because 208 /// Analyzer checks boolean conversions, but we need to check too, because
199 /// it uses the default assignability rules that allow `dynamic` and `Object` 209 /// it uses the default assignability rules that allow `dynamic` and `Object`
200 /// to be assigned to bool with no message. 210 /// to be assigned to bool with no message.
201 void checkBoolean(Expression expr) => 211 void checkBoolean(Expression expr) =>
202 checkAssignment(expr, typeProvider.boolType); 212 checkAssignment(expr, typeProvider.boolType);
203 213
204 void checkFunctionApplication(InvocationExpression node) { 214 void _checkFunctionApplication(InvocationExpression node) {
205 var ft = _getTypeAsCaller(node); 215 var ft = _getTypeAsCaller(node);
206 216
207 if (_isDynamicCall(node, ft)) { 217 if (_isDynamicCall(node, ft)) {
208 // If f is Function and this is a method invocation, we should have 218 // 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. 219 // gotten an analyzer error, so no need to issue another error.
210 _recordDynamicInvoke(node, node.function); 220 _recordDynamicInvoke(node, node.function);
211 } else { 221 } else {
212 checkArgumentList(node.argumentList, ft); 222 checkArgumentList(node.argumentList, ft);
213 } 223 }
214 } 224 }
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 default: 296 default:
287 assert(false); 297 assert(false);
288 } 298 }
289 } 299 }
290 node.visitChildren(this); 300 node.visitChildren(this);
291 } 301 }
292 302
293 @override 303 @override
294 void visitClassDeclaration(ClassDeclaration node) { 304 void visitClassDeclaration(ClassDeclaration node) {
295 _overrideChecker.check(node); 305 _overrideChecker.check(node);
296 super.visitClassDeclaration(node); 306 assert(_currentClass == null);
307 _currentClass = node.element;
308 node.members.accept(this);
309 _currentClass = null;
297 } 310 }
298 311
299 @override 312 @override
300 void visitClassTypeAlias(ClassTypeAlias node) { 313 void visitClassTypeAlias(ClassTypeAlias node) {
301 _overrideChecker.check(node); 314 _overrideChecker.check(node);
302 super.visitClassTypeAlias(node); 315 super.visitClassTypeAlias(node);
303 } 316 }
304 317
305 @override 318 @override
306 void visitComment(Comment node) { 319 void visitComment(Comment node) {
307 // skip, no need to do typechecking inside comments (they may contain 320 // skip, no need to do typechecking inside comments (they may contain
308 // comment references which would require resolution). 321 // comment references which would require resolution).
309 } 322 }
310 323
311 @override 324 @override
312 void visitCompilationUnit(CompilationUnit node) { 325 void visitCompilationUnit(CompilationUnit node) {
313 _hasImplicitCasts = false; 326 _hasImplicitCasts = false;
327 _covariantPrivateMembers = new HashSet();
314 node.visitChildren(this); 328 node.visitChildren(this);
315 setHasImplicitCasts(node, _hasImplicitCasts); 329 setHasImplicitCasts(node, _hasImplicitCasts);
330 setCovariantPrivateMembers(node, _covariantPrivateMembers);
316 } 331 }
317 332
318 @override 333 @override
319 void visitConditionalExpression(ConditionalExpression node) { 334 void visitConditionalExpression(ConditionalExpression node) {
320 checkBoolean(node.condition); 335 checkBoolean(node.condition);
321 node.visitChildren(this); 336 node.visitChildren(this);
322 } 337 }
323 338
324 /// Check constructor declaration to ensure correct super call placement. 339 /// Check constructor declaration to ensure correct super call placement.
325 @override 340 @override
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 @override 446 @override
432 void visitForStatement(ForStatement node) { 447 void visitForStatement(ForStatement node) {
433 if (node.condition != null) { 448 if (node.condition != null) {
434 checkBoolean(node.condition); 449 checkBoolean(node.condition);
435 } 450 }
436 node.visitChildren(this); 451 node.visitChildren(this);
437 } 452 }
438 453
439 @override 454 @override
440 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { 455 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
441 checkFunctionApplication(node); 456 _checkFunctionApplication(node);
442 node.visitChildren(this); 457 node.visitChildren(this);
443 } 458 }
444 459
445 @override 460 @override
446 void visitIfStatement(IfStatement node) { 461 void visitIfStatement(IfStatement node) {
447 checkBoolean(node.condition); 462 checkBoolean(node.condition);
448 node.visitChildren(this); 463 node.visitChildren(this);
449 } 464 }
450 465
451 @override 466 @override
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 // 573 //
559 // ... from case like: 574 // ... from case like:
560 // 575 //
561 // SomeType s; 576 // SomeType s;
562 // s.someDynamicField(...); // static get, followed by dynamic call. 577 // s.someDynamicField(...); // static get, followed by dynamic call.
563 // 578 //
564 // The first case is handled here, the second case is handled below when 579 // The first case is handled here, the second case is handled below when
565 // we call [checkFunctionApplication]. 580 // we call [checkFunctionApplication].
566 setIsDynamicInvoke(node.methodName, true); 581 setIsDynamicInvoke(node.methodName, true);
567 } else { 582 } else {
568 checkFunctionApplication(node); 583 _checkImplicitCovarianceCast(node, target, element);
584 _checkFunctionApplication(node);
569 } 585 }
570 node.visitChildren(this); 586 // Don't visit methodName, we already checked things related to the call.
587 node.target?.accept(this);
588 node.typeArguments?.accept(this);
589 node.argumentList?.accept(this);
571 } 590 }
572 591
573 @override 592 @override
574 void visitPostfixExpression(PostfixExpression node) { 593 void visitPostfixExpression(PostfixExpression node) {
575 _checkUnary(node.operand, node.operator, node.staticElement); 594 _checkUnary(node.operand, node.operator, node.staticElement);
576 node.visitChildren(this); 595 node.visitChildren(this);
577 } 596 }
578 597
579 @override 598 @override
580 void visitPrefixedIdentifier(PrefixedIdentifier node) { 599 void visitPrefixedIdentifier(PrefixedIdentifier node) {
(...skipping 28 matching lines...) Expand all
609 node.visitChildren(this); 628 node.visitChildren(this);
610 } 629 }
611 630
612 @override 631 @override
613 void visitReturnStatement(ReturnStatement node) { 632 void visitReturnStatement(ReturnStatement node) {
614 _checkReturnOrYield(node.expression, node); 633 _checkReturnOrYield(node.expression, node);
615 node.visitChildren(this); 634 node.visitChildren(this);
616 } 635 }
617 636
618 @override 637 @override
638 void visitSimpleIdentifier(SimpleIdentifier node) {
639 // Only visit SimpleIdentifiers when we're in a class, and at the
640 // class declaration level itself.
641 if (_currentClass != null && node.parent is! ClassMember) {
vsm 2017/07/05 22:57:34 Does this check match the comment? I.e., what if
Jennifer Messerly 2017/07/06 01:11:10 sorry, it should say "not at the class declaration
642 _checkImplicitCovarianceCast(node, null, node.staticElement);
643 }
644 }
645
646 @override
619 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { 647 void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
620 var element = node.staticElement; 648 var element = node.staticElement;
621 if (element != null) { 649 if (element != null) {
622 var type = resolutionMap.staticElementForConstructorReference(node).type; 650 var type = resolutionMap.staticElementForConstructorReference(node).type;
623 checkArgumentList(node.argumentList, type); 651 checkArgumentList(node.argumentList, type);
624 } 652 }
625 node.visitChildren(this); 653 node.visitChildren(this);
626 } 654 }
627 655
628 @override 656 @override
(...skipping 28 matching lines...) Expand all
657 variableElement.kind == ElementKind.FIELD) { 685 variableElement.kind == ElementKind.FIELD) {
658 _validateTopLevelInitializer(variableElement.name, node.initializer); 686 _validateTopLevelInitializer(variableElement.name, node.initializer);
659 } 687 }
660 } 688 }
661 return super.visitVariableDeclaration(node); 689 return super.visitVariableDeclaration(node);
662 } 690 }
663 691
664 @override 692 @override
665 void visitVariableDeclarationList(VariableDeclarationList node) { 693 void visitVariableDeclarationList(VariableDeclarationList node) {
666 TypeAnnotation type = node.type; 694 TypeAnnotation type = node.type;
667 if (type == null) { 695
668 // No checks are needed when the type is var. Although internally the 696 for (VariableDeclaration variable in node.variables) {
669 // typing rules may have inferred a more precise type for the variable 697 var initializer = variable.initializer;
670 // based on the initializer. 698 if (initializer != null) {
671 } else { 699 if (type != null) {
672 for (VariableDeclaration variable in node.variables) {
673 var initializer = variable.initializer;
674 if (initializer != null) {
675 checkAssignment(initializer, type.type); 700 checkAssignment(initializer, type.type);
676 } 701 }
677 } 702 }
678 } 703 }
704
679 node.visitChildren(this); 705 node.visitChildren(this);
680 } 706 }
681 707
682 @override 708 @override
683 void visitWhileStatement(WhileStatement node) { 709 void visitWhileStatement(WhileStatement node) {
684 checkBoolean(node.condition); 710 checkBoolean(node.condition);
685 node.visitChildren(this); 711 node.visitChildren(this);
686 } 712 }
687 713
688 @override 714 @override
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
722 // back to it. So these two implicit casts are equivalent: 748 // back to it. So these two implicit casts are equivalent:
723 // 749 //
724 // y = /*implicit cast*/(y + 42); 750 // y = /*implicit cast*/(y + 42);
725 // /*implicit assignment cast*/y += 42; 751 // /*implicit assignment cast*/y += 42;
726 // 752 //
727 _checkImplicitCast(expr.leftHandSide, lhsType, 753 _checkImplicitCast(expr.leftHandSide, lhsType,
728 from: returnType, opAssign: true); 754 from: returnType, opAssign: true);
729 } 755 }
730 } 756 }
731 757
732 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { 758 void _checkFieldAccess(
733 if (field.staticElement == null && 759 AstNode node, Expression target, SimpleIdentifier field) {
734 !typeProvider.isObjectMember(field.name)) { 760 var element = field.staticElement;
761 _checkImplicitCovarianceCast(node, target, element);
762 if (element == null && !typeProvider.isObjectMember(field.name)) {
735 _recordDynamicInvoke(node, target); 763 _recordDynamicInvoke(node, target);
736 } 764 }
737 node.visitChildren(this); 765 node.visitChildren(this);
738 } 766 }
739 767
740 /// Checks if an implicit cast of [expr] from [from] type to [to] type is 768 /// Checks if an implicit cast of [expr] from [from] type to [to] type is
741 /// needed, and if so records it. 769 /// needed, and if so records it.
742 /// 770 ///
743 /// If [from] is omitted, uses the static type of [expr]. 771 /// If [from] is omitted, uses the static type of [expr].
744 /// 772 ///
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
819 // /*implicit assignment cast*/y++; 847 // /*implicit assignment cast*/y++;
820 // 848 //
821 _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true); 849 _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true);
822 } 850 }
823 } 851 }
824 } 852 }
825 853
826 DartType _getDefiniteType(Expression expr) => 854 DartType _getDefiniteType(Expression expr) =>
827 getDefiniteType(expr, rules, typeProvider); 855 getDefiniteType(expr, rules, typeProvider);
828 856
857 /// If we're calling into [member] through the [target], we may need to
858 /// insert a caller side check for soundness on the result of the expression
859 /// [node].
860 ///
861 /// This happens when [target] is an unsafe covariant interface, and [member]
862 /// could return a type that is not a subtype of the expected static type
863 /// given target's type. For example:
864 ///
865 /// typedef F<T>(T t);
866 /// class C<T> {
867 /// F<T> f;
868 /// C(this.f);
869 /// }
870 /// test1() {
871 /// C<Object> c = new C<int>((int x) => x + 42));
872 /// F<Object> f = c.f; // need an implicit cast here.
873 /// f('hello');
874 /// }
875 ///
876 /// Here target is `c`, the target type is `C<Object>`, the member is
877 /// `get f() -> F<T>`, and the expression node is `c.f`. When we call `c.f`
878 /// the expected static result is `F<Object>`. However `c.f` actually returns
879 /// `F<int>`, which is not a subtype of `F<Object>`. So this method will add
880 /// an implicit cast `(c.f as F<Object>)` to guard against this case.
881 ///
882 /// Note that it is possible for the cast to succeed, for example:
883 /// `new C<int>((Object x) => '$x'))`. It is safe to pass any object to that
884 /// function, including an `int`.
885 void _checkImplicitCovarianceCast(
886 Expression node, Expression target, Element member) {
887 // If we're calling an instance method or getter, then we
888 // want to check the result type.
889 //
890 // We intentionally ignore method tear-offs, because those methods have
891 // covariance checks for their parameters inside the method.
892 if (member is ExecutableElement && _isInstanceMember(member)) {
893 DartType targetType;
894 if (target == null) {
895 if (!_inCurrentClass(member)) return;
896 targetType = _currentClass.type;
897 } else {
898 targetType = target.staticType;
899 }
900 if (targetType is InterfaceType && targetType.typeArguments.isNotEmpty) {
901 if (member.isPrivate && target != null) {
902 _trackPrivateMemberCovariance(target, member);
903 }
904
905 // Get the lower bound of the declared return type (e.g. `F<Null>`) and
906 // see if it can be assigned to the expected type (e.g. `F<Object>`).
907 //
908 // That way we can tell if any lower `T` will work or not.
909 var classType = targetType.element.type;
910 var classLowerBound = classType.instantiate(new List.filled(
911 classType.typeParameters.length, typeProvider.nullType));
912 var memberLowerBound = _lookUpMember(classLowerBound, member).type;
913 var expectedType = member.returnType;
914
915 if (!rules.isSubtypeOf(memberLowerBound.returnType, expectedType)) {
916 if (node is MethodInvocation && member is! MethodElement) {
917 // If `o.m` is not a method, we need to cast `o.m` before the call:
918 // `(o.m as expectedType)(args)`.
919 // This cannot be represented by an `as` node without changing the
920 // Dart AST structure, so we record it as a special cast.
921 setImplicitOperationCast(node, expectedType);
922 } else {
923 setImplicitCast(node, expectedType);
924 }
925 _hasImplicitCasts = true;
926 }
927 }
928 }
929 }
930
931 /// We can eliminate covariance checks on private members if they are only
932 /// accessed through something with a known generic type, such as `this`.
933 ///
934 /// For these expressions, we will know the generic parameters exactly:
935 ///
936 /// - this
937 /// - super
938 /// - non-factory instance creation
939 ///
940 /// For example:
941 ///
942 /// class C<T> {
943 /// T _t;
944 /// }
945 /// class D<T> extends C<T> {
946 /// method<S extends T>(T t, C<T> c) {
947 /// // implicit cast: t as T;
948 /// // implicit cast: c as C<T>;
949 ///
950 /// // These do not need further checks. The type parameter `T` for
951 /// // `this` must be the same as our `T`
952 /// this._t = t;
953 /// super._t = t;
954 /// new C<T>()._t = t; // non-factory
955 ///
956 /// // This needs further checks. The type of `c` could be `C<S>` for
957 /// // some `S <: T`.
958 /// c._t = t;
959 /// // factory statically returns `C<T>`, dynamically returns `C<S>`.
960 /// new F<T, S>()._t = t;
961 /// }
962 /// }
963 /// class F<T, S extends T> extends C<T> {
964 /// factory F() => new C<S>();
965 /// }
966 ///
967 void _trackPrivateMemberCovariance(Expression target, ExecutableElement e) {
968 if (target == null) return; // implicit this or invalid code.
969 if (target is ThisExpression || target is SuperExpression) return;
970 if (target is InstanceCreationExpression &&
971 target.staticElement?.isFactory == false) {
972 return;
973 }
974 if (e is PropertyAccessorElement && e.isGetter) return;
975
976 _covariantPrivateMembers.add(e is Member ? (e as Member).baseElement : e);
977 }
978
979 bool _isInstanceMember(ExecutableElement e) =>
980 !e.isStatic &&
981 (e is MethodElement ||
982 e is PropertyAccessorElement && e.variable is FieldElement);
983
984 bool _inCurrentClass(ExecutableElement e) {
985 if (_currentClass == null) return false;
986 var match = _lookUpMember(_currentClass.type, e);
987 return match?.enclosingElement == e.enclosingElement;
988 }
989
990 ExecutableElement _lookUpMember(InterfaceType type, ExecutableElement e) {
991 var name = e.name;
992 var library = e.library;
993 return e is PropertyAccessorElement
994 ? (e.isGetter
995 ? type.lookUpInheritedGetter(name, library: library)
996 : type.lookUpInheritedSetter(name, library: library))
997 : type.lookUpInheritedMethod(name, library: library);
998 }
999
829 /// Gets the expected return type of the given function [body], either from 1000 /// Gets the expected return type of the given function [body], either from
830 /// a normal return/yield, or from a yield*. 1001 /// a normal return/yield, or from a yield*.
831 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { 1002 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
832 FunctionType functionType; 1003 FunctionType functionType;
833 var parent = body.parent; 1004 var parent = body.parent;
834 if (parent is Declaration) { 1005 if (parent is Declaration) {
835 functionType = _elementType(parent.element); 1006 functionType = _elementType(parent.element);
836 } else { 1007 } else {
837 assert(parent is FunctionExpression); 1008 assert(parent is FunctionExpression);
838 functionType = 1009 functionType =
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after
1031 errorCode = StrongModeCode.DYNAMIC_CAST; 1202 errorCode = StrongModeCode.DYNAMIC_CAST;
1032 } else if (parent is VariableDeclaration && parent.initializer == expr) { 1203 } else if (parent is VariableDeclaration && parent.initializer == expr) {
1033 errorCode = StrongModeCode.ASSIGNMENT_CAST; 1204 errorCode = StrongModeCode.ASSIGNMENT_CAST;
1034 } else { 1205 } else {
1035 errorCode = opAssign 1206 errorCode = opAssign
1036 ? StrongModeCode.DOWN_CAST_IMPLICIT_ASSIGN 1207 ? StrongModeCode.DOWN_CAST_IMPLICIT_ASSIGN
1037 : StrongModeCode.DOWN_CAST_IMPLICIT; 1208 : StrongModeCode.DOWN_CAST_IMPLICIT;
1038 } 1209 }
1039 _recordMessage(expr, errorCode, [from, to]); 1210 _recordMessage(expr, errorCode, [from, to]);
1040 if (opAssign) { 1211 if (opAssign) {
1041 setImplicitAssignmentCast(expr, to); 1212 setImplicitOperationCast(expr, to);
1042 } else { 1213 } else {
1043 setImplicitCast(expr, to); 1214 setImplicitCast(expr, to);
1044 } 1215 }
1045 _hasImplicitCasts = true; 1216 _hasImplicitCasts = true;
1046 } 1217 }
1047 1218
1048 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { 1219 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) {
1049 // Compute the right severity taking the analysis options into account. 1220 // 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 1221 // We construct a dummy error to make the common case where we end up
1051 // ignoring the strong mode message cheaper. 1222 // ignoring the strong mode message cheaper.
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
1242 1413
1243 void check(Declaration node) { 1414 void check(Declaration node) {
1244 var element = 1415 var element =
1245 resolutionMap.elementDeclaredByDeclaration(node) as ClassElement; 1416 resolutionMap.elementDeclaredByDeclaration(node) as ClassElement;
1246 if (element.type.isObject) { 1417 if (element.type.isObject) {
1247 return; 1418 return;
1248 } 1419 }
1249 _checkSuperOverrides(node, element); 1420 _checkSuperOverrides(node, element);
1250 _checkMixinApplicationOverrides(node, element); 1421 _checkMixinApplicationOverrides(node, element);
1251 _checkAllInterfaceOverrides(node, element); 1422 _checkAllInterfaceOverrides(node, element);
1423 _checkForCovariantGenerics(node, element);
1424 }
1425
1426 /// Finds implicit casts that we need on parameters and type formals to
1427 /// ensure soundness of covariant generics, and records them on the [node].
1428 ///
1429 /// The parameter checks can be retrived using [getClassCovariantParameters]
1430 /// and [getSuperclassCovariantParameters].
1431 ///
1432 /// For each member of this class and non-overridden inherited member, we
1433 /// check to see if any generic super interface permits an unsound call to the
1434 /// concrete member. For example:
1435 ///
1436 /// class C<T> {
1437 /// add(T t) {} // C<Object>.add is unsafe, need a check on `t`
1438 /// }
1439 /// class D extends C<int> {
1440 /// add(int t) {} // C<Object>.add is unsafe, need a check on `t`
1441 /// }
1442 /// class E extends C<int> {
1443 /// add(Object t) {} // no check needed, C<Object>.add is safe
1444 /// }
1445 ///
1446 void _checkForCovariantGenerics(Declaration node, ClassElement element) {
1447 // Find all generic interfaces that could be used to call into members of
1448 // this class. This will help us identify which parameters need checks
1449 // for soundness.
1450 var allCovariant = _findAllGenericInterfaces(element.type);
1451 if (allCovariant.isEmpty) return;
1452
1453 var seenConcreteMembers = new HashSet<String>();
1454 var members = _getConcreteMembers(element.type, seenConcreteMembers);
1455
1456 // For members on this class, check them against all generic interfaces.
1457 var checks = _findCovariantChecks(members, allCovariant);
1458 // Store those checks on the class declaration.
1459 setClassCovariantParameters(node, checks);
1460
1461 // For members of the superclass, we may need to add checks because this
1462 // class adds a new unsafe interface. Collect those checks.
1463 checks = _findSuperclassCovariantChecks(
1464 element, allCovariant, seenConcreteMembers);
1465 // Store the checks on the class declaration, it will need to ensure the
1466 // inherited members are appropriately guarded to ensure soundness.
1467 setSuperclassCovariantParameters(node, checks);
1468 }
1469
1470 /// For each member of this class and non-overridden inherited member, we
1471 /// check to see if any generic super interface permits an unsound call to the
1472 /// concrete member. For example:
1473 ///
1474 /// We must check non-overridden inherited members because this class could
1475 /// contain a new interface that permits unsound access to that member. In
1476 /// those cases, the class is expected to insert stub that checks the type
1477 /// before calling `super`. For example:
1478 ///
1479 /// class C<T> {
1480 /// add(T t) {}
1481 /// }
1482 /// class D {
1483 /// add(int t) {}
1484 /// }
1485 /// class E extends D implements C<int> {
1486 /// // C<Object>.add is unsafe, and D.m is marked for a check.
1487 /// //
1488 /// // one way to implement this is to generate a stub method:
1489 /// // add(t) => super.add(t as int);
1490 /// }
1491 ///
1492 Set<Element> _findSuperclassCovariantChecks(ClassElement element,
1493 Set<ClassElement> allCovariant, HashSet<String> seenConcreteMembers) {
1494 var visited = new HashSet<ClassElement>()..add(element);
1495 var superChecks = new Set<Element>();
1496 var existingChecks = new HashSet<Element>();
1497
1498 void visitImmediateSuper(InterfaceType type) {
1499 // For members of mixins/supertypes, check them against new interfaces,
1500 // and also record any existing checks they already had.
1501 var oldCovariant = _findAllGenericInterfaces(type);
1502 var newCovariant = allCovariant.difference(oldCovariant);
1503 if (newCovariant.isEmpty) return;
1504
1505 void visitSuper(InterfaceType type) {
1506 var element = type.element;
1507 if (visited.add(element)) {
1508 var members = _getConcreteMembers(type, seenConcreteMembers);
1509 _findCovariantChecks(members, newCovariant, superChecks);
1510 _findCovariantChecks(members, oldCovariant, existingChecks);
1511 element.mixins.reversed.forEach(visitSuper);
1512 var s = element.supertype;
1513 if (s != null) visitSuper(s);
1514 }
1515 }
1516
1517 visitSuper(type);
1518 }
1519
1520 element.mixins.reversed.forEach(visitImmediateSuper);
1521 var s = element.supertype;
1522 if (s != null) visitImmediateSuper(s);
1523
1524 superChecks.removeAll(existingChecks);
1525 return superChecks;
1526 }
1527
1528 /// Gets all concrete instance members declared on this type, skipping already
1529 /// [seenConcreteMembers] and adding any found ones to it.
1530 ///
1531 /// By tracking the set of seen members, we can visit superclasses and mixins
1532 /// and ultimately collect every most-derived member exposed by a given type.
1533 static List<ExecutableElement> _getConcreteMembers(
1534 InterfaceType type, HashSet<String> seenConcreteMembers) {
1535 var members = <ExecutableElement>[];
1536 for (var declaredMembers in [type.accessors, type.methods]) {
1537 for (var member in declaredMembers) {
1538 // We only visit each most derived concrete member.
1539 // To avoid visiting an overridden superclass member, we skip members
1540 // we've seen, and visit starting from the class, then mixins in
1541 // reverse order, then superclasses.
1542 if (!member.isStatic &&
1543 !member.isAbstract &&
1544 seenConcreteMembers.add(member.name)) {
1545 members.add(member);
1546 }
1547 }
1548 }
1549 return members;
1550 }
1551
1552 /// Find all covariance checks on parameters/type parameters needed for
1553 /// soundness given a set of concrete [members] and a set of unsafe generic
1554 /// [covariantInterfaces] that may allow those members to be called in an
1555 /// unsound way.
1556 ///
1557 /// See [_findCovariantChecksForMember] for more inofrmation and an exmaple.
vsm 2017/07/05 22:57:34 inof -> info
Jennifer Messerly 2017/07/06 01:11:10 Done.
1558 Set<Element> _findCovariantChecks(Iterable<ExecutableElement> members,
1559 Iterable<ClassElement> covariantInterfaces,
1560 [Set<Element> covariantChecks]) {
1561 covariantChecks ??= new Set();
1562 if (members.isEmpty) return covariantChecks;
1563
1564 for (var iface in covariantInterfaces) {
1565 var unsafeSupertype = _getCovariantUpperBound(rules, iface);
1566 for (var m in members) {
1567 _findCovariantChecksForMember(m, unsafeSupertype, covariantChecks);
1568 }
1569 }
1570 return covariantChecks;
1571 }
1572
1573 /// Given a [member] and a covariant [unsafeSupertype], determine if any
1574 /// type formals or parameters of this member need a check because of the
1575 /// unsoundness in the unsafe covariant supertype.
1576 ///
1577 /// For example:
1578 ///
1579 /// class C<T> {
1580 /// m(T t) {}
1581 /// g<S extends T>() => <S>[];
1582 /// }
1583 /// class D extends C<num> {
1584 /// m(num n) {}
1585 /// g<R extends num>() => <R>[];
1586 /// }
1587 /// main() {
1588 /// C<Object> c = new C<int>();
1589 /// c.m('hi'); // must throw for soundness
1590 /// c.g<String>(); // must throw for soundness
1591 ///
1592 /// c = new D();
1593 /// c.m('hi'); // must throw for soundness
1594 /// c.g<String>(); // must throw for soundness
1595 /// }
1596 ///
1597 /// We've already found `C<Object>` is a potentially unsafe covariant generic
1598 /// supertpe, and we call this method to see if any members need a check
1599 /// because of `C<Object>`.
1600 ///
1601 /// In this example, we will call this method with:
1602 /// - `C<T>.m` and `C<Object>`, finding that `t` needs a check.
1603 /// - `C<T>.g` and `C<Object>`, finding that `S` needs a check.
1604 /// - `D.m` and `C<Object>`, finding that `n` needs a check.
1605 /// - `D.g` and `C<Object>`, finding that `R` needs a check.
1606 ///
1607 /// Given `C<T>.m` and `C<Object>`, we search for covariance checks like this
1608 /// (`*` short for `dynamic`):
1609 /// - get the type of `C<Object>.m`: `(Object) -> *`
1610 /// - get the type of `C<T>.m`: `(T) -> *`
1611 /// - perform a subtype check `(T) -> * <: (Object) -> *`,
1612 /// and record any parameters/type formals that violate soundess.
1613 /// - that checks `Object <: T`, which is false, thus we need a check on
1614 /// parameter `t` of `C<T>.m`
1615 ///
1616 /// Another example is `D.g` and `C<Object>`:
1617 /// - get the type of `C<Object>.m`: `<S extends Object>() -> *`
1618 /// - get the type of `D.g`: `<R extends num>() -> *`
1619 /// - perform a subtype check
1620 /// `<S extends Object>() -> * <: <R extends num>() -> *`,
1621 /// and record any parameters/type formals that violate soundess.
1622 /// - that checks the type formal bound of `S` and `R` asserting
1623 /// `Object <: num`, which is false, thus we need a check on type formal `R`
1624 /// of `D.g`.
1625 void _findCovariantChecksForMember(ExecutableElement member,
1626 InterfaceType unsafeSupertype, Set<Element> covariantChecks) {
1627 var f2 = _getMemberType(unsafeSupertype, member);
1628 if (f2 == null) return;
1629 var f1 = member.type;
1630
1631 // Find parameter or type formal checks that we need to ensure `f2 <: f1`.
1632 //
1633 // The static type system allows this subtyping, but it is not sound without
1634 // these runtime checks.
1635 void addCheck(Element e) {
1636 covariantChecks.add(e is Member ? e.baseElement : e);
1637 }
1638
1639 var fresh = FunctionTypeImpl.relateTypeFormals(f1, f2, (b2, b1, p2, p1) {
1640 if (!rules.isSubtypeOf(b2, b1)) addCheck(p1);
1641 return true;
1642 });
1643 if (fresh != null) {
1644 f1 = f1.instantiate(fresh);
1645 f2 = f2.instantiate(fresh);
1646 }
1647 FunctionTypeImpl.relateParameters(f1.parameters, f2.parameters, (p1, p2) {
1648 if (!rules.isOverrideSubtypeOfParameter(p1, p2)) addCheck(p1);
1649 return true;
1650 });
1651 }
1652
1653 /// Find all generic interfaces that are implemented by [type], including
1654 /// [type] itself if it is generic.
1655 ///
1656 /// This represents the complete set of unsafe covariant interfaces that could
1657 /// be used to call members of [type].
1658 ///
1659 /// Because we're going to instantiate these to their upper bound, we don't
1660 /// have to track type parameters.
1661 static Set<ClassElement> _findAllGenericInterfaces(InterfaceType type) {
1662 var visited = new HashSet<ClassElement>();
1663 var genericSupertypes = new Set<ClassElement>();
1664
1665 void visitTypeAndSupertypes(InterfaceType type) {
1666 var element = type.element;
1667 if (visited.add(element)) {
1668 if (element.typeParameters.isNotEmpty) {
1669 genericSupertypes.add(element);
1670 }
1671 var supertype = element.supertype;
1672 if (supertype != null) visitTypeAndSupertypes(supertype);
1673 element.mixins.forEach(visitTypeAndSupertypes);
1674 element.interfaces.forEach(visitTypeAndSupertypes);
1675 }
1676 }
1677
1678 visitTypeAndSupertypes(type);
1679
1680 return genericSupertypes;
1252 } 1681 }
1253 1682
1254 /// Checks that implementations correctly override all reachable interfaces. 1683 /// Checks that implementations correctly override all reachable interfaces.
1255 /// In particular, we need to check these overrides for the definitions in 1684 /// 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 1685 /// the class itself and each its superclasses. If a superclass is not
1257 /// abstract, then we can skip its transitive interfaces. For example, in: 1686 /// abstract, then we can skip its transitive interfaces. For example, in:
1258 /// 1687 ///
1259 /// B extends C implements G 1688 /// B extends C implements G
1260 /// A extends B with E, F implements H, I 1689 /// A extends B with E, F implements H, I
1261 /// 1690 ///
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
1360 /// Check that individual methods and fields in [subType] correctly override 1789 /// Check that individual methods and fields in [subType] correctly override
1361 /// the declarations in [baseType]. 1790 /// the declarations in [baseType].
1362 /// 1791 ///
1363 /// The [errorLocation] node indicates where errors are reported, see 1792 /// The [errorLocation] node indicates where errors are reported, see
1364 /// [_checkSingleOverride] for more details. 1793 /// [_checkSingleOverride] for more details.
1365 /// 1794 ///
1366 /// The set [seen] is used to avoid reporting overrides more than once. It 1795 /// 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 1796 /// 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 1797 /// types in a class hierarchy. Errors are reported only the first time an
1369 /// invalid override involving a specific member is encountered. 1798 /// invalid override involving a specific member is encountered.
1370 _checkIndividualOverridesFromType( 1799 void _checkIndividualOverridesFromType(
1371 InterfaceType subType, 1800 InterfaceType subType,
1372 InterfaceType baseType, 1801 InterfaceType baseType,
1373 AstNode errorLocation, 1802 AstNode errorLocation,
1374 Set<String> seen, 1803 Set<String> seen,
1375 bool isSubclass) { 1804 bool isSubclass) {
1376 void checkHelper(ExecutableElement e) { 1805 void checkHelper(ExecutableElement e) {
1377 if (e.isStatic) return; 1806 if (e.isStatic) return;
1378 if (seen.contains(e.name)) return; 1807 if (seen.contains(e.name)) return;
1379 if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) { 1808 if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) {
1380 seen.add(e.name); 1809 seen.add(e.name);
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
1495 /// ^ 1924 /// ^
1496 /// 1925 ///
1497 /// When checking for overrides from a type and it's super types, [node] is 1926 /// 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 1927 /// 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. 1928 /// type of the element could be inferred from the types in the super classes.
1500 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, 1929 bool _checkSingleOverride(ExecutableElement element, InterfaceType type,
1501 AstNode node, AstNode errorLocation, bool isSubclass) { 1930 AstNode node, AstNode errorLocation, bool isSubclass) {
1502 assert(!element.isStatic); 1931 assert(!element.isStatic);
1503 1932
1504 FunctionType subType = _elementType(element); 1933 FunctionType subType = _elementType(element);
1505 // TODO(vsm): Test for generic
1506 FunctionType baseType = _getMemberType(type, element); 1934 FunctionType baseType = _getMemberType(type, element);
1507 if (baseType == null) return false; 1935 if (baseType == null) return false;
1508 1936
1509 if (isSubclass && element is PropertyAccessorElement) { 1937 if (isSubclass && element is PropertyAccessorElement) {
1510 // Disallow any overriding if the base class defines this member 1938 // Disallow any overriding if the base class defines this member
1511 // as a field. We effectively treat fields as final / non-virtual, 1939 // as a field. We effectively treat fields as final / non-virtual,
1512 // unless they are explicitly marked as @virtual 1940 // unless they are explicitly marked as @virtual
1513 var field = _getMemberField(type, element); 1941 var field = _getMemberField(type, element);
1514 if (field != null && !field.isVirtual) { 1942 if (field != null && !field.isVirtual) {
1515 _checker._recordMessage( 1943 _checker._recordMessage(
1516 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [ 1944 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [
1517 element.enclosingElement.name, 1945 element.enclosingElement.name,
1518 element.name, 1946 element.name,
1519 subType, 1947 subType,
1520 type, 1948 type,
1521 baseType 1949 baseType
1522 ]); 1950 ]);
1523 } 1951 }
1524 } 1952 }
1953
1525 if (!rules.isOverrideSubtypeOf(subType, baseType)) { 1954 if (!rules.isOverrideSubtypeOf(subType, baseType)) {
1526 ErrorCode errorCode; 1955 ErrorCode errorCode;
1527 var parent = errorLocation?.parent; 1956 var parent = errorLocation?.parent;
1528 if (errorLocation is ExtendsClause || 1957 if (errorLocation is ExtendsClause ||
1529 parent is ClassTypeAlias && parent.superclass == errorLocation) { 1958 parent is ClassTypeAlias && parent.superclass == errorLocation) {
1530 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE; 1959 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE;
1531 } else if (parent is WithClause) { 1960 } else if (parent is WithClause) {
1532 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN; 1961 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN;
1533 } else { 1962 } else {
1534 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE; 1963 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE;
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1600 } 2029 }
1601 2030
1602 /// If node is a [ClassDeclaration] returns its members, otherwise if node is 2031 /// If node is a [ClassDeclaration] returns its members, otherwise if node is
1603 /// a [ClassTypeAlias] this returns an empty list. 2032 /// a [ClassTypeAlias] this returns an empty list.
1604 WithClause _withClause(Declaration node) { 2033 WithClause _withClause(Declaration node) {
1605 return node is ClassDeclaration 2034 return node is ClassDeclaration
1606 ? node.withClause 2035 ? node.withClause
1607 : (node as ClassTypeAlias).withClause; 2036 : (node as ClassTypeAlias).withClause;
1608 } 2037 }
1609 } 2038 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698