Index: pkg/analyzer/lib/src/task/strong/checker.dart |
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart |
index 98c4aa6a97dc640c90891e60bdefcfb186c215ad..21281719674209128269d53a0165ed8d8cab6fd2 100644 |
--- a/pkg/analyzer/lib/src/task/strong/checker.dart |
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart |
@@ -9,6 +9,7 @@ library analyzer.src.task.strong.checker; |
import 'package:analyzer/analyzer.dart'; |
import 'package:analyzer/dart/ast/ast.dart'; |
import 'package:analyzer/dart/ast/token.dart' show TokenType; |
+import 'package:analyzer/dart/ast/token.dart'; |
import 'package:analyzer/dart/ast/visitor.dart'; |
import 'package:analyzer/dart/element/element.dart'; |
import 'package:analyzer/dart/element/type.dart'; |
@@ -20,6 +21,21 @@ import 'package:analyzer/src/generated/type_system.dart'; |
import 'ast_properties.dart'; |
+bool isKnownFunction(Expression expression) { |
+ Element element = null; |
+ if (expression is FunctionExpression) { |
+ return true; |
+ } else if (expression is PropertyAccess) { |
+ element = expression.propertyName.staticElement; |
+ } else if (expression is Identifier) { |
+ element = expression.staticElement; |
+ } |
+// First class functions and static methods, where we know the original |
+// declaration, will have an exact type, so we know a downcast will fail. |
+ return element is FunctionElement || |
+ element is MethodElement && element.isStatic; |
+} |
+ |
DartType _elementType(Element e) { |
if (e == null) { |
// Malformed code - just return dynamic. |
@@ -28,6 +44,8 @@ DartType _elementType(Element e) { |
return (e as dynamic).type; |
} |
+// Return the field on type corresponding to member, or null if none |
+// exists or the "field" is actually a getter/setter. |
scheglov
2016/08/02 15:25:03
Make this a documentation comment?
Brian Wilkerson
2016/08/02 15:33:53
Or delete it. I didn't add this comment, it was mo
|
PropertyInducingElement _getMemberField( |
InterfaceType type, PropertyAccessorElement member) { |
String memberName = member.name; |
@@ -53,8 +71,6 @@ PropertyInducingElement _getMemberField( |
return field; |
} |
-// Return the field on type corresponding to member, or null if none |
-// exists or the "field" is actually a getter/setter. |
/// Looks up the declaration that matches [member] in [type] and returns it's |
/// declared type. |
FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => |
@@ -176,13 +192,6 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
- void visitCompilationUnit(CompilationUnit node) { |
- _hasImplicitCasts = false; |
- node.visitChildren(this); |
- setHasImplicitCasts(node, _hasImplicitCasts); |
- } |
- |
- @override |
void visitAsExpression(AsExpression node) { |
// We could do the same check as the IsExpression below, but that is |
// potentially too conservative. Instead, at runtime, we must fail hard |
@@ -192,11 +201,16 @@ class CodeChecker extends RecursiveAstVisitor { |
@override |
void visitAssignmentExpression(AssignmentExpression node) { |
- var token = node.operator; |
- if (token.type == TokenType.EQ || |
- token.type == TokenType.QUESTION_QUESTION_EQ) { |
+ Token operator = node.operator; |
+ TokenType operatorType = operator.type; |
+ if (operatorType == TokenType.EQ || |
+ operatorType == TokenType.QUESTION_QUESTION_EQ) { |
DartType staticType = _getStaticType(node.leftHandSide); |
checkAssignment(node.rightHandSide, staticType); |
+ } else if (operatorType == TokenType.AMPERSAND_AMPERSAND_EQ || |
+ operatorType == TokenType.BAR_BAR_EQ) { |
+ checkAssignment(node.leftHandSide, typeProvider.boolType); |
+ checkAssignment(node.rightHandSide, typeProvider.boolType); |
} else { |
_checkCompoundAssignment(node); |
} |
@@ -259,6 +273,13 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
+ void visitCompilationUnit(CompilationUnit node) { |
+ _hasImplicitCasts = false; |
+ node.visitChildren(this); |
+ setHasImplicitCasts(node, _hasImplicitCasts); |
+ } |
+ |
+ @override |
void visitConditionalExpression(ConditionalExpression node) { |
checkBoolean(node.condition); |
node.visitChildren(this); |
@@ -853,90 +874,6 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
} |
- /// Records an implicit cast for the [expression] from [fromType] to [toType]. |
- /// |
- /// This will emit the appropriate error/warning/hint message as well as mark |
- /// the AST node. |
- void _recordImplicitCast( |
- Expression expression, DartType fromType, DartType toType) { |
- // toT <:_R fromT => to <: fromT |
- // NB: classes with call methods are subtypes of function |
- // types, but the function type is not assignable to the class |
- assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); |
- |
- // Inference "casts": |
- if (expression is Literal || expression is FunctionExpression) { |
- // fromT should be an exact type - this will almost certainly fail at |
- // runtime. |
- _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
- [expression, fromType, toType]); |
- return; |
- } |
- |
- if (expression is InstanceCreationExpression) { |
- ConstructorElement e = expression.staticElement; |
- if (e == null || !e.isFactory) { |
- // fromT should be an exact type - this will almost certainly fail at |
- // runtime. |
- |
- _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
- [expression, fromType, toType]); |
- return; |
- } |
- } |
- |
- if (isKnownFunction(expression)) { |
- _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
- [expression, fromType, toType]); |
- return; |
- } |
- |
- // TODO(vsm): Change this to an assert when we have generic methods and |
- // fix TypeRules._coerceTo to disallow implicit sideways casts. |
- bool downCastComposite = false; |
- if (!rules.isSubtypeOf(toType, fromType)) { |
- assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); |
- downCastComposite = true; |
- } |
- |
- // Composite cast: these are more likely to fail. |
- if (!rules.isGroundType(toType)) { |
- // This cast is (probably) due to our different treatment of dynamic. |
- // It may be more likely to fail at runtime. |
- if (fromType is InterfaceType) { |
- // For class types, we'd like to allow non-generic down casts, e.g., |
- // Iterable<T> to List<T>. The intuition here is that raw (generic) |
- // casts are problematic, and we should complain about those. |
- var typeArgs = fromType.typeArguments; |
- downCastComposite = |
- typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic); |
- } else { |
- downCastComposite = true; |
- } |
- } |
- |
- var parent = expression.parent; |
- ErrorCode errorCode; |
- if (downCastComposite) { |
- errorCode = StrongModeCode.DOWN_CAST_COMPOSITE; |
- } else if (fromType.isDynamic) { |
- errorCode = StrongModeCode.DYNAMIC_CAST; |
- } else if (parent is VariableDeclaration && |
- parent.initializer == expression) { |
- errorCode = StrongModeCode.ASSIGNMENT_CAST; |
- } else { |
- errorCode = StrongModeCode.DOWN_CAST_IMPLICIT; |
- } |
- |
- _recordMessage(expression, errorCode, [fromType, toType]); |
- setImplicitCast(expression, toType); |
- _hasImplicitCasts = true; |
- } |
- |
- // Produce a coercion which coerces something of type fromT |
- // to something of type toT. |
- // Returns the error coercion if the types cannot be coerced |
- // according to our current criteria. |
/// Gets the expected return type of the given function [body], either from |
/// a normal return/yield, or from a yield*. |
DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
@@ -991,6 +928,10 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
} |
+ // Produce a coercion which coerces something of type fromT |
+ // to something of type toT. |
+ // Returns the error coercion if the types cannot be coerced |
+ // according to our current criteria. |
scheglov
2016/08/02 15:25:03
Make this a documentation comment?
Brian Wilkerson
2016/08/02 15:33:53
I didn't write this comment either, it was also mo
|
DartType _getStaticType(Expression expr) { |
DartType t = expr.staticType ?? DynamicTypeImpl.instance; |
@@ -1085,6 +1026,86 @@ class CodeChecker extends RecursiveAstVisitor { |
setIsDynamicInvoke(target, true); |
} |
+ /// Records an implicit cast for the [expression] from [fromType] to [toType]. |
+ /// |
+ /// This will emit the appropriate error/warning/hint message as well as mark |
+ /// the AST node. |
+ void _recordImplicitCast( |
+ Expression expression, DartType fromType, DartType toType) { |
+ // toT <:_R fromT => to <: fromT |
+ // NB: classes with call methods are subtypes of function |
+ // types, but the function type is not assignable to the class |
+ assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); |
+ |
+ // Inference "casts": |
+ if (expression is Literal || expression is FunctionExpression) { |
+ // fromT should be an exact type - this will almost certainly fail at |
+ // runtime. |
+ _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
+ [expression, fromType, toType]); |
+ return; |
+ } |
+ |
+ if (expression is InstanceCreationExpression) { |
+ ConstructorElement e = expression.staticElement; |
+ if (e == null || !e.isFactory) { |
+ // fromT should be an exact type - this will almost certainly fail at |
+ // runtime. |
+ |
+ _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
+ [expression, fromType, toType]); |
+ return; |
+ } |
+ } |
+ |
+ if (isKnownFunction(expression)) { |
+ _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
+ [expression, fromType, toType]); |
+ return; |
+ } |
+ |
+ // TODO(vsm): Change this to an assert when we have generic methods and |
+ // fix TypeRules._coerceTo to disallow implicit sideways casts. |
+ bool downCastComposite = false; |
+ if (!rules.isSubtypeOf(toType, fromType)) { |
+ assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); |
+ downCastComposite = true; |
+ } |
+ |
+ // Composite cast: these are more likely to fail. |
+ if (!rules.isGroundType(toType)) { |
+ // This cast is (probably) due to our different treatment of dynamic. |
+ // It may be more likely to fail at runtime. |
+ if (fromType is InterfaceType) { |
+ // For class types, we'd like to allow non-generic down casts, e.g., |
+ // Iterable<T> to List<T>. The intuition here is that raw (generic) |
+ // casts are problematic, and we should complain about those. |
+ var typeArgs = fromType.typeArguments; |
+ downCastComposite = |
+ typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic); |
+ } else { |
+ downCastComposite = true; |
+ } |
+ } |
+ |
+ var parent = expression.parent; |
+ ErrorCode errorCode; |
+ if (downCastComposite) { |
+ errorCode = StrongModeCode.DOWN_CAST_COMPOSITE; |
+ } else if (fromType.isDynamic) { |
+ errorCode = StrongModeCode.DYNAMIC_CAST; |
+ } else if (parent is VariableDeclaration && |
+ parent.initializer == expression) { |
+ errorCode = StrongModeCode.ASSIGNMENT_CAST; |
+ } else { |
+ errorCode = StrongModeCode.DOWN_CAST_IMPLICIT; |
+ } |
+ |
+ _recordMessage(expression, errorCode, [fromType, toType]); |
+ setImplicitCast(expression, toType); |
+ _hasImplicitCasts = true; |
+ } |
+ |
void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { |
var severity = errorCode.errorSeverity; |
if (severity == ErrorSeverity.ERROR) _failure = true; |
@@ -1101,21 +1122,6 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
} |
-bool isKnownFunction(Expression expression) { |
- Element element = null; |
- if (expression is FunctionExpression) { |
- return true; |
- } else if (expression is PropertyAccess) { |
- element = expression.propertyName.staticElement; |
- } else if (expression is Identifier) { |
- element = expression.staticElement; |
- } |
-// First class functions and static methods, where we know the original |
-// declaration, will have an exact type, so we know a downcast will fail. |
- return element is FunctionElement || |
- element is MethodElement && element.isStatic; |
-} |
- |
/// Checks for overriding declarations of fields and methods. This is used to |
/// check overrides between classes and superclasses, interfaces, and mixin |
/// applications. |