| 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 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 330 if (info.level >= logger.Level.SEVERE) _failure = true; | 330 if (info.level >= logger.Level.SEVERE) _failure = true; |
| 331 _reporter.log(info); | 331 _reporter.log(info); |
| 332 } | 332 } |
| 333 } | 333 } |
| 334 | 334 |
| 335 /// Checks the body of functions and properties. | 335 /// Checks the body of functions and properties. |
| 336 class CodeChecker extends RecursiveAstVisitor { | 336 class CodeChecker extends RecursiveAstVisitor { |
| 337 final TypeRules _rules; | 337 final TypeRules _rules; |
| 338 final CheckerReporter _reporter; | 338 final CheckerReporter _reporter; |
| 339 final _OverrideChecker _overrideChecker; | 339 final _OverrideChecker _overrideChecker; |
| 340 bool _constantContext = false; | |
| 341 bool _failure = false; | 340 bool _failure = false; |
| 342 bool get failure => _failure || _overrideChecker._failure; | 341 bool get failure => _failure || _overrideChecker._failure; |
| 343 | 342 |
| 344 CodeChecker( | 343 CodeChecker( |
| 345 TypeRules rules, CheckerReporter reporter, StrongModeOptions options) | 344 TypeRules rules, CheckerReporter reporter, StrongModeOptions options) |
| 346 : _rules = rules, | 345 : _rules = rules, |
| 347 _reporter = reporter, | 346 _reporter = reporter, |
| 348 _overrideChecker = new _OverrideChecker(rules, reporter, options); | 347 _overrideChecker = new _OverrideChecker(rules, reporter, options); |
| 349 | 348 |
| 350 @override | 349 @override |
| 351 void visitCompilationUnit(CompilationUnit unit) { | 350 void visitCompilationUnit(CompilationUnit unit) { |
| 352 void report(Expression expr) { | 351 void report(Expression expr) { |
| 353 _reporter.log(new MissingTypeError(expr)); | 352 _reporter.log(new MissingTypeError(expr)); |
| 354 } | 353 } |
| 355 var callback = _rules.reportMissingType; | 354 var callback = _rules.reportMissingType; |
| 356 _rules.reportMissingType = report; | 355 _rules.reportMissingType = report; |
| 357 unit.visitChildren(this); | 356 unit.visitChildren(this); |
| 358 _rules.reportMissingType = callback; | 357 _rules.reportMissingType = callback; |
| 359 } | 358 } |
| 360 | 359 |
| 361 _visitMaybeConst(AstNode n, visitNode(AstNode n)) { | |
| 362 var o = _constantContext; | |
| 363 if (!o) { | |
| 364 if (n is VariableDeclarationList) { | |
| 365 _constantContext = o || n.isConst; | |
| 366 } else if (n is VariableDeclaration) { | |
| 367 _constantContext = o || n.isConst; | |
| 368 } else if (n is DefaultFormalParameter) { | |
| 369 _constantContext = true; | |
| 370 } else if (n is FormalParameter) { | |
| 371 _constantContext = o || n.isConst; | |
| 372 } else if (n is InstanceCreationExpression) { | |
| 373 _constantContext = o || n.isConst; | |
| 374 } else if (n is ConstructorDeclaration) { | |
| 375 _constantContext = o || n.element.isConst; | |
| 376 } | |
| 377 } | |
| 378 visitNode(n); | |
| 379 _constantContext = o; | |
| 380 } | |
| 381 | |
| 382 @override | 360 @override |
| 383 void visitComment(Comment node) { | 361 void visitComment(Comment node) { |
| 384 // skip, no need to do typechecking inside comments (they may contain | 362 // skip, no need to do typechecking inside comments (they may contain |
| 385 // comment references which would require resolution). | 363 // comment references which would require resolution). |
| 386 } | 364 } |
| 387 | 365 |
| 388 @override | 366 @override |
| 389 void visitClassDeclaration(ClassDeclaration node) { | 367 void visitClassDeclaration(ClassDeclaration node) { |
| 390 _overrideChecker.check(node); | 368 _overrideChecker.check(node); |
| 391 super.visitClassDeclaration(node); | 369 super.visitClassDeclaration(node); |
| 392 } | 370 } |
| 393 | 371 |
| 394 @override | 372 @override |
| 395 void visitAssignmentExpression(AssignmentExpression node) { | 373 void visitAssignmentExpression(AssignmentExpression node) { |
| 396 var token = node.operator; | 374 var token = node.operator; |
| 397 if (token.type != TokenType.EQ) { | 375 if (token.type != TokenType.EQ) { |
| 398 _checkCompoundAssignment(node); | 376 _checkCompoundAssignment(node); |
| 399 } else { | 377 } else { |
| 400 DartType staticType = _rules.getStaticType(node.leftHandSide); | 378 DartType staticType = _rules.getStaticType(node.leftHandSide); |
| 401 checkAssignment(node.rightHandSide, staticType); | 379 checkAssignment(node.rightHandSide, staticType); |
| 402 } | 380 } |
| 403 node.visitChildren(this); | 381 node.visitChildren(this); |
| 404 } | 382 } |
| 405 | 383 |
| 406 /// Check constructor declaration to ensure correct super call placement. | 384 /// Check constructor declaration to ensure correct super call placement. |
| 407 @override | 385 @override |
| 408 void visitConstructorDeclaration(ConstructorDeclaration node) { | 386 void visitConstructorDeclaration(ConstructorDeclaration node) { |
| 409 _visitMaybeConst(node, (node) { | 387 node.visitChildren(this); |
| 410 node.visitChildren(this); | |
| 411 | 388 |
| 412 final init = node.initializers; | 389 final init = node.initializers; |
| 413 for (int i = 0, last = init.length - 1; i < last; i++) { | 390 for (int i = 0, last = init.length - 1; i < last; i++) { |
| 414 final node = init[i]; | 391 final node = init[i]; |
| 415 if (node is SuperConstructorInvocation) { | 392 if (node is SuperConstructorInvocation) { |
| 416 _recordMessage(new InvalidSuperInvocation(node)); | 393 _recordMessage(new InvalidSuperInvocation(node)); |
| 417 } | |
| 418 } | 394 } |
| 419 }); | 395 } |
| 420 } | 396 } |
| 421 | 397 |
| 422 @override | 398 @override |
| 423 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | 399 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 424 var field = node.fieldName; | 400 var field = node.fieldName; |
| 425 DartType staticType = _rules.elementType(field.staticElement); | 401 DartType staticType = _rules.elementType(field.staticElement); |
| 426 checkAssignment(node.expression, staticType); | 402 checkAssignment(node.expression, staticType); |
| 427 node.visitChildren(this); | 403 node.visitChildren(this); |
| 428 } | 404 } |
| 429 | 405 |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 var element = node.staticElement; | 572 var element = node.staticElement; |
| 597 if (element == null) { | 573 if (element == null) { |
| 598 _recordMessage(new MissingTypeError(node)); | 574 _recordMessage(new MissingTypeError(node)); |
| 599 } else { | 575 } else { |
| 600 var type = node.staticElement.type; | 576 var type = node.staticElement.type; |
| 601 checkArgumentList(node.argumentList, type); | 577 checkArgumentList(node.argumentList, type); |
| 602 } | 578 } |
| 603 node.visitChildren(this); | 579 node.visitChildren(this); |
| 604 } | 580 } |
| 605 | 581 |
| 606 AstNode _getOwnerFunction(AstNode node) { | 582 DartType _getExpectedReturnType(FunctionBody body, bool yieldStar) { |
| 607 var parent = node.parent; | 583 FunctionType functionType; |
| 608 while (parent is! FunctionExpression && | 584 var parent = body.parent; |
| 609 parent is! MethodDeclaration && | 585 if (parent is Declaration) { |
| 610 parent is! ConstructorDeclaration) { | 586 functionType = _rules.elementType(parent.element); |
| 611 parent = parent.parent; | 587 } else { |
| 588 assert(parent is FunctionExpression); |
| 589 functionType = _rules.getStaticType(parent); |
| 612 } | 590 } |
| 613 return parent; | |
| 614 } | |
| 615 | 591 |
| 616 FunctionType _getFunctionType(AstNode node) { | 592 var type = functionType.returnType; |
| 617 if (node is Declaration) { | 593 var provider = _rules.provider; |
| 618 return _rules.elementType(node.element); | 594 |
| 595 InterfaceType expectedType = null; |
| 596 if (body.isAsynchronous) { |
| 597 if (body.isGenerator) { |
| 598 // Stream<T> -> T |
| 599 expectedType = provider.streamType; |
| 600 } else { |
| 601 // Future<T> -> T |
| 602 // TODO(vsm): Revisit with issue #228. |
| 603 expectedType = provider.futureType; |
| 604 } |
| 619 } else { | 605 } else { |
| 620 assert(node is FunctionExpression); | 606 if (body.isGenerator) { |
| 621 return _rules.getStaticType(node); | 607 // Iterable<T> -> T |
| 608 expectedType = provider.iterableType; |
| 609 } else { |
| 610 // T -> T |
| 611 return type; |
| 612 } |
| 613 } |
| 614 if (yieldStar) { |
| 615 if (type.isDynamic) { |
| 616 // Ensure it's at least a Stream / Iterable. |
| 617 return expectedType.substitute4([provider.dynamicType]); |
| 618 } else { |
| 619 // Analyzer will provide a separate error if expected type |
| 620 // is not compatible with type. |
| 621 return type; |
| 622 } |
| 623 } |
| 624 if (type.isDynamic) { |
| 625 return type; |
| 626 } else if (type is InterfaceType && type.element == expectedType.element) { |
| 627 return type.typeArguments[0]; |
| 628 } else { |
| 629 // Malformed type - fallback on analyzer error. |
| 630 return null; |
| 622 } | 631 } |
| 623 } | 632 } |
| 624 | 633 |
| 625 void _checkReturn(Expression expression, AstNode node) { | 634 FunctionBody _getFunctionBody(AstNode node) { |
| 626 var type = _getFunctionType(_getOwnerFunction(node)).returnType; | 635 while (node is! FunctionBody) { |
| 636 node = node.parent; |
| 637 } |
| 638 return node as FunctionBody; |
| 639 } |
| 640 |
| 641 void _checkReturnOrYield(Expression expression, AstNode node, |
| 642 [bool yieldStar = false]) { |
| 643 var body = _getFunctionBody(node); |
| 644 var type = _getExpectedReturnType(body, yieldStar); |
| 645 if (type == null) { |
| 646 // We have a type mismatch: the async/async*/sync* modifier does |
| 647 // not match the return or yield type. We should have already gotten an |
| 648 // analyzer error in this case. |
| 649 return; |
| 650 } |
| 627 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. | 651 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. |
| 628 if (expression != null) checkAssignment(expression, type); | 652 if (expression != null) checkAssignment(expression, type); |
| 629 } | 653 } |
| 630 | 654 |
| 631 @override | 655 @override |
| 632 void visitExpressionFunctionBody(ExpressionFunctionBody node) { | 656 void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 633 _checkReturn(node.expression, node); | 657 _checkReturnOrYield(node.expression, node); |
| 634 node.visitChildren(this); | 658 node.visitChildren(this); |
| 635 } | 659 } |
| 636 | 660 |
| 637 @override | 661 @override |
| 638 void visitReturnStatement(ReturnStatement node) { | 662 void visitReturnStatement(ReturnStatement node) { |
| 639 _checkReturn(node.expression, node); | 663 _checkReturnOrYield(node.expression, node); |
| 640 node.visitChildren(this); | 664 node.visitChildren(this); |
| 641 } | 665 } |
| 642 | 666 |
| 667 @override |
| 668 void visitYieldStatement(YieldStatement node) { |
| 669 _checkReturnOrYield(node.expression, node, node.star != null); |
| 670 node.visitChildren(this); |
| 671 } |
| 672 |
| 643 @override | 673 @override |
| 644 void visitPropertyAccess(PropertyAccess node) { | 674 void visitPropertyAccess(PropertyAccess node) { |
| 645 var target = node.realTarget; | 675 var target = node.realTarget; |
| 646 if (_rules.isDynamicTarget(target)) { | 676 if (_rules.isDynamicTarget(target)) { |
| 647 _recordDynamicInvoke(node, target); | 677 _recordDynamicInvoke(node, target); |
| 648 } | 678 } |
| 649 node.visitChildren(this); | 679 node.visitChildren(this); |
| 650 } | 680 } |
| 651 | 681 |
| 652 @override | 682 @override |
| 653 void visitPrefixedIdentifier(PrefixedIdentifier node) { | 683 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 654 final target = node.prefix; | 684 final target = node.prefix; |
| 655 if (_rules.isDynamicTarget(target)) { | 685 if (_rules.isDynamicTarget(target)) { |
| 656 _recordDynamicInvoke(node, target); | 686 _recordDynamicInvoke(node, target); |
| 657 } | 687 } |
| 658 node.visitChildren(this); | 688 node.visitChildren(this); |
| 659 } | 689 } |
| 660 | 690 |
| 661 @override | 691 @override |
| 662 void visitDefaultFormalParameter(DefaultFormalParameter node) { | 692 void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 663 _visitMaybeConst(node, (node) { | 693 // Check that defaults have the proper subtype. |
| 664 // Check that defaults have the proper subtype. | 694 var parameter = node.parameter; |
| 665 var parameter = node.parameter; | 695 var parameterType = _rules.elementType(parameter.element); |
| 666 var parameterType = _rules.elementType(parameter.element); | 696 assert(parameterType != null); |
| 667 assert(parameterType != null); | 697 var defaultValue = node.defaultValue; |
| 668 var defaultValue = node.defaultValue; | 698 if (defaultValue == null) { |
| 669 if (defaultValue == null) { | 699 if (_rules.maybeNonNullableType(parameterType)) { |
| 670 if (_rules.maybeNonNullableType(parameterType)) { | 700 var staticInfo = new InvalidVariableDeclaration( |
| 671 var staticInfo = new InvalidVariableDeclaration( | 701 _rules, node.identifier, parameterType); |
| 672 _rules, node.identifier, parameterType); | 702 _recordMessage(staticInfo); |
| 673 _recordMessage(staticInfo); | |
| 674 } | |
| 675 } else { | |
| 676 checkAssignment(defaultValue, parameterType); | |
| 677 } | 703 } |
| 704 } else { |
| 705 checkAssignment(defaultValue, parameterType); |
| 706 } |
| 678 | 707 |
| 679 node.visitChildren(this); | 708 node.visitChildren(this); |
| 680 }); | |
| 681 } | 709 } |
| 682 | 710 |
| 683 @override | 711 @override |
| 684 void visitFieldFormalParameter(FieldFormalParameter node) { | 712 void visitFieldFormalParameter(FieldFormalParameter node) { |
| 685 var element = node.element; | 713 var element = node.element; |
| 686 var typeName = node.type; | 714 var typeName = node.type; |
| 687 if (typeName != null) { | 715 if (typeName != null) { |
| 688 var type = _rules.elementType(element); | 716 var type = _rules.elementType(element); |
| 689 var fieldElement = | 717 var fieldElement = |
| 690 node.identifier.staticElement as FieldFormalParameterElement; | 718 node.identifier.staticElement as FieldFormalParameterElement; |
| 691 var fieldType = _rules.elementType(fieldElement.field); | 719 var fieldType = _rules.elementType(fieldElement.field); |
| 692 if (!_rules.isSubTypeOf(type, fieldType)) { | 720 if (!_rules.isSubTypeOf(type, fieldType)) { |
| 693 var staticInfo = | 721 var staticInfo = |
| 694 new InvalidParameterDeclaration(_rules, node, fieldType); | 722 new InvalidParameterDeclaration(_rules, node, fieldType); |
| 695 _recordMessage(staticInfo); | 723 _recordMessage(staticInfo); |
| 696 } | 724 } |
| 697 } | 725 } |
| 698 node.visitChildren(this); | 726 node.visitChildren(this); |
| 699 } | 727 } |
| 700 | 728 |
| 701 @override | 729 @override |
| 702 void visitInstanceCreationExpression(InstanceCreationExpression node) { | 730 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 703 _visitMaybeConst(node, (node) { | 731 var arguments = node.argumentList; |
| 704 var arguments = node.argumentList; | 732 var element = node.staticElement; |
| 705 var element = node.staticElement; | 733 if (element != null) { |
| 706 if (element != null) { | 734 var type = _rules.elementType(node.staticElement); |
| 707 var type = _rules.elementType(node.staticElement); | 735 checkArgumentList(arguments, type); |
| 708 checkArgumentList(arguments, type); | 736 } else { |
| 709 } else { | 737 _recordMessage(new MissingTypeError(node)); |
| 710 _recordMessage(new MissingTypeError(node)); | 738 } |
| 711 } | 739 node.visitChildren(this); |
| 712 node.visitChildren(this); | |
| 713 }); | |
| 714 } | 740 } |
| 715 | 741 |
| 716 @override | 742 @override |
| 717 void visitVariableDeclarationList(VariableDeclarationList node) { | 743 void visitVariableDeclarationList(VariableDeclarationList node) { |
| 718 _visitMaybeConst(node, (node) { | 744 TypeName type = node.type; |
| 719 TypeName type = node.type; | 745 if (type == null) { |
| 720 if (type == null) { | 746 // No checks are needed when the type is var. Although internally the |
| 721 // No checks are needed when the type is var. Although internally the | 747 // typing rules may have inferred a more precise type for the variable |
| 722 // typing rules may have inferred a more precise type for the variable | 748 // based on the initializer. |
| 723 // based on the initializer. | 749 } else { |
| 724 } else { | 750 var dartType = getType(type); |
| 725 var dartType = getType(type); | 751 for (VariableDeclaration variable in node.variables) { |
| 726 for (VariableDeclaration variable in node.variables) { | 752 var initializer = variable.initializer; |
| 727 var initializer = variable.initializer; | 753 if (initializer != null) { |
| 728 if (initializer != null) { | 754 checkAssignment(initializer, dartType); |
| 729 checkAssignment(initializer, dartType); | 755 } else if (_rules.maybeNonNullableType(dartType)) { |
| 730 } else if (_rules.maybeNonNullableType(dartType)) { | 756 var element = variable.element; |
| 731 var element = variable.element; | 757 if (element is FieldElement && !element.isStatic) { |
| 732 if (element is FieldElement && !element.isStatic) { | 758 // Initialized - possibly implicitly - during construction. |
| 733 // Initialized - possibly implicitly - during construction. | 759 // Handle this via a runtime check during code generation. |
| 734 // Handle this via a runtime check during code generation. | |
| 735 | 760 |
| 736 // TODO(vsm): Detect statically whether this can fail and | 761 // TODO(vsm): Detect statically whether this can fail and |
| 737 // report a static error (must fail) or warning (can fail). | 762 // report a static error (must fail) or warning (can fail). |
| 738 } else { | 763 } else { |
| 739 var staticInfo = | 764 var staticInfo = |
| 740 new InvalidVariableDeclaration(_rules, variable, dartType); | 765 new InvalidVariableDeclaration(_rules, variable, dartType); |
| 741 _recordMessage(staticInfo); | 766 _recordMessage(staticInfo); |
| 742 } | |
| 743 } | 767 } |
| 744 } | 768 } |
| 745 } | 769 } |
| 746 node.visitChildren(this); | 770 } |
| 747 }); | 771 node.visitChildren(this); |
| 748 } | |
| 749 | |
| 750 @override | |
| 751 void visitVariableDeclaration(VariableDeclaration node) { | |
| 752 _visitMaybeConst(node, super.visitVariableDeclaration); | |
| 753 } | 772 } |
| 754 | 773 |
| 755 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { | 774 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { |
| 756 var type = getType(typeName); | 775 var type = getType(typeName); |
| 757 if (!_rules.isGroundType(type)) { | 776 if (!_rules.isGroundType(type)) { |
| 758 _recordMessage(new InvalidRuntimeCheckError(node, type)); | 777 _recordMessage(new InvalidRuntimeCheckError(node, type)); |
| 759 } | 778 } |
| 760 } | 779 } |
| 761 | 780 |
| 762 @override | 781 @override |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 878 /// Analyzer checks boolean conversions, but we need to check too, because | 897 /// Analyzer checks boolean conversions, but we need to check too, because |
| 879 /// it uses the default assignability rules that allow `dynamic` and `Object` | 898 /// it uses the default assignability rules that allow `dynamic` and `Object` |
| 880 /// to be assigned to bool with no message. | 899 /// to be assigned to bool with no message. |
| 881 void checkBoolean(Expression expr) => | 900 void checkBoolean(Expression expr) => |
| 882 checkAssignment(expr, _rules.provider.boolType); | 901 checkAssignment(expr, _rules.provider.boolType); |
| 883 | 902 |
| 884 void checkAssignment(Expression expr, DartType type) { | 903 void checkAssignment(Expression expr, DartType type) { |
| 885 if (expr is ParenthesizedExpression) { | 904 if (expr is ParenthesizedExpression) { |
| 886 checkAssignment(expr.expression, type); | 905 checkAssignment(expr.expression, type); |
| 887 } else { | 906 } else { |
| 888 _recordMessage(_rules.checkAssignment(expr, type, _constantContext)); | 907 _recordMessage(_rules.checkAssignment(expr, type)); |
| 889 } | 908 } |
| 890 } | 909 } |
| 891 | 910 |
| 892 DartType _specializedBinaryReturnType( | 911 DartType _specializedBinaryReturnType( |
| 893 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { | 912 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { |
| 894 // This special cases binary return types as per 16.26 and 16.27 of the | 913 // This special cases binary return types as per 16.26 and 16.27 of the |
| 895 // Dart language spec. | 914 // Dart language spec. |
| 896 switch (op) { | 915 switch (op) { |
| 897 case TokenType.PLUS: | 916 case TokenType.PLUS: |
| 898 case TokenType.MINUS: | 917 case TokenType.MINUS: |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 949 } else { | 968 } else { |
| 950 // Static type error | 969 // Static type error |
| 951 staticInfo = new StaticTypeError(_rules, expr, lhsType); | 970 staticInfo = new StaticTypeError(_rules, expr, lhsType); |
| 952 } | 971 } |
| 953 _recordMessage(staticInfo); | 972 _recordMessage(staticInfo); |
| 954 } | 973 } |
| 955 | 974 |
| 956 // Check the rhs type | 975 // Check the rhs type |
| 957 if (staticInfo is! CoercionInfo) { | 976 if (staticInfo is! CoercionInfo) { |
| 958 var paramType = paramTypes.first; | 977 var paramType = paramTypes.first; |
| 959 staticInfo = _rules.checkAssignment( | 978 staticInfo = _rules.checkAssignment(expr.rightHandSide, paramType); |
| 960 expr.rightHandSide, paramType, _constantContext); | |
| 961 _recordMessage(staticInfo); | 979 _recordMessage(staticInfo); |
| 962 } | 980 } |
| 963 } | 981 } |
| 964 } | 982 } |
| 965 | 983 |
| 966 void _recordDynamicInvoke(AstNode node, AstNode target) { | 984 void _recordDynamicInvoke(AstNode node, AstNode target) { |
| 967 var dinvoke = new DynamicInvoke(_rules, node); | 985 var dinvoke = new DynamicInvoke(_rules, node); |
| 968 _reporter.log(dinvoke); | 986 _reporter.log(dinvoke); |
| 969 // TODO(jmesserly): we may eventually want to record if the whole operation | 987 // TODO(jmesserly): we may eventually want to record if the whole operation |
| 970 // (node) was dynamic, rather than the target, but this is an easier fit | 988 // (node) was dynamic, rather than the target, but this is an easier fit |
| 971 // with what we used to do. | 989 // with what we used to do. |
| 972 DynamicInvoke.set(target, true); | 990 DynamicInvoke.set(target, true); |
| 973 } | 991 } |
| 974 | 992 |
| 975 void _recordMessage(StaticInfo info) { | 993 void _recordMessage(StaticInfo info) { |
| 976 if (info == null) return; | 994 if (info == null) return; |
| 977 if (info.level >= logger.Level.SEVERE) _failure = true; | 995 if (info.level >= logger.Level.SEVERE) _failure = true; |
| 978 _reporter.log(info); | 996 _reporter.log(info); |
| 979 if (info is CoercionInfo) { | 997 if (info is CoercionInfo) { |
| 980 assert(CoercionInfo.get(info.node) == null); | 998 assert(CoercionInfo.get(info.node) == null); |
| 981 CoercionInfo.set(info.node, info); | 999 CoercionInfo.set(info.node, info); |
| 982 } | 1000 } |
| 983 } | 1001 } |
| 984 } | 1002 } |
| OLD | NEW |