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 |