Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be | 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be |
| 6 // refactored to fit into analyzer. | 6 // refactored to fit into analyzer. |
| 7 library analyzer.src.task.strong.checker; | 7 library analyzer.src.task.strong.checker; |
| 8 | 8 |
| 9 import 'dart:collection'; | |
| 9 import 'package:analyzer/analyzer.dart'; | 10 import 'package:analyzer/analyzer.dart'; |
| 10 import 'package:analyzer/dart/ast/ast.dart'; | 11 import 'package:analyzer/dart/ast/ast.dart'; |
| 11 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; | 12 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; |
| 12 import 'package:analyzer/dart/ast/token.dart' show TokenType; | 13 import 'package:analyzer/dart/ast/token.dart' show TokenType; |
| 13 import 'package:analyzer/dart/ast/token.dart'; | 14 import 'package:analyzer/dart/ast/token.dart'; |
| 14 import 'package:analyzer/dart/ast/visitor.dart'; | 15 import 'package:analyzer/dart/ast/visitor.dart'; |
| 15 import 'package:analyzer/dart/element/element.dart'; | 16 import 'package:analyzer/dart/element/element.dart'; |
| 16 import 'package:analyzer/dart/element/type.dart'; | 17 import 'package:analyzer/dart/element/type.dart'; |
| 17 import 'package:analyzer/source/error_processor.dart' show ErrorProcessor; | 18 import 'package:analyzer/source/error_processor.dart' show ErrorProcessor; |
| 18 import 'package:analyzer/src/dart/element/element.dart'; | 19 import 'package:analyzer/src/dart/element/element.dart'; |
| 20 import 'package:analyzer/src/dart/element/member.dart'; | |
| 19 import 'package:analyzer/src/dart/element/type.dart'; | 21 import 'package:analyzer/src/dart/element/type.dart'; |
| 20 import 'package:analyzer/src/error/codes.dart' show StrongModeCode; | 22 import 'package:analyzer/src/error/codes.dart' show StrongModeCode; |
| 21 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; | 23 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; |
| 22 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 24 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 23 import 'package:analyzer/src/generated/type_system.dart'; | 25 import 'package:analyzer/src/generated/type_system.dart'; |
| 24 import 'package:analyzer/src/summary/idl.dart'; | 26 import 'package:analyzer/src/summary/idl.dart'; |
| 25 | 27 |
| 26 import 'ast_properties.dart'; | 28 import 'ast_properties.dart'; |
| 27 | 29 |
| 28 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider], | 30 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider], |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 44 return typeSystem.functionTypeToConcreteType(type); | 46 return typeSystem.functionTypeToConcreteType(type); |
| 45 } | 47 } |
| 46 return type; | 48 return type; |
| 47 } | 49 } |
| 48 | 50 |
| 49 bool hasStrictArrow(Expression expression) { | 51 bool hasStrictArrow(Expression expression) { |
| 50 var element = _getKnownElement(expression); | 52 var element = _getKnownElement(expression); |
| 51 return element is FunctionElement || element is MethodElement; | 53 return element is FunctionElement || element is MethodElement; |
| 52 } | 54 } |
| 53 | 55 |
| 56 /// Given a generic class [element] find its covariant upper bound, using | |
| 57 /// the type system [rules]. | |
| 58 /// | |
| 59 /// Unlike [TypeSystem.instantiateToBounds], this will change `dynamic` into | |
| 60 /// `Object` to work around an issue with fuzzy arrows. | |
| 61 InterfaceType _getCovariantUpperBound(TypeSystem rules, ClassElement element) { | |
| 62 var upperBound = rules.instantiateToBounds(element.type) as InterfaceType; | |
| 63 var typeArgs = upperBound.typeArguments; | |
| 64 // TODO(jmesserly): remove this. It is a workaround for fuzzy arrows. | |
| 65 // To prevent extra checks due to fuzzy arrows, we need to instantiate with | |
| 66 // `Object` rather than `dynamic`. Consider a case like: | |
| 67 // | |
| 68 // class C<T> { | |
| 69 // void forEach(f(T t)) {} | |
| 70 // } | |
| 71 // | |
| 72 // If we try `(dynamic) ~> void <: (T) ~> void` with fuzzy arrows, we will | |
| 73 // treat `dynamic` as `bottom` and get `(bottom) -> void <: (T) -> void` | |
| 74 // which indicates that a check is required on the parameter `f`. This check | |
| 75 // is not sufficient when `T` is `dynamic`, however, because calling a | |
| 76 // function with a fuzzy arrow type is not safe and requires a dynamic call. | |
| 77 // See: https://github.com/dart-lang/sdk/issues/29295 | |
| 78 // | |
| 79 // For all other values of T, the check is unnecessary: it is sound to pass | |
| 80 // a function that accepts any Object. | |
| 81 if (typeArgs.any((t) => t.isDynamic)) { | |
| 82 var newTypeArgs = typeArgs | |
| 83 .map((t) => t.isDynamic ? rules.typeProvider.objectType : t) | |
| 84 .toList(); | |
| 85 upperBound = element.type.instantiate(newTypeArgs); | |
| 86 } | |
| 87 return upperBound; | |
| 88 } | |
| 89 | |
| 54 DartType _elementType(Element e) { | 90 DartType _elementType(Element e) { |
| 55 if (e == null) { | 91 if (e == null) { |
| 56 // Malformed code - just return dynamic. | 92 // Malformed code - just return dynamic. |
| 57 return DynamicTypeImpl.instance; | 93 return DynamicTypeImpl.instance; |
| 58 } | 94 } |
| 59 return (e as dynamic).type; | 95 return (e as dynamic).type; |
| 60 } | 96 } |
| 61 | 97 |
| 62 Element _getKnownElement(Expression expression) { | 98 Element _getKnownElement(Expression expression) { |
| 63 if (expression is ParenthesizedExpression) { | 99 if (expression is ParenthesizedExpression) { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 96 field = setter.variable; | 132 field = setter.variable; |
| 97 } else { | 133 } else { |
| 98 return null; | 134 return null; |
| 99 } | 135 } |
| 100 if (field.isSynthetic) return null; | 136 if (field.isSynthetic) return null; |
| 101 return field; | 137 return field; |
| 102 } | 138 } |
| 103 | 139 |
| 104 /// Looks up the declaration that matches [member] in [type] and returns it's | 140 /// Looks up the declaration that matches [member] in [type] and returns it's |
| 105 /// declared type. | 141 /// declared type. |
| 106 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => | 142 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) { |
| 107 _memberTypeGetter(member)(type); | 143 if (member.isPrivate && type.element.library != member.library) { |
| 108 | 144 return null; |
| 109 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) { | |
| 110 String memberName = member.name; | |
| 111 final isGetter = member is PropertyAccessorElement && member.isGetter; | |
| 112 final isSetter = member is PropertyAccessorElement && member.isSetter; | |
| 113 | |
| 114 FunctionType f(InterfaceType type) { | |
| 115 ExecutableElement baseMethod; | |
| 116 | |
| 117 if (member.isPrivate) { | |
| 118 var subtypeLibrary = member.library; | |
| 119 var baseLibrary = type.element.library; | |
| 120 if (baseLibrary != subtypeLibrary) { | |
| 121 return null; | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 try { | |
| 126 if (isGetter) { | |
| 127 assert(!isSetter); | |
| 128 // Look for getter or field. | |
| 129 baseMethod = type.getGetter(memberName); | |
| 130 } else if (isSetter) { | |
| 131 baseMethod = type.getSetter(memberName); | |
| 132 } else { | |
| 133 baseMethod = type.getMethod(memberName); | |
| 134 } | |
| 135 } catch (e) { | |
| 136 // TODO(sigmund): remove this try-catch block (see issue #48). | |
| 137 } | |
| 138 if (baseMethod == null || baseMethod.isStatic) return null; | |
| 139 return baseMethod.type; | |
| 140 } | 145 } |
| 141 | 146 |
| 142 return f; | 147 var name = member.name; |
| 148 var baseMember = member is PropertyAccessorElement | |
| 149 ? (member.isGetter ? type.getGetter(name) : type.getSetter(name)) | |
| 150 : type.getMethod(name); | |
| 151 if (baseMember == null || baseMember.isStatic) return null; | |
| 152 return baseMember.type; | |
| 143 } | 153 } |
| 144 | 154 |
| 145 typedef FunctionType _MemberTypeGetter(InterfaceType type); | |
| 146 | |
| 147 /// Checks the body of functions and properties. | 155 /// Checks the body of functions and properties. |
| 148 class CodeChecker extends RecursiveAstVisitor { | 156 class CodeChecker extends RecursiveAstVisitor { |
| 149 final StrongTypeSystemImpl rules; | 157 final StrongTypeSystemImpl rules; |
| 150 final TypeProvider typeProvider; | 158 final TypeProvider typeProvider; |
| 151 final AnalysisErrorListener reporter; | 159 final AnalysisErrorListener reporter; |
| 152 final AnalysisOptionsImpl _options; | 160 final AnalysisOptionsImpl _options; |
| 153 _OverrideChecker _overrideChecker; | 161 _OverrideChecker _overrideChecker; |
| 154 | 162 |
| 155 bool _failure = false; | 163 bool _failure = false; |
| 156 bool _hasImplicitCasts; | 164 bool _hasImplicitCasts; |
| 165 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |