| 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 library dev_compiler.src.checker.checker; | 5 library dev_compiler.src.checker.checker; |
| 6 | 6 |
| 7 import 'package:analyzer/analyzer.dart'; | 7 import 'package:analyzer/analyzer.dart'; |
| 8 import 'package:analyzer/src/generated/ast.dart'; | 8 import 'package:analyzer/src/generated/ast.dart'; |
| 9 import 'package:analyzer/src/generated/element.dart'; | 9 import 'package:analyzer/src/generated/element.dart'; |
| 10 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; | 10 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; |
| (...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 super.visitClassDeclaration(node); | 391 super.visitClassDeclaration(node); |
| 392 } | 392 } |
| 393 | 393 |
| 394 @override | 394 @override |
| 395 void visitAssignmentExpression(AssignmentExpression node) { | 395 void visitAssignmentExpression(AssignmentExpression node) { |
| 396 var token = node.operator; | 396 var token = node.operator; |
| 397 if (token.type != TokenType.EQ) { | 397 if (token.type != TokenType.EQ) { |
| 398 _checkCompoundAssignment(node); | 398 _checkCompoundAssignment(node); |
| 399 } else { | 399 } else { |
| 400 DartType staticType = _rules.getStaticType(node.leftHandSide); | 400 DartType staticType = _rules.getStaticType(node.leftHandSide); |
| 401 node.rightHandSide = checkAssignment(node.rightHandSide, staticType); | 401 checkAssignment(node.rightHandSide, staticType); |
| 402 } | 402 } |
| 403 node.visitChildren(this); | 403 node.visitChildren(this); |
| 404 } | 404 } |
| 405 | 405 |
| 406 /// Check constructor declaration to ensure correct super call placement. | 406 /// Check constructor declaration to ensure correct super call placement. |
| 407 @override | 407 @override |
| 408 void visitConstructorDeclaration(ConstructorDeclaration node) { | 408 void visitConstructorDeclaration(ConstructorDeclaration node) { |
| 409 _visitMaybeConst(node, (node) { | 409 _visitMaybeConst(node, (node) { |
| 410 node.visitChildren(this); | 410 node.visitChildren(this); |
| 411 | 411 |
| 412 final init = node.initializers; | 412 final init = node.initializers; |
| 413 for (int i = 0, last = init.length - 1; i < last; i++) { | 413 for (int i = 0, last = init.length - 1; i < last; i++) { |
| 414 final node = init[i]; | 414 final node = init[i]; |
| 415 if (node is SuperConstructorInvocation) { | 415 if (node is SuperConstructorInvocation) { |
| 416 _recordMessage(new InvalidSuperInvocation(node)); | 416 _recordMessage(new InvalidSuperInvocation(node)); |
| 417 } | 417 } |
| 418 } | 418 } |
| 419 }); | 419 }); |
| 420 } | 420 } |
| 421 | 421 |
| 422 @override | 422 @override |
| 423 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | 423 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 424 var field = node.fieldName; | 424 var field = node.fieldName; |
| 425 DartType staticType = _rules.elementType(field.staticElement); | 425 DartType staticType = _rules.elementType(field.staticElement); |
| 426 node.expression = checkAssignment(node.expression, staticType); | 426 checkAssignment(node.expression, staticType); |
| 427 node.visitChildren(this); | 427 node.visitChildren(this); |
| 428 } | 428 } |
| 429 | 429 |
| 430 @override | 430 @override |
| 431 void visitForEachStatement(ForEachStatement node) { | 431 void visitForEachStatement(ForEachStatement node) { |
| 432 // Check that the expression is an Iterable. | 432 // Check that the expression is an Iterable. |
| 433 var expr = node.iterable; | 433 var expr = node.iterable; |
| 434 var iterableType = _rules.provider.iterableType; | 434 var iterableType = _rules.provider.iterableType; |
| 435 var loopVariable = node.identifier != null | 435 var loopVariable = node.identifier != null |
| 436 ? node.identifier | 436 ? node.identifier |
| 437 : node.loopVariable.identifier; | 437 : node.loopVariable.identifier; |
| 438 var iteratorType = loopVariable.staticType; | 438 var iteratorType = loopVariable.staticType; |
| 439 var checkedType = iterableType.substitute4([iteratorType]); | 439 var checkedType = iterableType.substitute4([iteratorType]); |
| 440 node.iterable = checkAssignment(expr, checkedType); | 440 checkAssignment(expr, checkedType); |
| 441 node.visitChildren(this); | 441 node.visitChildren(this); |
| 442 } | 442 } |
| 443 | 443 |
| 444 @override | 444 @override |
| 445 void visitForStatement(ForStatement node) { | 445 void visitForStatement(ForStatement node) { |
| 446 if (node.condition != null) { | 446 if (node.condition != null) { |
| 447 node.condition = checkBoolean(node.condition); | 447 checkBoolean(node.condition); |
| 448 } | 448 } |
| 449 node.visitChildren(this); | 449 node.visitChildren(this); |
| 450 } | 450 } |
| 451 | 451 |
| 452 @override | 452 @override |
| 453 void visitIfStatement(IfStatement node) { | 453 void visitIfStatement(IfStatement node) { |
| 454 node.condition = checkBoolean(node.condition); | 454 checkBoolean(node.condition); |
| 455 node.visitChildren(this); | 455 node.visitChildren(this); |
| 456 } | 456 } |
| 457 | 457 |
| 458 @override | 458 @override |
| 459 void visitDoStatement(DoStatement node) { | 459 void visitDoStatement(DoStatement node) { |
| 460 node.condition = checkBoolean(node.condition); | 460 checkBoolean(node.condition); |
| 461 node.visitChildren(this); | 461 node.visitChildren(this); |
| 462 } | 462 } |
| 463 | 463 |
| 464 @override | 464 @override |
| 465 void visitWhileStatement(WhileStatement node) { | 465 void visitWhileStatement(WhileStatement node) { |
| 466 node.condition = checkBoolean(node.condition); | 466 checkBoolean(node.condition); |
| 467 node.visitChildren(this); | 467 node.visitChildren(this); |
| 468 } | 468 } |
| 469 | 469 |
| 470 @override | 470 @override |
| 471 void visitSwitchStatement(SwitchStatement node) { | 471 void visitSwitchStatement(SwitchStatement node) { |
| 472 // SwitchStatement defines a boolean conversion to check the result of the | 472 // SwitchStatement defines a boolean conversion to check the result of the |
| 473 // case value == the switch value, but in dev_compiler we require a boolean | 473 // case value == the switch value, but in dev_compiler we require a boolean |
| 474 // return type from an overridden == operator (because Object.==), so | 474 // return type from an overridden == operator (because Object.==), so |
| 475 // checking in SwitchStatement shouldn't be necessary. | 475 // checking in SwitchStatement shouldn't be necessary. |
| 476 node.visitChildren(this); | 476 node.visitChildren(this); |
| 477 } | 477 } |
| 478 | 478 |
| 479 @override | 479 @override |
| 480 void visitListLiteral(ListLiteral node) { | 480 void visitListLiteral(ListLiteral node) { |
| 481 var type = _rules.provider.dynamicType; | 481 var type = _rules.provider.dynamicType; |
| 482 if (node.typeArguments != null) { | 482 if (node.typeArguments != null) { |
| 483 var targs = node.typeArguments.arguments; | 483 var targs = node.typeArguments.arguments; |
| 484 if (targs.length > 0) type = targs[0].type; | 484 if (targs.length > 0) type = targs[0].type; |
| 485 } | 485 } |
| 486 var elements = node.elements; | 486 var elements = node.elements; |
| 487 for (int i = 0; i < elements.length; i++) { | 487 for (int i = 0; i < elements.length; i++) { |
| 488 elements[i] = checkArgument(elements[i], type); | 488 checkArgument(elements[i], type); |
| 489 } | 489 } |
| 490 super.visitListLiteral(node); | 490 super.visitListLiteral(node); |
| 491 } | 491 } |
| 492 | 492 |
| 493 @override | 493 @override |
| 494 void visitMapLiteral(MapLiteral node) { | 494 void visitMapLiteral(MapLiteral node) { |
| 495 var ktype = _rules.provider.dynamicType; | 495 var ktype = _rules.provider.dynamicType; |
| 496 var vtype = _rules.provider.dynamicType; | 496 var vtype = _rules.provider.dynamicType; |
| 497 if (node.typeArguments != null) { | 497 if (node.typeArguments != null) { |
| 498 var targs = node.typeArguments.arguments; | 498 var targs = node.typeArguments.arguments; |
| 499 if (targs.length > 0) ktype = targs[0].type; | 499 if (targs.length > 0) ktype = targs[0].type; |
| 500 if (targs.length > 1) vtype = targs[1].type; | 500 if (targs.length > 1) vtype = targs[1].type; |
| 501 } | 501 } |
| 502 var entries = node.entries; | 502 var entries = node.entries; |
| 503 for (int i = 0; i < entries.length; i++) { | 503 for (int i = 0; i < entries.length; i++) { |
| 504 var entry = entries[i]; | 504 var entry = entries[i]; |
| 505 entry.key = checkArgument(entry.key, ktype); | 505 checkArgument(entry.key, ktype); |
| 506 entry.value = checkArgument(entry.value, vtype); | 506 checkArgument(entry.value, vtype); |
| 507 } | 507 } |
| 508 super.visitMapLiteral(node); | 508 super.visitMapLiteral(node); |
| 509 } | 509 } |
| 510 | 510 |
| 511 // Check invocations | 511 // Check invocations |
| 512 bool checkArgumentList(ArgumentList node, FunctionType type) { | 512 void checkArgumentList(ArgumentList node, FunctionType type) { |
| 513 NodeList<Expression> list = node.arguments; | 513 NodeList<Expression> list = node.arguments; |
| 514 int len = list.length; | 514 int len = list.length; |
| 515 for (int i = 0; i < len; ++i) { | 515 for (int i = 0; i < len; ++i) { |
| 516 Expression arg = list[i]; | 516 Expression arg = list[i]; |
| 517 ParameterElement element = arg.staticParameterElement; | 517 ParameterElement element = arg.staticParameterElement; |
| 518 if (element == null) { | 518 if (element == null) { |
| 519 if (type.parameters.length < len) { | 519 if (type.parameters.length < len) { |
| 520 // We found an argument mismatch, the analyzer will report this too, | 520 // We found an argument mismatch, the analyzer will report this too, |
| 521 // so no need to insert an error for this here. | 521 // so no need to insert an error for this here. |
| 522 continue; | 522 continue; |
| 523 } | 523 } |
| 524 element = type.parameters[i]; | 524 element = type.parameters[i]; |
| 525 // TODO(vsm): When can this happen? | 525 // TODO(vsm): When can this happen? |
| 526 assert(element != null); | 526 assert(element != null); |
| 527 } | 527 } |
| 528 DartType expectedType = _rules.elementType(element); | 528 DartType expectedType = _rules.elementType(element); |
| 529 if (expectedType == null) expectedType = _rules.provider.dynamicType; | 529 if (expectedType == null) expectedType = _rules.provider.dynamicType; |
| 530 list[i] = checkArgument(arg, expectedType); | 530 checkArgument(arg, expectedType); |
| 531 } | 531 } |
| 532 return true; | |
| 533 } | 532 } |
| 534 | 533 |
| 535 Expression checkArgument(Expression arg, DartType expectedType) { | 534 void checkArgument(Expression arg, DartType expectedType) { |
| 536 // Preserve named argument structure, so their immediate parent is the | 535 // Preserve named argument structure, so their immediate parent is the |
| 537 // method invocation. | 536 // method invocation. |
| 538 if (arg is NamedExpression) { | 537 if (arg is NamedExpression) { |
| 539 arg.expression = checkAssignment(arg.expression, expectedType); | 538 arg = (arg as NamedExpression).expression; |
| 540 return arg; | |
| 541 } | 539 } |
| 542 return checkAssignment(arg, expectedType); | 540 checkAssignment(arg, expectedType); |
| 543 } | 541 } |
| 544 | 542 |
| 545 void checkFunctionApplication( | 543 void checkFunctionApplication( |
| 546 Expression node, Expression f, ArgumentList list) { | 544 Expression node, Expression f, ArgumentList list) { |
| 547 if (_rules.isDynamicCall(f)) { | 545 if (_rules.isDynamicCall(f)) { |
| 548 // If f is Function and this is a method invocation, we should have | 546 // If f is Function and this is a method invocation, we should have |
| 549 // gotten an analyzer error, so no need to issue another error. | 547 // gotten an analyzer error, so no need to issue another error. |
| 550 _recordDynamicInvoke(node); | 548 _recordDynamicInvoke(node); |
| 551 } else { | 549 } else { |
| 552 checkArgumentList(list, _rules.getTypeAsCaller(f)); | 550 checkArgumentList(list, _rules.getTypeAsCaller(f)); |
| 553 } | 551 } |
| 554 } | 552 } |
| 555 | 553 |
| 556 @override | 554 @override |
| 557 void visitMethodInvocation(MethodInvocation node) { | 555 void visitMethodInvocation(MethodInvocation node) { |
| 558 checkFunctionApplication(node, node.methodName, node.argumentList); | 556 checkFunctionApplication(node, node.methodName, node.argumentList); |
| 559 node.visitChildren(this); | 557 node.visitChildren(this); |
| 560 } | 558 } |
| 561 | 559 |
| 562 @override | 560 @override |
| 563 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | 561 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| 564 checkFunctionApplication(node, node.function, node.argumentList); | 562 checkFunctionApplication(node, node.function, node.argumentList); |
| 565 node.visitChildren(this); | 563 node.visitChildren(this); |
| 566 } | 564 } |
| 567 | 565 |
| 568 @override | 566 @override |
| 569 void visitRedirectingConstructorInvocation( | 567 void visitRedirectingConstructorInvocation( |
| 570 RedirectingConstructorInvocation node) { | 568 RedirectingConstructorInvocation node) { |
| 571 var type = node.staticElement.type; | 569 var type = node.staticElement.type; |
| 572 bool checked = checkArgumentList(node.argumentList, type); | 570 checkArgumentList(node.argumentList, type); |
| 573 assert(checked); | |
| 574 node.visitChildren(this); | 571 node.visitChildren(this); |
| 575 } | 572 } |
| 576 | 573 |
| 577 @override | 574 @override |
| 578 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { | 575 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| 579 var element = node.staticElement; | 576 var element = node.staticElement; |
| 580 if (element == null) { | 577 if (element == null) { |
| 581 _recordMessage(new MissingTypeError(node)); | 578 _recordMessage(new MissingTypeError(node)); |
| 582 } else { | 579 } else { |
| 583 var type = node.staticElement.type; | 580 var type = node.staticElement.type; |
| 584 bool checked = checkArgumentList(node.argumentList, type); | 581 checkArgumentList(node.argumentList, type); |
| 585 assert(checked); | |
| 586 } | 582 } |
| 587 node.visitChildren(this); | 583 node.visitChildren(this); |
| 588 } | 584 } |
| 589 | 585 |
| 590 AstNode _getOwnerFunction(AstNode node) { | 586 AstNode _getOwnerFunction(AstNode node) { |
| 591 var parent = node.parent; | 587 var parent = node.parent; |
| 592 while (parent is! FunctionExpression && | 588 while (parent is! FunctionExpression && |
| 593 parent is! MethodDeclaration && | 589 parent is! MethodDeclaration && |
| 594 parent is! ConstructorDeclaration) { | 590 parent is! ConstructorDeclaration) { |
| 595 parent = parent.parent; | 591 parent = parent.parent; |
| 596 } | 592 } |
| 597 return parent; | 593 return parent; |
| 598 } | 594 } |
| 599 | 595 |
| 600 FunctionType _getFunctionType(AstNode node) { | 596 FunctionType _getFunctionType(AstNode node) { |
| 601 if (node is Declaration) { | 597 if (node is Declaration) { |
| 602 return _rules.elementType(node.element); | 598 return _rules.elementType(node.element); |
| 603 } else { | 599 } else { |
| 604 assert(node is FunctionExpression); | 600 assert(node is FunctionExpression); |
| 605 return _rules.getStaticType(node); | 601 return _rules.getStaticType(node); |
| 606 } | 602 } |
| 607 } | 603 } |
| 608 | 604 |
| 609 Expression _checkReturn(Expression expression, AstNode node) { | 605 void _checkReturn(Expression expression, AstNode node) { |
| 610 var type = _getFunctionType(_getOwnerFunction(node)).returnType; | 606 var type = _getFunctionType(_getOwnerFunction(node)).returnType; |
| 611 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. | 607 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. |
| 612 if (expression == null) return null; | 608 if (expression != null) checkAssignment(expression, type); |
| 613 return checkAssignment(expression, type); | |
| 614 } | 609 } |
| 615 | 610 |
| 616 @override | 611 @override |
| 617 void visitExpressionFunctionBody(ExpressionFunctionBody node) { | 612 void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 618 node.expression = _checkReturn(node.expression, node); | 613 _checkReturn(node.expression, node); |
| 619 node.visitChildren(this); | 614 node.visitChildren(this); |
| 620 } | 615 } |
| 621 | 616 |
| 622 @override | 617 @override |
| 623 void visitReturnStatement(ReturnStatement node) { | 618 void visitReturnStatement(ReturnStatement node) { |
| 624 node.expression = _checkReturn(node.expression, node); | 619 _checkReturn(node.expression, node); |
| 625 node.visitChildren(this); | 620 node.visitChildren(this); |
| 626 } | 621 } |
| 627 | 622 |
| 628 @override | 623 @override |
| 629 void visitPropertyAccess(PropertyAccess node) { | 624 void visitPropertyAccess(PropertyAccess node) { |
| 630 if (node.staticType.isDynamic && _rules.isDynamicTarget(node.realTarget)) { | 625 if (node.staticType.isDynamic && _rules.isDynamicTarget(node.realTarget)) { |
| 631 _recordDynamicInvoke(node); | 626 _recordDynamicInvoke(node); |
| 632 } | 627 } |
| 633 node.visitChildren(this); | 628 node.visitChildren(this); |
| 634 } | 629 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 650 var parameterType = _rules.elementType(parameter.element); | 645 var parameterType = _rules.elementType(parameter.element); |
| 651 assert(parameterType != null); | 646 assert(parameterType != null); |
| 652 var defaultValue = node.defaultValue; | 647 var defaultValue = node.defaultValue; |
| 653 if (defaultValue == null) { | 648 if (defaultValue == null) { |
| 654 if (_rules.maybeNonNullableType(parameterType)) { | 649 if (_rules.maybeNonNullableType(parameterType)) { |
| 655 var staticInfo = new InvalidVariableDeclaration( | 650 var staticInfo = new InvalidVariableDeclaration( |
| 656 _rules, node.identifier, parameterType); | 651 _rules, node.identifier, parameterType); |
| 657 _recordMessage(staticInfo); | 652 _recordMessage(staticInfo); |
| 658 } | 653 } |
| 659 } else { | 654 } else { |
| 660 var staticInfo = checkAssignment(defaultValue, parameterType); | 655 checkAssignment(defaultValue, parameterType); |
| 661 if (staticInfo is! StaticError) node.defaultValue = staticInfo; | |
| 662 } | 656 } |
| 663 | 657 |
| 664 node.visitChildren(this); | 658 node.visitChildren(this); |
| 665 }); | 659 }); |
| 666 } | 660 } |
| 667 | 661 |
| 668 @override | 662 @override |
| 669 void visitFieldFormalParameter(FieldFormalParameter node) { | 663 void visitFieldFormalParameter(FieldFormalParameter node) { |
| 670 var element = node.element; | 664 var element = node.element; |
| 671 var typeName = node.type; | 665 var typeName = node.type; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 704 TypeName type = node.type; | 698 TypeName type = node.type; |
| 705 if (type == null) { | 699 if (type == null) { |
| 706 // No checks are needed when the type is var. Although internally the | 700 // No checks are needed when the type is var. Although internally the |
| 707 // typing rules may have inferred a more precise type for the variable | 701 // typing rules may have inferred a more precise type for the variable |
| 708 // based on the initializer. | 702 // based on the initializer. |
| 709 } else { | 703 } else { |
| 710 var dartType = getType(type); | 704 var dartType = getType(type); |
| 711 for (VariableDeclaration variable in node.variables) { | 705 for (VariableDeclaration variable in node.variables) { |
| 712 var initializer = variable.initializer; | 706 var initializer = variable.initializer; |
| 713 if (initializer != null) { | 707 if (initializer != null) { |
| 714 variable.initializer = checkAssignment(initializer, dartType); | 708 checkAssignment(initializer, dartType); |
| 715 } else if (_rules.maybeNonNullableType(dartType)) { | 709 } else if (_rules.maybeNonNullableType(dartType)) { |
| 716 var element = variable.element; | 710 var element = variable.element; |
| 717 if (element is FieldElement && !element.isStatic) { | 711 if (element is FieldElement && !element.isStatic) { |
| 718 // Initialized - possibly implicitly - during construction. | 712 // Initialized - possibly implicitly - during construction. |
| 719 // Handle this via a runtime check during code generation. | 713 // Handle this via a runtime check during code generation. |
| 720 | 714 |
| 721 // TODO(vsm): Detect statically whether this can fail and | 715 // TODO(vsm): Detect statically whether this can fail and |
| 722 // report a static error (must fail) or warning (can fail). | 716 // report a static error (must fail) or warning (can fail). |
| 723 } else { | 717 } else { |
| 724 var staticInfo = | 718 var staticInfo = |
| (...skipping 29 matching lines...) Expand all Loading... |
| 754 | 748 |
| 755 @override | 749 @override |
| 756 void visitIsExpression(IsExpression node) { | 750 void visitIsExpression(IsExpression node) { |
| 757 _checkRuntimeTypeCheck(node, node.type); | 751 _checkRuntimeTypeCheck(node, node.type); |
| 758 node.visitChildren(this); | 752 node.visitChildren(this); |
| 759 } | 753 } |
| 760 | 754 |
| 761 @override | 755 @override |
| 762 void visitPrefixExpression(PrefixExpression node) { | 756 void visitPrefixExpression(PrefixExpression node) { |
| 763 if (node.operator.type == TokenType.BANG) { | 757 if (node.operator.type == TokenType.BANG) { |
| 764 node.operand = checkBoolean(node.operand); | 758 checkBoolean(node.operand); |
| 765 } else { | 759 } else { |
| 766 _checkUnary(node); | 760 _checkUnary(node); |
| 767 } | 761 } |
| 768 node.visitChildren(this); | 762 node.visitChildren(this); |
| 769 } | 763 } |
| 770 | 764 |
| 771 @override | 765 @override |
| 772 void visitPostfixExpression(PostfixExpression node) { | 766 void visitPostfixExpression(PostfixExpression node) { |
| 773 _checkUnary(node); | 767 _checkUnary(node); |
| 774 node.visitChildren(this); | 768 node.visitChildren(this); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 799 _recordDynamicInvoke(node); | 793 _recordDynamicInvoke(node); |
| 800 } | 794 } |
| 801 } else { | 795 } else { |
| 802 var element = node.staticElement; | 796 var element = node.staticElement; |
| 803 // Method invocation. | 797 // Method invocation. |
| 804 if (element is MethodElement) { | 798 if (element is MethodElement) { |
| 805 var type = element.type as FunctionType; | 799 var type = element.type as FunctionType; |
| 806 // Analyzer should enforce number of parameter types, but check in | 800 // Analyzer should enforce number of parameter types, but check in |
| 807 // case we have erroneous input. | 801 // case we have erroneous input. |
| 808 if (type.normalParameterTypes.isNotEmpty) { | 802 if (type.normalParameterTypes.isNotEmpty) { |
| 809 node.rightOperand = | 803 checkArgument(node.rightOperand, type.normalParameterTypes[0]); |
| 810 checkArgument(node.rightOperand, type.normalParameterTypes[0]); | |
| 811 } | 804 } |
| 812 } else { | 805 } else { |
| 813 // TODO(vsm): Assert that the analyzer found an error here? | 806 // TODO(vsm): Assert that the analyzer found an error here? |
| 814 } | 807 } |
| 815 } | 808 } |
| 816 } else { | 809 } else { |
| 817 // Non-method operator. | 810 // Non-method operator. |
| 818 switch (op.type) { | 811 switch (op.type) { |
| 819 case TokenType.AMPERSAND_AMPERSAND: | 812 case TokenType.AMPERSAND_AMPERSAND: |
| 820 case TokenType.BAR_BAR: | 813 case TokenType.BAR_BAR: |
| 821 node.leftOperand = checkBoolean(node.leftOperand); | 814 checkBoolean(node.leftOperand); |
| 822 node.rightOperand = checkBoolean(node.rightOperand); | 815 checkBoolean(node.rightOperand); |
| 823 break; | 816 break; |
| 824 case TokenType.BANG_EQ: | 817 case TokenType.BANG_EQ: |
| 825 break; | 818 break; |
| 826 default: | 819 default: |
| 827 assert(false); | 820 assert(false); |
| 828 } | 821 } |
| 829 } | 822 } |
| 830 node.visitChildren(this); | 823 node.visitChildren(this); |
| 831 } | 824 } |
| 832 | 825 |
| 833 @override | 826 @override |
| 834 void visitConditionalExpression(ConditionalExpression node) { | 827 void visitConditionalExpression(ConditionalExpression node) { |
| 835 node.condition = checkBoolean(node.condition); | 828 checkBoolean(node.condition); |
| 836 node.visitChildren(this); | 829 node.visitChildren(this); |
| 837 } | 830 } |
| 838 | 831 |
| 839 @override | 832 @override |
| 840 void visitIndexExpression(IndexExpression node) { | 833 void visitIndexExpression(IndexExpression node) { |
| 841 if (_rules.isDynamicTarget(node.target)) { | 834 if (_rules.isDynamicTarget(node.target)) { |
| 842 _recordDynamicInvoke(node); | 835 _recordDynamicInvoke(node); |
| 843 } else { | 836 } else { |
| 844 var element = node.staticElement; | 837 var element = node.staticElement; |
| 845 if (element is MethodElement) { | 838 if (element is MethodElement) { |
| 846 var type = element.type as FunctionType; | 839 var type = element.type as FunctionType; |
| 847 // Analyzer should enforce number of parameter types, but check in | 840 // Analyzer should enforce number of parameter types, but check in |
| 848 // case we have erroneous input. | 841 // case we have erroneous input. |
| 849 if (type.normalParameterTypes.isNotEmpty) { | 842 if (type.normalParameterTypes.isNotEmpty) { |
| 850 node.index = checkArgument(node.index, type.normalParameterTypes[0]); | 843 checkArgument(node.index, type.normalParameterTypes[0]); |
| 851 } | 844 } |
| 852 } else { | 845 } else { |
| 853 // TODO(vsm): Assert that the analyzer found an error here? | 846 // TODO(vsm): Assert that the analyzer found an error here? |
| 854 } | 847 } |
| 855 } | 848 } |
| 856 node.visitChildren(this); | 849 node.visitChildren(this); |
| 857 } | 850 } |
| 858 | 851 |
| 859 DartType getType(TypeName name) { | 852 DartType getType(TypeName name) { |
| 860 return (name == null) ? _rules.provider.dynamicType : name.type; | 853 return (name == null) ? _rules.provider.dynamicType : name.type; |
| 861 } | 854 } |
| 862 | 855 |
| 863 /// Analyzer checks boolean conversions, but we need to check too, because | 856 /// Analyzer checks boolean conversions, but we need to check too, because |
| 864 /// it uses the default assignability rules that allow `dynamic` and `Object` | 857 /// it uses the default assignability rules that allow `dynamic` and `Object` |
| 865 /// to be assigned to bool with no message. | 858 /// to be assigned to bool with no message. |
| 866 Expression checkBoolean(Expression expr) => | 859 void checkBoolean(Expression expr) => |
| 867 checkAssignment(expr, _rules.provider.boolType); | 860 checkAssignment(expr, _rules.provider.boolType); |
| 868 | 861 |
| 869 Expression checkAssignment(Expression expr, DartType type) { | 862 void checkAssignment(Expression expr, DartType type) { |
| 870 if (expr is ParenthesizedExpression) { | 863 if (expr is ParenthesizedExpression) { |
| 871 expr.expression = checkAssignment(expr.expression, type); | 864 checkAssignment(expr.expression, type); |
| 872 } else { | 865 } else { |
| 873 final staticInfo = _rules.checkAssignment(expr, type, _constantContext); | 866 _recordMessage(_rules.checkAssignment(expr, type, _constantContext)); |
| 874 _recordMessage(staticInfo); | |
| 875 if (staticInfo is Conversion) expr = staticInfo; | |
| 876 } | 867 } |
| 877 return expr; | |
| 878 } | 868 } |
| 879 | 869 |
| 880 DartType _specializedBinaryReturnType( | 870 DartType _specializedBinaryReturnType( |
| 881 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { | 871 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { |
| 882 // This special cases binary return types as per 16.26 and 16.27 of the | 872 // This special cases binary return types as per 16.26 and 16.27 of the |
| 883 // Dart language spec. | 873 // Dart language spec. |
| 884 switch (op) { | 874 switch (op) { |
| 885 case TokenType.PLUS: | 875 case TokenType.PLUS: |
| 886 case TokenType.MINUS: | 876 case TokenType.MINUS: |
| 887 case TokenType.STAR: | 877 case TokenType.STAR: |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 926 | 916 |
| 927 if (!_rules.isSubTypeOf(returnType, lhsType)) { | 917 if (!_rules.isSubTypeOf(returnType, lhsType)) { |
| 928 final numType = _rules.provider.numType; | 918 final numType = _rules.provider.numType; |
| 929 // Try to fix up the numerical case if possible. | 919 // Try to fix up the numerical case if possible. |
| 930 if (_rules.isSubTypeOf(lhsType, numType) && | 920 if (_rules.isSubTypeOf(lhsType, numType) && |
| 931 _rules.isSubTypeOf(lhsType, rhsType)) { | 921 _rules.isSubTypeOf(lhsType, rhsType)) { |
| 932 // This is also slightly different from spec, but allows us to keep | 922 // This is also slightly different from spec, but allows us to keep |
| 933 // compound operators in the int += num and num += dynamic cases. | 923 // compound operators in the int += num and num += dynamic cases. |
| 934 staticInfo = DownCast.create( | 924 staticInfo = DownCast.create( |
| 935 _rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType)); | 925 _rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType)); |
| 936 if (staticInfo is DownCast) { | |
| 937 expr.rightHandSide = staticInfo; | |
| 938 } | |
| 939 rhsType = lhsType; | 926 rhsType = lhsType; |
| 940 } else { | 927 } else { |
| 941 // Static type error | 928 // Static type error |
| 942 staticInfo = new StaticTypeError(_rules, expr, lhsType); | 929 staticInfo = new StaticTypeError(_rules, expr, lhsType); |
| 943 } | 930 } |
| 944 _recordMessage(staticInfo); | 931 _recordMessage(staticInfo); |
| 945 } | 932 } |
| 946 | 933 |
| 947 // Check the rhs type | 934 // Check the rhs type |
| 948 if (staticInfo is! Conversion) { | 935 if (staticInfo is! CoercionInfo) { |
| 949 var paramType = paramTypes.first; | 936 var paramType = paramTypes.first; |
| 950 staticInfo = _rules.checkAssignment( | 937 staticInfo = _rules.checkAssignment( |
| 951 expr.rightHandSide, paramType, _constantContext); | 938 expr.rightHandSide, paramType, _constantContext); |
| 952 _recordMessage(staticInfo); | 939 _recordMessage(staticInfo); |
| 953 if (staticInfo is Conversion) expr.rightHandSide = staticInfo; | |
| 954 } | 940 } |
| 955 } | 941 } |
| 956 } | 942 } |
| 957 | 943 |
| 958 void _recordDynamicInvoke(AstNode node) { | 944 void _recordDynamicInvoke(AstNode node) { |
| 959 _reporter.log(new DynamicInvoke(_rules, node)); | 945 _reporter.log(new DynamicInvoke(_rules, node)); |
| 960 } | 946 } |
| 961 | 947 |
| 962 void _recordMessage(StaticInfo info) { | 948 void _recordMessage(StaticInfo info) { |
| 963 if (info == null) return; | 949 if (info == null) return; |
| 964 if (info.level >= logger.Level.SEVERE) _failure = true; | 950 if (info.level >= logger.Level.SEVERE) _failure = true; |
| 965 _reporter.log(info); | 951 _reporter.log(info); |
| 952 if (info is CoercionInfo) { |
| 953 assert(CoercionInfo.get(info.node) == null); |
| 954 CoercionInfo.set(info.node, info); |
| 955 } |
| 966 } | 956 } |
| 967 } | 957 } |
| OLD | NEW |