Index: lib/src/checker/checker.dart |
diff --git a/lib/src/checker/checker.dart b/lib/src/checker/checker.dart |
index 9d328979a5677938e33542f0a601ec3f1ab943b3..78b1a20e68d0344c324558a1439fda3877777e07 100644 |
--- a/lib/src/checker/checker.dart |
+++ b/lib/src/checker/checker.dart |
@@ -348,7 +348,7 @@ class CodeChecker extends RecursiveAstVisitor { |
_overrideChecker = new _OverrideChecker(rules, reporter, options); |
@override |
- visitCompilationUnit(CompilationUnit unit) { |
+ void visitCompilationUnit(CompilationUnit unit) { |
void report(Expression expr) { |
_reporter.log(new MissingTypeError(expr)); |
} |
@@ -379,17 +379,20 @@ class CodeChecker extends RecursiveAstVisitor { |
_constantContext = o; |
} |
- visitComment(Comment node) { |
+ @override |
+ void visitComment(Comment node) { |
// skip, no need to do typechecking inside comments (they may contain |
// comment references which would require resolution). |
} |
- visitClassDeclaration(ClassDeclaration node) { |
+ @override |
+ void visitClassDeclaration(ClassDeclaration node) { |
_overrideChecker.check(node); |
super.visitClassDeclaration(node); |
} |
- visitAssignmentExpression(AssignmentExpression node) { |
+ @override |
+ void visitAssignmentExpression(AssignmentExpression node) { |
var token = node.operator; |
if (token.type != TokenType.EQ) { |
_checkCompoundAssignment(node); |
@@ -402,7 +405,7 @@ class CodeChecker extends RecursiveAstVisitor { |
/// Check constructor declaration to ensure correct super call placement. |
@override |
- visitConstructorDeclaration(ConstructorDeclaration node) { |
+ void visitConstructorDeclaration(ConstructorDeclaration node) { |
_visitMaybeConst(node, (node) { |
node.visitChildren(this); |
@@ -417,7 +420,7 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
- visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
+ void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
var field = node.fieldName; |
DartType staticType = _rules.elementType(field.staticElement); |
node.expression = checkAssignment(node.expression, staticType); |
@@ -425,7 +428,7 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
- visitForEachStatement(ForEachStatement node) { |
+ void visitForEachStatement(ForEachStatement node) { |
// Check that the expression is an Iterable. |
var expr = node.iterable; |
var iterableType = _rules.provider.iterableType; |
@@ -438,7 +441,43 @@ class CodeChecker extends RecursiveAstVisitor { |
node.visitChildren(this); |
} |
- @override visitListLiteral(ListLiteral node) { |
+ @override |
+ void visitForStatement(ForStatement node) { |
+ if (node.condition != null) { |
+ node.condition = checkBoolean(node.condition); |
+ } |
+ node.visitChildren(this); |
+ } |
+ |
+ @override |
+ void visitIfStatement(IfStatement node) { |
+ node.condition = checkBoolean(node.condition); |
+ node.visitChildren(this); |
+ } |
+ |
+ @override |
+ void visitDoStatement(DoStatement node) { |
+ node.condition = checkBoolean(node.condition); |
+ node.visitChildren(this); |
+ } |
+ |
+ @override |
+ void visitWhileStatement(WhileStatement node) { |
+ node.condition = checkBoolean(node.condition); |
+ node.visitChildren(this); |
+ } |
+ |
+ @override |
+ void visitSwitchStatement(SwitchStatement node) { |
+ // SwitchStatement defines a boolean conversion to check the result of the |
+ // case value == the switch value, but in dev_compiler we require a boolean |
+ // return type from an overridden == operator (because Object.==), so |
+ // checking in SwitchStatement shouldn't be necessary. |
+ node.visitChildren(this); |
+ } |
+ |
+ @override |
+ void visitListLiteral(ListLiteral node) { |
var type = _rules.provider.dynamicType; |
if (node.typeArguments != null) { |
var targs = node.typeArguments.arguments; |
@@ -451,7 +490,8 @@ class CodeChecker extends RecursiveAstVisitor { |
super.visitListLiteral(node); |
} |
- @override visitMapLiteral(MapLiteral node) { |
+ @override |
+ void visitMapLiteral(MapLiteral node) { |
var ktype = _rules.provider.dynamicType; |
var vtype = _rules.provider.dynamicType; |
if (node.typeArguments != null) { |
@@ -513,24 +553,29 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
} |
- visitMethodInvocation(MethodInvocation node) { |
+ @override |
+ void visitMethodInvocation(MethodInvocation node) { |
checkFunctionApplication(node, node.methodName, node.argumentList); |
node.visitChildren(this); |
} |
- visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
+ @override |
+ void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
checkFunctionApplication(node, node.function, node.argumentList); |
node.visitChildren(this); |
} |
- visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) { |
+ @override |
+ void visitRedirectingConstructorInvocation( |
+ RedirectingConstructorInvocation node) { |
var type = node.staticElement.type; |
bool checked = checkArgumentList(node.argumentList, type); |
assert(checked); |
node.visitChildren(this); |
} |
- visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
+ @override |
+ void visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
var element = node.staticElement; |
if (element == null) { |
_recordMessage(new MissingTypeError(node)); |
@@ -568,24 +613,28 @@ class CodeChecker extends RecursiveAstVisitor { |
return checkAssignment(expression, type); |
} |
- visitExpressionFunctionBody(ExpressionFunctionBody node) { |
+ @override |
+ void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
node.expression = _checkReturn(node.expression, node); |
node.visitChildren(this); |
} |
- visitReturnStatement(ReturnStatement node) { |
+ @override |
+ void visitReturnStatement(ReturnStatement node) { |
node.expression = _checkReturn(node.expression, node); |
node.visitChildren(this); |
} |
- visitPropertyAccess(PropertyAccess node) { |
+ @override |
+ void visitPropertyAccess(PropertyAccess node) { |
if (node.staticType.isDynamic && _rules.isDynamicTarget(node.realTarget)) { |
_recordDynamicInvoke(node); |
} |
node.visitChildren(this); |
} |
- visitPrefixedIdentifier(PrefixedIdentifier node) { |
+ @override |
+ void visitPrefixedIdentifier(PrefixedIdentifier node) { |
final target = node.prefix; |
if (_rules.isDynamicTarget(target)) { |
_recordDynamicInvoke(node); |
@@ -593,7 +642,8 @@ class CodeChecker extends RecursiveAstVisitor { |
node.visitChildren(this); |
} |
- @override visitDefaultFormalParameter(DefaultFormalParameter node) { |
+ @override |
+ void visitDefaultFormalParameter(DefaultFormalParameter node) { |
_visitMaybeConst(node, (node) { |
// Check that defaults have the proper subtype. |
var parameter = node.parameter; |
@@ -615,7 +665,8 @@ class CodeChecker extends RecursiveAstVisitor { |
}); |
} |
- visitFieldFormalParameter(FieldFormalParameter node) { |
+ @override |
+ void visitFieldFormalParameter(FieldFormalParameter node) { |
var element = node.element; |
var typeName = node.type; |
if (typeName != null) { |
@@ -633,7 +684,7 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
- visitInstanceCreationExpression(InstanceCreationExpression node) { |
+ void visitInstanceCreationExpression(InstanceCreationExpression node) { |
_visitMaybeConst(node, (node) { |
var arguments = node.argumentList; |
var element = node.staticElement; |
@@ -648,7 +699,7 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
- visitVariableDeclarationList(VariableDeclarationList node) { |
+ void visitVariableDeclarationList(VariableDeclarationList node) { |
_visitMaybeConst(node, (node) { |
TypeName type = node.type; |
if (type == null) { |
@@ -682,7 +733,7 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
- visitVariableDeclaration(VariableDeclaration node) { |
+ void visitVariableDeclaration(VariableDeclaration node) { |
_visitMaybeConst(node, super.visitVariableDeclaration); |
} |
@@ -693,19 +744,52 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
} |
- visitAsExpression(AsExpression node) { |
+ @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 |
// if the Dart as and the DDC as would return different values. |
node.visitChildren(this); |
} |
- visitIsExpression(IsExpression node) { |
+ @override |
+ void visitIsExpression(IsExpression node) { |
_checkRuntimeTypeCheck(node, node.type); |
node.visitChildren(this); |
} |
- visitBinaryExpression(BinaryExpression node) { |
+ @override |
+ void visitPrefixExpression(PrefixExpression node) { |
+ if (node.operator.type == TokenType.BANG) { |
+ node.operand = checkBoolean(node.operand); |
+ } else { |
+ _checkUnary(node); |
+ } |
+ node.visitChildren(this); |
+ } |
+ |
+ @override |
+ void visitPostfixExpression(PostfixExpression node) { |
+ _checkUnary(node); |
+ node.visitChildren(this); |
+ } |
+ |
+ void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { |
+ var op = node.operator; |
+ if (op.isUserDefinableOperator || |
+ op.type == TokenType.PLUS_PLUS || |
+ op.type == TokenType.MINUS_MINUS) { |
+ if (_rules.isDynamicTarget(node.operand)) { |
+ _recordDynamicInvoke(node); |
+ } |
+ // For ++ and --, even if it is not dynamic, we still need to check |
+ // that the user defined method accepts an `int` as the RHS. |
+ // We assume Analyzer has done this already. |
+ } |
+ } |
+ |
+ @override |
+ void visitBinaryExpression(BinaryExpression node) { |
var op = node.operator; |
if (op.isUserDefinableOperator) { |
if (_rules.isDynamicTarget(node.leftOperand)) { |
@@ -734,9 +818,8 @@ class CodeChecker extends RecursiveAstVisitor { |
switch (op.type) { |
case TokenType.AMPERSAND_AMPERSAND: |
case TokenType.BAR_BAR: |
- var boolType = _rules.provider.boolType; |
- node.leftOperand = checkArgument(node.leftOperand, boolType); |
- node.rightOperand = checkArgument(node.rightOperand, boolType); |
+ node.leftOperand = checkBoolean(node.leftOperand); |
+ node.rightOperand = checkBoolean(node.rightOperand); |
break; |
case TokenType.BANG_EQ: |
break; |
@@ -748,6 +831,12 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
+ void visitConditionalExpression(ConditionalExpression node) { |
+ node.condition = checkBoolean(node.condition); |
+ node.visitChildren(this); |
+ } |
+ |
+ @override |
void visitIndexExpression(IndexExpression node) { |
if (_rules.isDynamicTarget(node.target)) { |
_recordDynamicInvoke(node); |
@@ -771,6 +860,12 @@ class CodeChecker extends RecursiveAstVisitor { |
return (name == null) ? _rules.provider.dynamicType : name.type; |
} |
+ /// Analyzer checks boolean conversions, but we need to check too, because |
+ /// it uses the default assignability rules that allow `dynamic` and `Object` |
+ /// to be assigned to bool with no message. |
+ Expression checkBoolean(Expression expr) => |
+ checkAssignment(expr, _rules.provider.boolType); |
+ |
Expression checkAssignment(Expression expr, DartType type) { |
if (expr is ParenthesizedExpression) { |
expr.expression = checkAssignment(expr.expression, type); |