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 |