| 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 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
| 10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
| 11 import 'package:analyzer/dart/ast/token.dart' show TokenType; | 11 import 'package:analyzer/dart/ast/token.dart' show TokenType; |
| 12 import 'package:analyzer/dart/ast/token.dart'; | 12 import 'package:analyzer/dart/ast/token.dart'; |
| 13 import 'package:analyzer/dart/ast/visitor.dart'; | 13 import 'package:analyzer/dart/ast/visitor.dart'; |
| 14 import 'package:analyzer/dart/element/element.dart'; | 14 import 'package:analyzer/dart/element/element.dart'; |
| 15 import 'package:analyzer/dart/element/type.dart'; | 15 import 'package:analyzer/dart/element/type.dart'; |
| 16 import 'package:analyzer/src/dart/element/type.dart'; | 16 import 'package:analyzer/src/dart/element/type.dart'; |
| 17 import 'package:analyzer/src/error/codes.dart' show StrongModeCode; |
| 17 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; | 18 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; |
| 18 import 'package:analyzer/src/generated/error.dart' show StrongModeCode; | |
| 19 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 19 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 20 import 'package:analyzer/src/generated/type_system.dart'; | 20 import 'package:analyzer/src/generated/type_system.dart'; |
| 21 | 21 |
| 22 import 'ast_properties.dart'; | 22 import 'ast_properties.dart'; |
| 23 | 23 |
| 24 bool isKnownFunction(Expression expression) { | |
| 25 var element = _getKnownElement(expression); | |
| 26 // First class functions and static methods, where we know the original | |
| 27 // declaration, will have an exact type, so we know a downcast will fail. | |
| 28 return element is FunctionElement || | |
| 29 element is MethodElement && element.isStatic; | |
| 30 } | |
| 31 | |
| 32 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider], | 24 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider], |
| 33 /// gets the known static type of the expression. | 25 /// gets the known static type of the expression. |
| 34 /// | 26 /// |
| 35 /// Normally when we ask for an expression's type, we get the type of the | 27 /// Normally when we ask for an expression's type, we get the type of the |
| 36 /// storage slot that would contain it. For function types, this is necessarily | 28 /// storage slot that would contain it. For function types, this is necessarily |
| 37 /// a "fuzzy arrow" that treats `dynamic` as bottom. However, if we're | 29 /// a "fuzzy arrow" that treats `dynamic` as bottom. However, if we're |
| 38 /// interested in the expression's own type, it can often be a "strict arrow" | 30 /// interested in the expression's own type, it can often be a "strict arrow" |
| 39 /// because we know it evaluates to a specific, concrete function, and we can | 31 /// because we know it evaluates to a specific, concrete function, and we can |
| 40 /// treat "dynamic" as top for that case, which is more permissive. | 32 /// treat "dynamic" as top for that case, which is more permissive. |
| 41 DartType getDefiniteType( | 33 DartType getDefiniteType( |
| 42 Expression expression, TypeSystem typeSystem, TypeProvider typeProvider) { | 34 Expression expression, TypeSystem typeSystem, TypeProvider typeProvider) { |
| 43 DartType type = expression.staticType ?? DynamicTypeImpl.instance; | 35 DartType type = expression.staticType ?? DynamicTypeImpl.instance; |
| 44 if (typeSystem is StrongTypeSystemImpl && | 36 if (typeSystem is StrongTypeSystemImpl && |
| 45 type is FunctionType && | 37 type is FunctionType && |
| 46 _hasStrictArrow(expression)) { | 38 _hasStrictArrow(expression)) { |
| 47 // Remove fuzzy arrow if possible. | 39 // Remove fuzzy arrow if possible. |
| 48 return typeSystem.functionTypeToConcreteType(typeProvider, type); | 40 return typeSystem.functionTypeToConcreteType(typeProvider, type); |
| 49 } | 41 } |
| 50 return type; | 42 return type; |
| 51 } | 43 } |
| 52 | 44 |
| 53 bool _hasStrictArrow(Expression expression) { | 45 bool isKnownFunction(Expression expression) { |
| 54 var element = _getKnownElement(expression); | 46 var element = _getKnownElement(expression); |
| 55 return element is FunctionElement || element is MethodElement; | 47 // First class functions and static methods, where we know the original |
| 48 // declaration, will have an exact type, so we know a downcast will fail. |
| 49 return element is FunctionElement || |
| 50 element is MethodElement && element.isStatic; |
| 51 } |
| 52 |
| 53 DartType _elementType(Element e) { |
| 54 if (e == null) { |
| 55 // Malformed code - just return dynamic. |
| 56 return DynamicTypeImpl.instance; |
| 57 } |
| 58 return (e as dynamic).type; |
| 56 } | 59 } |
| 57 | 60 |
| 58 Element _getKnownElement(Expression expression) { | 61 Element _getKnownElement(Expression expression) { |
| 59 if (expression is ParenthesizedExpression) { | 62 if (expression is ParenthesizedExpression) { |
| 60 expression = (expression as ParenthesizedExpression).expression; | 63 expression = (expression as ParenthesizedExpression).expression; |
| 61 } | 64 } |
| 62 if (expression is FunctionExpression) { | 65 if (expression is FunctionExpression) { |
| 63 return expression.element; | 66 return expression.element; |
| 64 } else if (expression is PropertyAccess) { | 67 } else if (expression is PropertyAccess) { |
| 65 return expression.propertyName.staticElement; | 68 return expression.propertyName.staticElement; |
| 66 } else if (expression is Identifier) { | 69 } else if (expression is Identifier) { |
| 67 return expression.staticElement; | 70 return expression.staticElement; |
| 68 } | 71 } |
| 69 return null; | 72 return null; |
| 70 } | 73 } |
| 71 | 74 |
| 72 DartType _elementType(Element e) { | 75 /// Return the field on type corresponding to member, or null if none |
| 73 if (e == null) { | 76 /// exists or the "field" is actually a getter/setter. |
| 74 // Malformed code - just return dynamic. | |
| 75 return DynamicTypeImpl.instance; | |
| 76 } | |
| 77 return (e as dynamic).type; | |
| 78 } | |
| 79 | |
| 80 // Return the field on type corresponding to member, or null if none | |
| 81 // exists or the "field" is actually a getter/setter. | |
| 82 PropertyInducingElement _getMemberField( | 77 PropertyInducingElement _getMemberField( |
| 83 InterfaceType type, PropertyAccessorElement member) { | 78 InterfaceType type, PropertyAccessorElement member) { |
| 84 String memberName = member.name; | 79 String memberName = member.name; |
| 85 PropertyInducingElement field; | 80 PropertyInducingElement field; |
| 86 if (member.isGetter) { | 81 if (member.isGetter) { |
| 87 // The subclass member is an explicit getter or a field | 82 // The subclass member is an explicit getter or a field |
| 88 // - lookup the getter on the superclass. | 83 // - lookup the getter on the superclass. |
| 89 var getter = type.getGetter(memberName); | 84 var getter = type.getGetter(memberName); |
| 90 if (getter == null || getter.isStatic) return null; | 85 if (getter == null || getter.isStatic) return null; |
| 91 field = getter.variable; | 86 field = getter.variable; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 102 } | 97 } |
| 103 if (field.isSynthetic) return null; | 98 if (field.isSynthetic) return null; |
| 104 return field; | 99 return field; |
| 105 } | 100 } |
| 106 | 101 |
| 107 /// Looks up the declaration that matches [member] in [type] and returns it's | 102 /// Looks up the declaration that matches [member] in [type] and returns it's |
| 108 /// declared type. | 103 /// declared type. |
| 109 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => | 104 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => |
| 110 _memberTypeGetter(member)(type); | 105 _memberTypeGetter(member)(type); |
| 111 | 106 |
| 107 bool _hasStrictArrow(Expression expression) { |
| 108 var element = _getKnownElement(expression); |
| 109 return element is FunctionElement || element is MethodElement; |
| 110 } |
| 111 |
| 112 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) { | 112 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) { |
| 113 String memberName = member.name; | 113 String memberName = member.name; |
| 114 final isGetter = member is PropertyAccessorElement && member.isGetter; | 114 final isGetter = member is PropertyAccessorElement && member.isGetter; |
| 115 final isSetter = member is PropertyAccessorElement && member.isSetter; | 115 final isSetter = member is PropertyAccessorElement && member.isSetter; |
| 116 | 116 |
| 117 FunctionType f(InterfaceType type) { | 117 FunctionType f(InterfaceType type) { |
| 118 ExecutableElement baseMethod; | 118 ExecutableElement baseMethod; |
| 119 | 119 |
| 120 if (member.isPrivate) { | 120 if (member.isPrivate) { |
| 121 var subtypeLibrary = member.library; | 121 var subtypeLibrary = member.library; |
| (...skipping 511 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 633 @override | 633 @override |
| 634 void visitSwitchStatement(SwitchStatement node) { | 634 void visitSwitchStatement(SwitchStatement node) { |
| 635 // SwitchStatement defines a boolean conversion to check the result of the | 635 // SwitchStatement defines a boolean conversion to check the result of the |
| 636 // case value == the switch value, but in dev_compiler we require a boolean | 636 // case value == the switch value, but in dev_compiler we require a boolean |
| 637 // return type from an overridden == operator (because Object.==), so | 637 // return type from an overridden == operator (because Object.==), so |
| 638 // checking in SwitchStatement shouldn't be necessary. | 638 // checking in SwitchStatement shouldn't be necessary. |
| 639 node.visitChildren(this); | 639 node.visitChildren(this); |
| 640 } | 640 } |
| 641 | 641 |
| 642 @override | 642 @override |
| 643 Object visitVariableDeclaration(VariableDeclaration node) { |
| 644 if (!node.isConst && |
| 645 !node.isFinal && |
| 646 node.initializer == null && |
| 647 rules.isNonNullableType(node?.element?.type)) { |
| 648 _recordMessage( |
| 649 node, |
| 650 StaticTypeWarningCode.NON_NULLABLE_FIELD_NOT_INITIALIZED, |
| 651 [node.name, node?.element?.type]); |
| 652 } |
| 653 return super.visitVariableDeclaration(node); |
| 654 } |
| 655 |
| 656 @override |
| 643 void visitVariableDeclarationList(VariableDeclarationList node) { | 657 void visitVariableDeclarationList(VariableDeclarationList node) { |
| 644 TypeName type = node.type; | 658 TypeName type = node.type; |
| 645 if (type == null) { | 659 if (type == null) { |
| 646 // No checks are needed when the type is var. Although internally the | 660 // No checks are needed when the type is var. Although internally the |
| 647 // typing rules may have inferred a more precise type for the variable | 661 // typing rules may have inferred a more precise type for the variable |
| 648 // based on the initializer. | 662 // based on the initializer. |
| 649 } else { | 663 } else { |
| 650 for (VariableDeclaration variable in node.variables) { | 664 for (VariableDeclaration variable in node.variables) { |
| 651 var initializer = variable.initializer; | 665 var initializer = variable.initializer; |
| 652 if (initializer != null) { | 666 if (initializer != null) { |
| 653 checkAssignment(initializer, type.type); | 667 checkAssignment(initializer, type.type); |
| 654 } | 668 } |
| 655 } | 669 } |
| 656 } | 670 } |
| 657 node.visitChildren(this); | 671 node.visitChildren(this); |
| 658 } | 672 } |
| 659 | 673 |
| 660 @override | 674 @override |
| 661 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 662 if (!node.isConst && | |
| 663 !node.isFinal && | |
| 664 node.initializer == null && | |
| 665 rules.isNonNullableType(node?.element?.type)) { | |
| 666 _recordMessage( | |
| 667 node, | |
| 668 StaticTypeWarningCode.NON_NULLABLE_FIELD_NOT_INITIALIZED, | |
| 669 [node.name, node?.element?.type]); | |
| 670 } | |
| 671 return super.visitVariableDeclaration(node); | |
| 672 } | |
| 673 | |
| 674 @override | |
| 675 void visitWhileStatement(WhileStatement node) { | 675 void visitWhileStatement(WhileStatement node) { |
| 676 checkBoolean(node.condition); | 676 checkBoolean(node.condition); |
| 677 node.visitChildren(this); | 677 node.visitChildren(this); |
| 678 } | 678 } |
| 679 | 679 |
| 680 @override | 680 @override |
| 681 void visitYieldStatement(YieldStatement node) { | 681 void visitYieldStatement(YieldStatement node) { |
| 682 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); | 682 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); |
| 683 node.visitChildren(this); | 683 node.visitChildren(this); |
| 684 } | 684 } |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 870 // Variable declaration is inside a function or method, so it's safe. | 870 // Variable declaration is inside a function or method, so it's safe. |
| 871 return; | 871 return; |
| 872 } | 872 } |
| 873 _recordMessage(node, StrongModeCode.UNSAFE_BLOCK_CLOSURE_INFERENCE, | 873 _recordMessage(node, StrongModeCode.UNSAFE_BLOCK_CLOSURE_INFERENCE, |
| 874 [declElement.name]); | 874 [declElement.name]); |
| 875 } | 875 } |
| 876 | 876 |
| 877 /// Checks if the assignment is valid with respect to non-nullable types. | 877 /// Checks if the assignment is valid with respect to non-nullable types. |
| 878 /// Returns `false` if a nullable expression is assigned to a variable of | 878 /// Returns `false` if a nullable expression is assigned to a variable of |
| 879 /// non-nullable type and `true` otherwise. | 879 /// non-nullable type and `true` otherwise. |
| 880 bool _checkNonNullAssignment(Expression expression, DartType to, DartType from
) { | 880 bool _checkNonNullAssignment( |
| 881 Expression expression, DartType to, DartType from) { |
| 881 if (rules.isNonNullableType(to) && rules.isNullableType(from)) { | 882 if (rules.isNonNullableType(to) && rules.isNullableType(from)) { |
| 882 _recordMessage(expression, StaticTypeWarningCode.INVALID_ASSIGNMENT, | 883 _recordMessage( |
| 883 [from, to]); | 884 expression, StaticTypeWarningCode.INVALID_ASSIGNMENT, [from, to]); |
| 884 return false; | 885 return false; |
| 885 } | 886 } |
| 886 return true; | 887 return true; |
| 887 } | 888 } |
| 888 | 889 |
| 889 void _checkReturnOrYield(Expression expression, AstNode node, | 890 void _checkReturnOrYield(Expression expression, AstNode node, |
| 890 {bool yieldStar: false}) { | 891 {bool yieldStar: false}) { |
| 891 FunctionBody body = node.getAncestor((n) => n is FunctionBody); | 892 FunctionBody body = node.getAncestor((n) => n is FunctionBody); |
| 892 var type = _getExpectedReturnType(body, yieldStar: yieldStar); | 893 var type = _getExpectedReturnType(body, yieldStar: yieldStar); |
| 893 if (type == null) { | 894 if (type == null) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 916 op.type == TokenType.MINUS_MINUS) { | 917 op.type == TokenType.MINUS_MINUS) { |
| 917 if (element == null) { | 918 if (element == null) { |
| 918 _recordDynamicInvoke(node, node.operand); | 919 _recordDynamicInvoke(node, node.operand); |
| 919 } | 920 } |
| 920 // For ++ and --, even if it is not dynamic, we still need to check | 921 // For ++ and --, even if it is not dynamic, we still need to check |
| 921 // that the user defined method accepts an `int` as the RHS. | 922 // that the user defined method accepts an `int` as the RHS. |
| 922 // We assume Analyzer has done this already. | 923 // We assume Analyzer has done this already. |
| 923 } | 924 } |
| 924 } | 925 } |
| 925 | 926 |
| 927 DartType _getDefiniteType(Expression expr) => |
| 928 getDefiniteType(expr, rules, typeProvider); |
| 929 |
| 926 /// Gets the expected return type of the given function [body], either from | 930 /// Gets the expected return type of the given function [body], either from |
| 927 /// a normal return/yield, or from a yield*. | 931 /// a normal return/yield, or from a yield*. |
| 928 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | 932 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
| 929 FunctionType functionType; | 933 FunctionType functionType; |
| 930 var parent = body.parent; | 934 var parent = body.parent; |
| 931 if (parent is Declaration) { | 935 if (parent is Declaration) { |
| 932 functionType = _elementType(parent.element); | 936 functionType = _elementType(parent.element); |
| 933 } else { | 937 } else { |
| 934 assert(parent is FunctionExpression); | 938 assert(parent is FunctionExpression); |
| 935 functionType = | 939 functionType = |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 970 if (type.isDynamic) { | 974 if (type.isDynamic) { |
| 971 return type; | 975 return type; |
| 972 } else if (type is InterfaceType && type.element == expectedType.element) { | 976 } else if (type is InterfaceType && type.element == expectedType.element) { |
| 973 return type.typeArguments[0]; | 977 return type.typeArguments[0]; |
| 974 } else { | 978 } else { |
| 975 // Malformed type - fallback on analyzer error. | 979 // Malformed type - fallback on analyzer error. |
| 976 return null; | 980 return null; |
| 977 } | 981 } |
| 978 } | 982 } |
| 979 | 983 |
| 980 DartType _getDefiniteType(Expression expr) => | |
| 981 getDefiniteType(expr, rules, typeProvider); | |
| 982 | |
| 983 /// Given an expression, return its type assuming it is | 984 /// Given an expression, return its type assuming it is |
| 984 /// in the caller position of a call (that is, accounting | 985 /// in the caller position of a call (that is, accounting |
| 985 /// for the possibility of a call method). Returns null | 986 /// for the possibility of a call method). Returns null |
| 986 /// if expression is not statically callable. | 987 /// if expression is not statically callable. |
| 987 FunctionType _getTypeAsCaller(InvocationExpression node) { | 988 FunctionType _getTypeAsCaller(InvocationExpression node) { |
| 988 DartType type = node.staticInvokeType; | 989 DartType type = node.staticInvokeType; |
| 989 if (type is FunctionType) { | 990 if (type is FunctionType) { |
| 990 return type; | 991 return type; |
| 991 } else if (type is InterfaceType) { | 992 } else if (type is InterfaceType) { |
| 992 return rules.getCallMethodType(type); | 993 return rules.getCallMethodType(type); |
| (...skipping 469 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1462 var visited = new Set<InterfaceType>(); | 1463 var visited = new Set<InterfaceType>(); |
| 1463 do { | 1464 do { |
| 1464 visited.add(current); | 1465 visited.add(current); |
| 1465 current.mixins.reversed.forEach( | 1466 current.mixins.reversed.forEach( |
| 1466 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); | 1467 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); |
| 1467 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); | 1468 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); |
| 1468 current = current.superclass; | 1469 current = current.superclass; |
| 1469 } while (!current.isObject && !visited.contains(current)); | 1470 } while (!current.isObject && !visited.contains(current)); |
| 1470 } | 1471 } |
| 1471 } | 1472 } |
| OLD | NEW |