Chromium Code Reviews| 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) { |
| 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 if (type.isDynamic) return provider.dynamicType; |
| 595 | |
| 596 InterfaceType expectedType = null; | |
| 597 if (body.isAsynchronous) { | |
| 598 if (body.isGenerator) { | |
| 599 // Stream<T> -> T | |
| 600 expectedType = provider.streamType; | |
| 601 } else { | |
| 602 // Future<T> -> T | |
| 603 // TODO(vsm): Revisit with issue #228. | |
| 604 expectedType = provider.futureType; | |
| 605 } | |
| 619 } else { | 606 } else { |
| 620 assert(node is FunctionExpression); | 607 if (body.isGenerator) { |
| 621 return _rules.getStaticType(node); | 608 // Iterable<T> -> T |
| 609 expectedType = provider.iterableType; | |
| 610 } else { | |
| 611 // T -> T | |
| 612 return type; | |
| 613 } | |
| 614 } | |
| 615 if (type is InterfaceType && type.element == expectedType.element) { | |
| 616 return type.typeArguments[0]; | |
| 617 } else { | |
| 618 return null; | |
| 622 } | 619 } |
| 623 } | 620 } |
| 624 | 621 |
| 625 void _checkReturn(Expression expression, AstNode node) { | 622 FunctionBody _getFunctionBody(AstNode node) { |
| 626 var type = _getFunctionType(_getOwnerFunction(node)).returnType; | 623 while (node is! FunctionBody) { |
| 624 node = node.parent; | |
| 625 } | |
| 626 return node as FunctionBody; | |
| 627 } | |
| 628 | |
| 629 void _checkReturnOrYield(Expression expression, AstNode node) { | |
| 630 var body = _getFunctionBody(node); | |
| 631 var type = _getExpectedReturnType(body); | |
| 632 if (type == null) { | |
| 633 // We have a type mismatch: the async/async*/sync* modifier does | |
| 634 // not match the return or yield type. We should have already gotten an | |
| 635 // analyzer error in this case. | |
| 636 return; | |
| 637 } | |
| 627 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. | 638 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. |
| 628 if (expression != null) checkAssignment(expression, type); | 639 if (expression != null) checkAssignment(expression, type); |
| 629 } | 640 } |
| 630 | 641 |
| 631 @override | 642 @override |
| 632 void visitExpressionFunctionBody(ExpressionFunctionBody node) { | 643 void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 633 _checkReturn(node.expression, node); | 644 _checkReturnOrYield(node.expression, node); |
| 634 node.visitChildren(this); | 645 node.visitChildren(this); |
| 635 } | 646 } |
| 636 | 647 |
| 637 @override | 648 @override |
| 638 void visitReturnStatement(ReturnStatement node) { | 649 void visitReturnStatement(ReturnStatement node) { |
| 639 _checkReturn(node.expression, node); | 650 _checkReturnOrYield(node.expression, node); |
| 640 node.visitChildren(this); | 651 node.visitChildren(this); |
| 641 } | 652 } |
| 642 | 653 |
| 654 @override | |
| 655 void visitYieldStatement(YieldStatement node) { | |
| 656 _checkReturnOrYield(node.expression, node); | |
|
Leaf
2015/06/19 22:15:27
I think this will do the wrong thing if this yield
| |
| 657 node.visitChildren(this); | |
| 658 } | |
| 659 | |
| 643 @override | 660 @override |
| 644 void visitPropertyAccess(PropertyAccess node) { | 661 void visitPropertyAccess(PropertyAccess node) { |
| 645 var target = node.realTarget; | 662 var target = node.realTarget; |
| 646 if (_rules.isDynamicTarget(target)) { | 663 if (_rules.isDynamicTarget(target)) { |
| 647 _recordDynamicInvoke(node, target); | 664 _recordDynamicInvoke(node, target); |
| 648 } | 665 } |
| 649 node.visitChildren(this); | 666 node.visitChildren(this); |
| 650 } | 667 } |
| 651 | 668 |
| 652 @override | 669 @override |
| 653 void visitPrefixedIdentifier(PrefixedIdentifier node) { | 670 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 654 final target = node.prefix; | 671 final target = node.prefix; |
| 655 if (_rules.isDynamicTarget(target)) { | 672 if (_rules.isDynamicTarget(target)) { |
| 656 _recordDynamicInvoke(node, target); | 673 _recordDynamicInvoke(node, target); |
| 657 } | 674 } |
| 658 node.visitChildren(this); | 675 node.visitChildren(this); |
| 659 } | 676 } |
| 660 | 677 |
| 661 @override | 678 @override |
| 662 void visitDefaultFormalParameter(DefaultFormalParameter node) { | 679 void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 663 _visitMaybeConst(node, (node) { | 680 // Check that defaults have the proper subtype. |
| 664 // Check that defaults have the proper subtype. | 681 var parameter = node.parameter; |
| 665 var parameter = node.parameter; | 682 var parameterType = _rules.elementType(parameter.element); |
| 666 var parameterType = _rules.elementType(parameter.element); | 683 assert(parameterType != null); |
| 667 assert(parameterType != null); | 684 var defaultValue = node.defaultValue; |
| 668 var defaultValue = node.defaultValue; | 685 if (defaultValue == null) { |
| 669 if (defaultValue == null) { | 686 if (_rules.maybeNonNullableType(parameterType)) { |
| 670 if (_rules.maybeNonNullableType(parameterType)) { | 687 var staticInfo = new InvalidVariableDeclaration( |
| 671 var staticInfo = new InvalidVariableDeclaration( | 688 _rules, node.identifier, parameterType); |
| 672 _rules, node.identifier, parameterType); | 689 _recordMessage(staticInfo); |
| 673 _recordMessage(staticInfo); | |
| 674 } | |
| 675 } else { | |
| 676 checkAssignment(defaultValue, parameterType); | |
| 677 } | 690 } |
| 691 } else { | |
| 692 checkAssignment(defaultValue, parameterType); | |
| 693 } | |
| 678 | 694 |
| 679 node.visitChildren(this); | 695 node.visitChildren(this); |
| 680 }); | |
| 681 } | 696 } |
| 682 | 697 |
| 683 @override | 698 @override |
| 684 void visitFieldFormalParameter(FieldFormalParameter node) { | 699 void visitFieldFormalParameter(FieldFormalParameter node) { |
| 685 var element = node.element; | 700 var element = node.element; |
| 686 var typeName = node.type; | 701 var typeName = node.type; |
| 687 if (typeName != null) { | 702 if (typeName != null) { |
| 688 var type = _rules.elementType(element); | 703 var type = _rules.elementType(element); |
| 689 var fieldElement = | 704 var fieldElement = |
| 690 node.identifier.staticElement as FieldFormalParameterElement; | 705 node.identifier.staticElement as FieldFormalParameterElement; |
| 691 var fieldType = _rules.elementType(fieldElement.field); | 706 var fieldType = _rules.elementType(fieldElement.field); |
| 692 if (!_rules.isSubTypeOf(type, fieldType)) { | 707 if (!_rules.isSubTypeOf(type, fieldType)) { |
| 693 var staticInfo = | 708 var staticInfo = |
| 694 new InvalidParameterDeclaration(_rules, node, fieldType); | 709 new InvalidParameterDeclaration(_rules, node, fieldType); |
| 695 _recordMessage(staticInfo); | 710 _recordMessage(staticInfo); |
| 696 } | 711 } |
| 697 } | 712 } |
| 698 node.visitChildren(this); | 713 node.visitChildren(this); |
| 699 } | 714 } |
| 700 | 715 |
| 701 @override | 716 @override |
| 702 void visitInstanceCreationExpression(InstanceCreationExpression node) { | 717 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 703 _visitMaybeConst(node, (node) { | 718 var arguments = node.argumentList; |
| 704 var arguments = node.argumentList; | 719 var element = node.staticElement; |
| 705 var element = node.staticElement; | 720 if (element != null) { |
| 706 if (element != null) { | 721 var type = _rules.elementType(node.staticElement); |
| 707 var type = _rules.elementType(node.staticElement); | 722 checkArgumentList(arguments, type); |
| 708 checkArgumentList(arguments, type); | 723 } else { |
| 709 } else { | 724 _recordMessage(new MissingTypeError(node)); |
| 710 _recordMessage(new MissingTypeError(node)); | 725 } |
| 711 } | 726 node.visitChildren(this); |
| 712 node.visitChildren(this); | |
| 713 }); | |
| 714 } | 727 } |
| 715 | 728 |
| 716 @override | 729 @override |
| 717 void visitVariableDeclarationList(VariableDeclarationList node) { | 730 void visitVariableDeclarationList(VariableDeclarationList node) { |
| 718 _visitMaybeConst(node, (node) { | 731 TypeName type = node.type; |
| 719 TypeName type = node.type; | 732 if (type == null) { |
| 720 if (type == null) { | 733 // 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 | 734 // 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 | 735 // based on the initializer. |
| 723 // based on the initializer. | 736 } else { |
| 724 } else { | 737 var dartType = getType(type); |
| 725 var dartType = getType(type); | 738 for (VariableDeclaration variable in node.variables) { |
| 726 for (VariableDeclaration variable in node.variables) { | 739 var initializer = variable.initializer; |
| 727 var initializer = variable.initializer; | 740 if (initializer != null) { |
| 728 if (initializer != null) { | 741 checkAssignment(initializer, dartType); |
| 729 checkAssignment(initializer, dartType); | 742 } else if (_rules.maybeNonNullableType(dartType)) { |
| 730 } else if (_rules.maybeNonNullableType(dartType)) { | 743 var element = variable.element; |
| 731 var element = variable.element; | 744 if (element is FieldElement && !element.isStatic) { |
| 732 if (element is FieldElement && !element.isStatic) { | 745 // Initialized - possibly implicitly - during construction. |
| 733 // Initialized - possibly implicitly - during construction. | 746 // Handle this via a runtime check during code generation. |
| 734 // Handle this via a runtime check during code generation. | |
| 735 | 747 |
| 736 // TODO(vsm): Detect statically whether this can fail and | 748 // TODO(vsm): Detect statically whether this can fail and |
| 737 // report a static error (must fail) or warning (can fail). | 749 // report a static error (must fail) or warning (can fail). |
| 738 } else { | 750 } else { |
| 739 var staticInfo = | 751 var staticInfo = |
| 740 new InvalidVariableDeclaration(_rules, variable, dartType); | 752 new InvalidVariableDeclaration(_rules, variable, dartType); |
| 741 _recordMessage(staticInfo); | 753 _recordMessage(staticInfo); |
| 742 } | |
| 743 } | 754 } |
| 744 } | 755 } |
| 745 } | 756 } |
| 746 node.visitChildren(this); | 757 } |
| 747 }); | 758 node.visitChildren(this); |
| 748 } | |
| 749 | |
| 750 @override | |
| 751 void visitVariableDeclaration(VariableDeclaration node) { | |
| 752 _visitMaybeConst(node, super.visitVariableDeclaration); | |
| 753 } | 759 } |
| 754 | 760 |
| 755 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { | 761 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { |
| 756 var type = getType(typeName); | 762 var type = getType(typeName); |
| 757 if (!_rules.isGroundType(type)) { | 763 if (!_rules.isGroundType(type)) { |
| 758 _recordMessage(new InvalidRuntimeCheckError(node, type)); | 764 _recordMessage(new InvalidRuntimeCheckError(node, type)); |
| 759 } | 765 } |
| 760 } | 766 } |
| 761 | 767 |
| 762 @override | 768 @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 | 884 /// Analyzer checks boolean conversions, but we need to check too, because |
| 879 /// it uses the default assignability rules that allow `dynamic` and `Object` | 885 /// it uses the default assignability rules that allow `dynamic` and `Object` |
| 880 /// to be assigned to bool with no message. | 886 /// to be assigned to bool with no message. |
| 881 void checkBoolean(Expression expr) => | 887 void checkBoolean(Expression expr) => |
| 882 checkAssignment(expr, _rules.provider.boolType); | 888 checkAssignment(expr, _rules.provider.boolType); |
| 883 | 889 |
| 884 void checkAssignment(Expression expr, DartType type) { | 890 void checkAssignment(Expression expr, DartType type) { |
| 885 if (expr is ParenthesizedExpression) { | 891 if (expr is ParenthesizedExpression) { |
| 886 checkAssignment(expr.expression, type); | 892 checkAssignment(expr.expression, type); |
| 887 } else { | 893 } else { |
| 888 _recordMessage(_rules.checkAssignment(expr, type, _constantContext)); | 894 _recordMessage(_rules.checkAssignment(expr, type)); |
| 889 } | 895 } |
| 890 } | 896 } |
| 891 | 897 |
| 892 DartType _specializedBinaryReturnType( | 898 DartType _specializedBinaryReturnType( |
| 893 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { | 899 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { |
| 894 // This special cases binary return types as per 16.26 and 16.27 of the | 900 // This special cases binary return types as per 16.26 and 16.27 of the |
| 895 // Dart language spec. | 901 // Dart language spec. |
| 896 switch (op) { | 902 switch (op) { |
| 897 case TokenType.PLUS: | 903 case TokenType.PLUS: |
| 898 case TokenType.MINUS: | 904 case TokenType.MINUS: |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 949 } else { | 955 } else { |
| 950 // Static type error | 956 // Static type error |
| 951 staticInfo = new StaticTypeError(_rules, expr, lhsType); | 957 staticInfo = new StaticTypeError(_rules, expr, lhsType); |
| 952 } | 958 } |
| 953 _recordMessage(staticInfo); | 959 _recordMessage(staticInfo); |
| 954 } | 960 } |
| 955 | 961 |
| 956 // Check the rhs type | 962 // Check the rhs type |
| 957 if (staticInfo is! CoercionInfo) { | 963 if (staticInfo is! CoercionInfo) { |
| 958 var paramType = paramTypes.first; | 964 var paramType = paramTypes.first; |
| 959 staticInfo = _rules.checkAssignment( | 965 staticInfo = _rules.checkAssignment(expr.rightHandSide, paramType); |
| 960 expr.rightHandSide, paramType, _constantContext); | |
| 961 _recordMessage(staticInfo); | 966 _recordMessage(staticInfo); |
| 962 } | 967 } |
| 963 } | 968 } |
| 964 } | 969 } |
| 965 | 970 |
| 966 void _recordDynamicInvoke(AstNode node, AstNode target) { | 971 void _recordDynamicInvoke(AstNode node, AstNode target) { |
| 967 var dinvoke = new DynamicInvoke(_rules, node); | 972 var dinvoke = new DynamicInvoke(_rules, node); |
| 968 _reporter.log(dinvoke); | 973 _reporter.log(dinvoke); |
| 969 // TODO(jmesserly): we may eventually want to record if the whole operation | 974 // 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 | 975 // (node) was dynamic, rather than the target, but this is an easier fit |
| 971 // with what we used to do. | 976 // with what we used to do. |
| 972 DynamicInvoke.set(target, true); | 977 DynamicInvoke.set(target, true); |
| 973 } | 978 } |
| 974 | 979 |
| 975 void _recordMessage(StaticInfo info) { | 980 void _recordMessage(StaticInfo info) { |
| 976 if (info == null) return; | 981 if (info == null) return; |
| 977 if (info.level >= logger.Level.SEVERE) _failure = true; | 982 if (info.level >= logger.Level.SEVERE) _failure = true; |
| 978 _reporter.log(info); | 983 _reporter.log(info); |
| 979 if (info is CoercionInfo) { | 984 if (info is CoercionInfo) { |
| 980 assert(CoercionInfo.get(info.node) == null); | 985 assert(CoercionInfo.get(info.node) == null); |
| 981 CoercionInfo.set(info.node, info); | 986 CoercionInfo.set(info.node, info); |
| 982 } | 987 } |
| 983 } | 988 } |
| 984 } | 989 } |
| OLD | NEW |