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 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 bool _failure = false; | 341 bool _failure = false; |
342 bool get failure => _failure || _overrideChecker._failure; | 342 bool get failure => _failure || _overrideChecker._failure; |
343 | 343 |
344 CodeChecker( | 344 CodeChecker( |
345 TypeRules rules, CheckerReporter reporter, CompilerOptions options) | 345 TypeRules rules, CheckerReporter reporter, CompilerOptions options) |
346 : _rules = rules, | 346 : _rules = rules, |
347 _reporter = reporter, | 347 _reporter = reporter, |
348 _overrideChecker = new _OverrideChecker(rules, reporter, options); | 348 _overrideChecker = new _OverrideChecker(rules, reporter, options); |
349 | 349 |
350 @override | 350 @override |
351 visitCompilationUnit(CompilationUnit unit) { | 351 void visitCompilationUnit(CompilationUnit unit) { |
352 void report(Expression expr) { | 352 void report(Expression expr) { |
353 _reporter.log(new MissingTypeError(expr)); | 353 _reporter.log(new MissingTypeError(expr)); |
354 } | 354 } |
355 var callback = _rules.reportMissingType; | 355 var callback = _rules.reportMissingType; |
356 _rules.reportMissingType = report; | 356 _rules.reportMissingType = report; |
357 unit.visitChildren(this); | 357 unit.visitChildren(this); |
358 _rules.reportMissingType = callback; | 358 _rules.reportMissingType = callback; |
359 } | 359 } |
360 | 360 |
361 _visitMaybeConst(AstNode n, visitNode(AstNode n)) { | 361 _visitMaybeConst(AstNode n, visitNode(AstNode n)) { |
(...skipping 10 matching lines...) Expand all Loading... |
372 } else if (n is InstanceCreationExpression) { | 372 } else if (n is InstanceCreationExpression) { |
373 _constantContext = o || n.isConst; | 373 _constantContext = o || n.isConst; |
374 } else if (n is ConstructorDeclaration) { | 374 } else if (n is ConstructorDeclaration) { |
375 _constantContext = o || n.element.isConst; | 375 _constantContext = o || n.element.isConst; |
376 } | 376 } |
377 } | 377 } |
378 visitNode(n); | 378 visitNode(n); |
379 _constantContext = o; | 379 _constantContext = o; |
380 } | 380 } |
381 | 381 |
382 visitComment(Comment node) { | 382 @override |
| 383 void visitComment(Comment node) { |
383 // skip, no need to do typechecking inside comments (they may contain | 384 // skip, no need to do typechecking inside comments (they may contain |
384 // comment references which would require resolution). | 385 // comment references which would require resolution). |
385 } | 386 } |
386 | 387 |
387 visitClassDeclaration(ClassDeclaration node) { | 388 @override |
| 389 void visitClassDeclaration(ClassDeclaration node) { |
388 _overrideChecker.check(node); | 390 _overrideChecker.check(node); |
389 super.visitClassDeclaration(node); | 391 super.visitClassDeclaration(node); |
390 } | 392 } |
391 | 393 |
392 visitAssignmentExpression(AssignmentExpression node) { | 394 @override |
| 395 void visitAssignmentExpression(AssignmentExpression node) { |
393 var token = node.operator; | 396 var token = node.operator; |
394 if (token.type != TokenType.EQ) { | 397 if (token.type != TokenType.EQ) { |
395 _checkCompoundAssignment(node); | 398 _checkCompoundAssignment(node); |
396 } else { | 399 } else { |
397 DartType staticType = _rules.getStaticType(node.leftHandSide); | 400 DartType staticType = _rules.getStaticType(node.leftHandSide); |
398 node.rightHandSide = checkAssignment(node.rightHandSide, staticType); | 401 node.rightHandSide = checkAssignment(node.rightHandSide, staticType); |
399 } | 402 } |
400 node.visitChildren(this); | 403 node.visitChildren(this); |
401 } | 404 } |
402 | 405 |
403 /// Check constructor declaration to ensure correct super call placement. | 406 /// Check constructor declaration to ensure correct super call placement. |
404 @override | 407 @override |
405 visitConstructorDeclaration(ConstructorDeclaration node) { | 408 void visitConstructorDeclaration(ConstructorDeclaration node) { |
406 _visitMaybeConst(node, (node) { | 409 _visitMaybeConst(node, (node) { |
407 node.visitChildren(this); | 410 node.visitChildren(this); |
408 | 411 |
409 final init = node.initializers; | 412 final init = node.initializers; |
410 for (int i = 0, last = init.length - 1; i < last; i++) { | 413 for (int i = 0, last = init.length - 1; i < last; i++) { |
411 final node = init[i]; | 414 final node = init[i]; |
412 if (node is SuperConstructorInvocation) { | 415 if (node is SuperConstructorInvocation) { |
413 _recordMessage(new InvalidSuperInvocation(node)); | 416 _recordMessage(new InvalidSuperInvocation(node)); |
414 } | 417 } |
415 } | 418 } |
416 }); | 419 }); |
417 } | 420 } |
418 | 421 |
419 @override | 422 @override |
420 visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | 423 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
421 var field = node.fieldName; | 424 var field = node.fieldName; |
422 DartType staticType = _rules.elementType(field.staticElement); | 425 DartType staticType = _rules.elementType(field.staticElement); |
423 node.expression = checkAssignment(node.expression, staticType); | 426 node.expression = checkAssignment(node.expression, staticType); |
424 node.visitChildren(this); | 427 node.visitChildren(this); |
425 } | 428 } |
426 | 429 |
427 @override | 430 @override |
428 visitForEachStatement(ForEachStatement node) { | 431 void visitForEachStatement(ForEachStatement node) { |
429 // Check that the expression is an Iterable. | 432 // Check that the expression is an Iterable. |
430 var expr = node.iterable; | 433 var expr = node.iterable; |
431 var iterableType = _rules.provider.iterableType; | 434 var iterableType = _rules.provider.iterableType; |
432 var loopVariable = node.identifier != null | 435 var loopVariable = node.identifier != null |
433 ? node.identifier | 436 ? node.identifier |
434 : node.loopVariable.identifier; | 437 : node.loopVariable.identifier; |
435 var iteratorType = loopVariable.staticType; | 438 var iteratorType = loopVariable.staticType; |
436 var checkedType = iterableType.substitute4([iteratorType]); | 439 var checkedType = iterableType.substitute4([iteratorType]); |
437 node.iterable = checkAssignment(expr, checkedType); | 440 node.iterable = checkAssignment(expr, checkedType); |
438 node.visitChildren(this); | 441 node.visitChildren(this); |
439 } | 442 } |
440 | 443 |
441 @override visitListLiteral(ListLiteral node) { | 444 @override |
| 445 void visitForStatement(ForStatement node) { |
| 446 if (node.condition != null) { |
| 447 node.condition = checkBoolean(node.condition); |
| 448 } |
| 449 node.visitChildren(this); |
| 450 } |
| 451 |
| 452 @override |
| 453 void visitIfStatement(IfStatement node) { |
| 454 node.condition = checkBoolean(node.condition); |
| 455 node.visitChildren(this); |
| 456 } |
| 457 |
| 458 @override |
| 459 void visitDoStatement(DoStatement node) { |
| 460 node.condition = checkBoolean(node.condition); |
| 461 node.visitChildren(this); |
| 462 } |
| 463 |
| 464 @override |
| 465 void visitWhileStatement(WhileStatement node) { |
| 466 node.condition = checkBoolean(node.condition); |
| 467 node.visitChildren(this); |
| 468 } |
| 469 |
| 470 @override |
| 471 void visitSwitchStatement(SwitchStatement node) { |
| 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 |
| 474 // return type from an overridden == operator (because Object.==), so |
| 475 // checking in SwitchStatement shouldn't be necessary. |
| 476 node.visitChildren(this); |
| 477 } |
| 478 |
| 479 @override |
| 480 void visitListLiteral(ListLiteral node) { |
442 var type = _rules.provider.dynamicType; | 481 var type = _rules.provider.dynamicType; |
443 if (node.typeArguments != null) { | 482 if (node.typeArguments != null) { |
444 var targs = node.typeArguments.arguments; | 483 var targs = node.typeArguments.arguments; |
445 if (targs.length > 0) type = targs[0].type; | 484 if (targs.length > 0) type = targs[0].type; |
446 } | 485 } |
447 var elements = node.elements; | 486 var elements = node.elements; |
448 for (int i = 0; i < elements.length; i++) { | 487 for (int i = 0; i < elements.length; i++) { |
449 elements[i] = checkArgument(elements[i], type); | 488 elements[i] = checkArgument(elements[i], type); |
450 } | 489 } |
451 super.visitListLiteral(node); | 490 super.visitListLiteral(node); |
452 } | 491 } |
453 | 492 |
454 @override visitMapLiteral(MapLiteral node) { | 493 @override |
| 494 void visitMapLiteral(MapLiteral node) { |
455 var ktype = _rules.provider.dynamicType; | 495 var ktype = _rules.provider.dynamicType; |
456 var vtype = _rules.provider.dynamicType; | 496 var vtype = _rules.provider.dynamicType; |
457 if (node.typeArguments != null) { | 497 if (node.typeArguments != null) { |
458 var targs = node.typeArguments.arguments; | 498 var targs = node.typeArguments.arguments; |
459 if (targs.length > 0) ktype = targs[0].type; | 499 if (targs.length > 0) ktype = targs[0].type; |
460 if (targs.length > 1) vtype = targs[1].type; | 500 if (targs.length > 1) vtype = targs[1].type; |
461 } | 501 } |
462 var entries = node.entries; | 502 var entries = node.entries; |
463 for (int i = 0; i < entries.length; i++) { | 503 for (int i = 0; i < entries.length; i++) { |
464 var entry = entries[i]; | 504 var entry = entries[i]; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 Expression node, Expression f, ArgumentList list) { | 546 Expression node, Expression f, ArgumentList list) { |
507 if (_rules.isDynamicCall(f)) { | 547 if (_rules.isDynamicCall(f)) { |
508 // If f is Function and this is a method invocation, we should have | 548 // If f is Function and this is a method invocation, we should have |
509 // gotten an analyzer error, so no need to issue another error. | 549 // gotten an analyzer error, so no need to issue another error. |
510 _recordDynamicInvoke(node); | 550 _recordDynamicInvoke(node); |
511 } else { | 551 } else { |
512 checkArgumentList(list, _rules.getTypeAsCaller(f)); | 552 checkArgumentList(list, _rules.getTypeAsCaller(f)); |
513 } | 553 } |
514 } | 554 } |
515 | 555 |
516 visitMethodInvocation(MethodInvocation node) { | 556 @override |
| 557 void visitMethodInvocation(MethodInvocation node) { |
517 checkFunctionApplication(node, node.methodName, node.argumentList); | 558 checkFunctionApplication(node, node.methodName, node.argumentList); |
518 node.visitChildren(this); | 559 node.visitChildren(this); |
519 } | 560 } |
520 | 561 |
521 visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | 562 @override |
| 563 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
522 checkFunctionApplication(node, node.function, node.argumentList); | 564 checkFunctionApplication(node, node.function, node.argumentList); |
523 node.visitChildren(this); | 565 node.visitChildren(this); |
524 } | 566 } |
525 | 567 |
526 visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) { | 568 @override |
| 569 void visitRedirectingConstructorInvocation( |
| 570 RedirectingConstructorInvocation node) { |
527 var type = node.staticElement.type; | 571 var type = node.staticElement.type; |
528 bool checked = checkArgumentList(node.argumentList, type); | 572 bool checked = checkArgumentList(node.argumentList, type); |
529 assert(checked); | 573 assert(checked); |
530 node.visitChildren(this); | 574 node.visitChildren(this); |
531 } | 575 } |
532 | 576 |
533 visitSuperConstructorInvocation(SuperConstructorInvocation node) { | 577 @override |
| 578 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
534 var element = node.staticElement; | 579 var element = node.staticElement; |
535 if (element == null) { | 580 if (element == null) { |
536 _recordMessage(new MissingTypeError(node)); | 581 _recordMessage(new MissingTypeError(node)); |
537 } else { | 582 } else { |
538 var type = node.staticElement.type; | 583 var type = node.staticElement.type; |
539 bool checked = checkArgumentList(node.argumentList, type); | 584 bool checked = checkArgumentList(node.argumentList, type); |
540 assert(checked); | 585 assert(checked); |
541 } | 586 } |
542 node.visitChildren(this); | 587 node.visitChildren(this); |
543 } | 588 } |
(...skipping 17 matching lines...) Expand all Loading... |
561 } | 606 } |
562 } | 607 } |
563 | 608 |
564 Expression _checkReturn(Expression expression, AstNode node) { | 609 Expression _checkReturn(Expression expression, AstNode node) { |
565 var type = _getFunctionType(_getOwnerFunction(node)).returnType; | 610 var type = _getFunctionType(_getOwnerFunction(node)).returnType; |
566 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. | 611 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. |
567 if (expression == null) return null; | 612 if (expression == null) return null; |
568 return checkAssignment(expression, type); | 613 return checkAssignment(expression, type); |
569 } | 614 } |
570 | 615 |
571 visitExpressionFunctionBody(ExpressionFunctionBody node) { | 616 @override |
| 617 void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
572 node.expression = _checkReturn(node.expression, node); | 618 node.expression = _checkReturn(node.expression, node); |
573 node.visitChildren(this); | 619 node.visitChildren(this); |
574 } | 620 } |
575 | 621 |
576 visitReturnStatement(ReturnStatement node) { | 622 @override |
| 623 void visitReturnStatement(ReturnStatement node) { |
577 node.expression = _checkReturn(node.expression, node); | 624 node.expression = _checkReturn(node.expression, node); |
578 node.visitChildren(this); | 625 node.visitChildren(this); |
579 } | 626 } |
580 | 627 |
581 visitPropertyAccess(PropertyAccess node) { | 628 @override |
| 629 void visitPropertyAccess(PropertyAccess node) { |
582 if (node.staticType.isDynamic && _rules.isDynamicTarget(node.realTarget)) { | 630 if (node.staticType.isDynamic && _rules.isDynamicTarget(node.realTarget)) { |
583 _recordDynamicInvoke(node); | 631 _recordDynamicInvoke(node); |
584 } | 632 } |
585 node.visitChildren(this); | 633 node.visitChildren(this); |
586 } | 634 } |
587 | 635 |
588 visitPrefixedIdentifier(PrefixedIdentifier node) { | 636 @override |
| 637 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
589 final target = node.prefix; | 638 final target = node.prefix; |
590 if (_rules.isDynamicTarget(target)) { | 639 if (_rules.isDynamicTarget(target)) { |
591 _recordDynamicInvoke(node); | 640 _recordDynamicInvoke(node); |
592 } | 641 } |
593 node.visitChildren(this); | 642 node.visitChildren(this); |
594 } | 643 } |
595 | 644 |
596 @override visitDefaultFormalParameter(DefaultFormalParameter node) { | 645 @override |
| 646 void visitDefaultFormalParameter(DefaultFormalParameter node) { |
597 _visitMaybeConst(node, (node) { | 647 _visitMaybeConst(node, (node) { |
598 // Check that defaults have the proper subtype. | 648 // Check that defaults have the proper subtype. |
599 var parameter = node.parameter; | 649 var parameter = node.parameter; |
600 var parameterType = _rules.elementType(parameter.element); | 650 var parameterType = _rules.elementType(parameter.element); |
601 assert(parameterType != null); | 651 assert(parameterType != null); |
602 var defaultValue = node.defaultValue; | 652 var defaultValue = node.defaultValue; |
603 if (defaultValue == null) { | 653 if (defaultValue == null) { |
604 if (_rules.maybeNonNullableType(parameterType)) { | 654 if (_rules.maybeNonNullableType(parameterType)) { |
605 var staticInfo = new InvalidVariableDeclaration( | 655 var staticInfo = new InvalidVariableDeclaration( |
606 _rules, node.identifier, parameterType); | 656 _rules, node.identifier, parameterType); |
607 _recordMessage(staticInfo); | 657 _recordMessage(staticInfo); |
608 } | 658 } |
609 } else { | 659 } else { |
610 var staticInfo = checkAssignment(defaultValue, parameterType); | 660 var staticInfo = checkAssignment(defaultValue, parameterType); |
611 if (staticInfo is! StaticError) node.defaultValue = staticInfo; | 661 if (staticInfo is! StaticError) node.defaultValue = staticInfo; |
612 } | 662 } |
613 | 663 |
614 node.visitChildren(this); | 664 node.visitChildren(this); |
615 }); | 665 }); |
616 } | 666 } |
617 | 667 |
618 visitFieldFormalParameter(FieldFormalParameter node) { | 668 @override |
| 669 void visitFieldFormalParameter(FieldFormalParameter node) { |
619 var element = node.element; | 670 var element = node.element; |
620 var typeName = node.type; | 671 var typeName = node.type; |
621 if (typeName != null) { | 672 if (typeName != null) { |
622 var type = _rules.elementType(element); | 673 var type = _rules.elementType(element); |
623 var fieldElement = | 674 var fieldElement = |
624 node.identifier.staticElement as FieldFormalParameterElement; | 675 node.identifier.staticElement as FieldFormalParameterElement; |
625 var fieldType = _rules.elementType(fieldElement.field); | 676 var fieldType = _rules.elementType(fieldElement.field); |
626 if (!_rules.isSubTypeOf(type, fieldType)) { | 677 if (!_rules.isSubTypeOf(type, fieldType)) { |
627 var staticInfo = | 678 var staticInfo = |
628 new InvalidParameterDeclaration(_rules, node, fieldType); | 679 new InvalidParameterDeclaration(_rules, node, fieldType); |
629 _recordMessage(staticInfo); | 680 _recordMessage(staticInfo); |
630 } | 681 } |
631 } | 682 } |
632 node.visitChildren(this); | 683 node.visitChildren(this); |
633 } | 684 } |
634 | 685 |
635 @override | 686 @override |
636 visitInstanceCreationExpression(InstanceCreationExpression node) { | 687 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
637 _visitMaybeConst(node, (node) { | 688 _visitMaybeConst(node, (node) { |
638 var arguments = node.argumentList; | 689 var arguments = node.argumentList; |
639 var element = node.staticElement; | 690 var element = node.staticElement; |
640 if (element != null) { | 691 if (element != null) { |
641 var type = _rules.elementType(node.staticElement); | 692 var type = _rules.elementType(node.staticElement); |
642 checkArgumentList(arguments, type); | 693 checkArgumentList(arguments, type); |
643 } else { | 694 } else { |
644 _recordMessage(new MissingTypeError(node)); | 695 _recordMessage(new MissingTypeError(node)); |
645 } | 696 } |
646 node.visitChildren(this); | 697 node.visitChildren(this); |
647 }); | 698 }); |
648 } | 699 } |
649 | 700 |
650 @override | 701 @override |
651 visitVariableDeclarationList(VariableDeclarationList node) { | 702 void visitVariableDeclarationList(VariableDeclarationList node) { |
652 _visitMaybeConst(node, (node) { | 703 _visitMaybeConst(node, (node) { |
653 TypeName type = node.type; | 704 TypeName type = node.type; |
654 if (type == null) { | 705 if (type == null) { |
655 // No checks are needed when the type is var. Although internally the | 706 // No checks are needed when the type is var. Although internally the |
656 // typing rules may have inferred a more precise type for the variable | 707 // typing rules may have inferred a more precise type for the variable |
657 // based on the initializer. | 708 // based on the initializer. |
658 } else { | 709 } else { |
659 var dartType = getType(type); | 710 var dartType = getType(type); |
660 for (VariableDeclaration variable in node.variables) { | 711 for (VariableDeclaration variable in node.variables) { |
661 var initializer = variable.initializer; | 712 var initializer = variable.initializer; |
(...skipping 13 matching lines...) Expand all Loading... |
675 _recordMessage(staticInfo); | 726 _recordMessage(staticInfo); |
676 } | 727 } |
677 } | 728 } |
678 } | 729 } |
679 } | 730 } |
680 node.visitChildren(this); | 731 node.visitChildren(this); |
681 }); | 732 }); |
682 } | 733 } |
683 | 734 |
684 @override | 735 @override |
685 visitVariableDeclaration(VariableDeclaration node) { | 736 void visitVariableDeclaration(VariableDeclaration node) { |
686 _visitMaybeConst(node, super.visitVariableDeclaration); | 737 _visitMaybeConst(node, super.visitVariableDeclaration); |
687 } | 738 } |
688 | 739 |
689 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { | 740 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { |
690 var type = getType(typeName); | 741 var type = getType(typeName); |
691 if (!_rules.isGroundType(type)) { | 742 if (!_rules.isGroundType(type)) { |
692 _recordMessage(new InvalidRuntimeCheckError(node, type)); | 743 _recordMessage(new InvalidRuntimeCheckError(node, type)); |
693 } | 744 } |
694 } | 745 } |
695 | 746 |
696 visitAsExpression(AsExpression node) { | 747 @override |
| 748 void visitAsExpression(AsExpression node) { |
697 // We could do the same check as the IsExpression below, but that is | 749 // We could do the same check as the IsExpression below, but that is |
698 // potentially too conservative. Instead, at runtime, we must fail hard | 750 // potentially too conservative. Instead, at runtime, we must fail hard |
699 // if the Dart as and the DDC as would return different values. | 751 // if the Dart as and the DDC as would return different values. |
700 node.visitChildren(this); | 752 node.visitChildren(this); |
701 } | 753 } |
702 | 754 |
703 visitIsExpression(IsExpression node) { | 755 @override |
| 756 void visitIsExpression(IsExpression node) { |
704 _checkRuntimeTypeCheck(node, node.type); | 757 _checkRuntimeTypeCheck(node, node.type); |
705 node.visitChildren(this); | 758 node.visitChildren(this); |
706 } | 759 } |
707 | 760 |
708 visitBinaryExpression(BinaryExpression node) { | 761 @override |
| 762 void visitPrefixExpression(PrefixExpression node) { |
| 763 if (node.operator.type == TokenType.BANG) { |
| 764 node.operand = checkBoolean(node.operand); |
| 765 } else { |
| 766 _checkUnary(node); |
| 767 } |
| 768 node.visitChildren(this); |
| 769 } |
| 770 |
| 771 @override |
| 772 void visitPostfixExpression(PostfixExpression node) { |
| 773 _checkUnary(node); |
| 774 node.visitChildren(this); |
| 775 } |
| 776 |
| 777 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { |
| 778 var op = node.operator; |
| 779 if (op.isUserDefinableOperator || |
| 780 op.type == TokenType.PLUS_PLUS || |
| 781 op.type == TokenType.MINUS_MINUS) { |
| 782 if (_rules.isDynamicTarget(node.operand)) { |
| 783 _recordDynamicInvoke(node); |
| 784 } |
| 785 // For ++ and --, even if it is not dynamic, we still need to check |
| 786 // that the user defined method accepts an `int` as the RHS. |
| 787 // We assume Analyzer has done this already. |
| 788 } |
| 789 } |
| 790 |
| 791 @override |
| 792 void visitBinaryExpression(BinaryExpression node) { |
709 var op = node.operator; | 793 var op = node.operator; |
710 if (op.isUserDefinableOperator) { | 794 if (op.isUserDefinableOperator) { |
711 if (_rules.isDynamicTarget(node.leftOperand)) { | 795 if (_rules.isDynamicTarget(node.leftOperand)) { |
712 // Dynamic invocation | 796 // Dynamic invocation |
713 // TODO(vsm): Move this logic to the resolver? | 797 // TODO(vsm): Move this logic to the resolver? |
714 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { | 798 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { |
715 _recordDynamicInvoke(node); | 799 _recordDynamicInvoke(node); |
716 } | 800 } |
717 } else { | 801 } else { |
718 var element = node.staticElement; | 802 var element = node.staticElement; |
719 // Method invocation. | 803 // Method invocation. |
720 if (element is MethodElement) { | 804 if (element is MethodElement) { |
721 var type = element.type as FunctionType; | 805 var type = element.type as FunctionType; |
722 // Analyzer should enforce number of parameter types, but check in | 806 // Analyzer should enforce number of parameter types, but check in |
723 // case we have erroneous input. | 807 // case we have erroneous input. |
724 if (type.normalParameterTypes.isNotEmpty) { | 808 if (type.normalParameterTypes.isNotEmpty) { |
725 node.rightOperand = | 809 node.rightOperand = |
726 checkArgument(node.rightOperand, type.normalParameterTypes[0]); | 810 checkArgument(node.rightOperand, type.normalParameterTypes[0]); |
727 } | 811 } |
728 } else { | 812 } else { |
729 // TODO(vsm): Assert that the analyzer found an error here? | 813 // TODO(vsm): Assert that the analyzer found an error here? |
730 } | 814 } |
731 } | 815 } |
732 } else { | 816 } else { |
733 // Non-method operator. | 817 // Non-method operator. |
734 switch (op.type) { | 818 switch (op.type) { |
735 case TokenType.AMPERSAND_AMPERSAND: | 819 case TokenType.AMPERSAND_AMPERSAND: |
736 case TokenType.BAR_BAR: | 820 case TokenType.BAR_BAR: |
737 var boolType = _rules.provider.boolType; | 821 node.leftOperand = checkBoolean(node.leftOperand); |
738 node.leftOperand = checkArgument(node.leftOperand, boolType); | 822 node.rightOperand = checkBoolean(node.rightOperand); |
739 node.rightOperand = checkArgument(node.rightOperand, boolType); | |
740 break; | 823 break; |
741 case TokenType.BANG_EQ: | 824 case TokenType.BANG_EQ: |
742 break; | 825 break; |
743 default: | 826 default: |
744 assert(false); | 827 assert(false); |
745 } | 828 } |
746 } | 829 } |
747 node.visitChildren(this); | 830 node.visitChildren(this); |
748 } | 831 } |
749 | 832 |
750 @override | 833 @override |
| 834 void visitConditionalExpression(ConditionalExpression node) { |
| 835 node.condition = checkBoolean(node.condition); |
| 836 node.visitChildren(this); |
| 837 } |
| 838 |
| 839 @override |
751 void visitIndexExpression(IndexExpression node) { | 840 void visitIndexExpression(IndexExpression node) { |
752 if (_rules.isDynamicTarget(node.target)) { | 841 if (_rules.isDynamicTarget(node.target)) { |
753 _recordDynamicInvoke(node); | 842 _recordDynamicInvoke(node); |
754 } else { | 843 } else { |
755 var element = node.staticElement; | 844 var element = node.staticElement; |
756 if (element is MethodElement) { | 845 if (element is MethodElement) { |
757 var type = element.type as FunctionType; | 846 var type = element.type as FunctionType; |
758 // Analyzer should enforce number of parameter types, but check in | 847 // Analyzer should enforce number of parameter types, but check in |
759 // case we have erroneous input. | 848 // case we have erroneous input. |
760 if (type.normalParameterTypes.isNotEmpty) { | 849 if (type.normalParameterTypes.isNotEmpty) { |
761 node.index = checkArgument(node.index, type.normalParameterTypes[0]); | 850 node.index = checkArgument(node.index, type.normalParameterTypes[0]); |
762 } | 851 } |
763 } else { | 852 } else { |
764 // TODO(vsm): Assert that the analyzer found an error here? | 853 // TODO(vsm): Assert that the analyzer found an error here? |
765 } | 854 } |
766 } | 855 } |
767 node.visitChildren(this); | 856 node.visitChildren(this); |
768 } | 857 } |
769 | 858 |
770 DartType getType(TypeName name) { | 859 DartType getType(TypeName name) { |
771 return (name == null) ? _rules.provider.dynamicType : name.type; | 860 return (name == null) ? _rules.provider.dynamicType : name.type; |
772 } | 861 } |
773 | 862 |
| 863 /// Analyzer checks boolean conversions, but we need to check too, because |
| 864 /// it uses the default assignability rules that allow `dynamic` and `Object` |
| 865 /// to be assigned to bool with no message. |
| 866 Expression checkBoolean(Expression expr) => |
| 867 checkAssignment(expr, _rules.provider.boolType); |
| 868 |
774 Expression checkAssignment(Expression expr, DartType type) { | 869 Expression checkAssignment(Expression expr, DartType type) { |
775 if (expr is ParenthesizedExpression) { | 870 if (expr is ParenthesizedExpression) { |
776 expr.expression = checkAssignment(expr.expression, type); | 871 expr.expression = checkAssignment(expr.expression, type); |
777 } else { | 872 } else { |
778 final staticInfo = _rules.checkAssignment(expr, type, _constantContext); | 873 final staticInfo = _rules.checkAssignment(expr, type, _constantContext); |
779 _recordMessage(staticInfo); | 874 _recordMessage(staticInfo); |
780 if (staticInfo is Conversion) expr = staticInfo; | 875 if (staticInfo is Conversion) expr = staticInfo; |
781 } | 876 } |
782 return expr; | 877 return expr; |
783 } | 878 } |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
863 void _recordDynamicInvoke(AstNode node) { | 958 void _recordDynamicInvoke(AstNode node) { |
864 _reporter.log(new DynamicInvoke(_rules, node)); | 959 _reporter.log(new DynamicInvoke(_rules, node)); |
865 } | 960 } |
866 | 961 |
867 void _recordMessage(StaticInfo info) { | 962 void _recordMessage(StaticInfo info) { |
868 if (info == null) return; | 963 if (info == null) return; |
869 if (info.level >= logger.Level.SEVERE) _failure = true; | 964 if (info.level >= logger.Level.SEVERE) _failure = true; |
870 _reporter.log(info); | 965 _reporter.log(info); |
871 } | 966 } |
872 } | 967 } |
OLD | NEW |