| 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 ddc.src.checker.checker; | 5 library ddc.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; |
| 340 bool _failure = false; | 341 bool _failure = false; |
| 341 bool get failure => _failure || _overrideChecker._failure; | 342 bool get failure => _failure || _overrideChecker._failure; |
| 342 | 343 |
| 343 CodeChecker( | 344 CodeChecker( |
| 344 TypeRules rules, CheckerReporter reporter, CompilerOptions options) | 345 TypeRules rules, CheckerReporter reporter, CompilerOptions options) |
| 345 : _rules = rules, | 346 : _rules = rules, |
| 346 _reporter = reporter, | 347 _reporter = reporter, |
| 347 _overrideChecker = new _OverrideChecker(rules, reporter, options); | 348 _overrideChecker = new _OverrideChecker(rules, reporter, options); |
| 348 | 349 |
| 350 _visitMaybeConst(AstNode n, visitNode(AstNode n)) { |
| 351 var o = _constantContext; |
| 352 if (!o) { |
| 353 if (n is VariableDeclarationList) { |
| 354 _constantContext = o || n.isConst; |
| 355 } else if (n is VariableDeclaration) { |
| 356 _constantContext = o || n.isConst; |
| 357 } else if (n is FormalParameter) { |
| 358 _constantContext = o || n.isConst; |
| 359 } else if (n is InstanceCreationExpression) { |
| 360 _constantContext = o || n.isConst; |
| 361 } else if (n is ConstructorDeclaration) { |
| 362 _constantContext = o || n.element.isConst; |
| 363 } |
| 364 } |
| 365 visitNode(n); |
| 366 _constantContext = o; |
| 367 } |
| 368 |
| 349 visitComment(Comment node) { | 369 visitComment(Comment node) { |
| 350 // skip, no need to do typechecking inside comments (they may contain | 370 // skip, no need to do typechecking inside comments (they may contain |
| 351 // comment references which would require resolution). | 371 // comment references which would require resolution). |
| 352 } | 372 } |
| 353 | 373 |
| 354 visitClassDeclaration(ClassDeclaration node) { | 374 visitClassDeclaration(ClassDeclaration node) { |
| 355 _overrideChecker.check(node); | 375 _overrideChecker.check(node); |
| 356 super.visitClassDeclaration(node); | 376 super.visitClassDeclaration(node); |
| 357 } | 377 } |
| 358 | 378 |
| 359 visitAssignmentExpression(AssignmentExpression node) { | 379 visitAssignmentExpression(AssignmentExpression node) { |
| 360 var token = node.operator; | 380 var token = node.operator; |
| 361 if (token.type != TokenType.EQ) { | 381 if (token.type != TokenType.EQ) { |
| 362 _checkCompoundAssignment(node); | 382 _checkCompoundAssignment(node); |
| 363 } else { | 383 } else { |
| 364 DartType staticType = _rules.getStaticType(node.leftHandSide); | 384 DartType staticType = _rules.getStaticType(node.leftHandSide); |
| 365 node.rightHandSide = checkAssignment(node.rightHandSide, staticType); | 385 node.rightHandSide = checkAssignment(node.rightHandSide, staticType); |
| 366 } | 386 } |
| 367 node.visitChildren(this); | 387 node.visitChildren(this); |
| 368 } | 388 } |
| 369 | 389 |
| 370 /// Check constructor declaration to ensure correct super call placement. | 390 /// Check constructor declaration to ensure correct super call placement. |
| 371 @override | 391 @override |
| 372 visitConstructorDeclaration(ConstructorDeclaration node) { | 392 visitConstructorDeclaration(ConstructorDeclaration node) { |
| 373 node.visitChildren(this); | 393 _visitMaybeConst(node, (node) { |
| 394 node.visitChildren(this); |
| 374 | 395 |
| 375 final init = node.initializers; | 396 final init = node.initializers; |
| 376 for (int i = 0, last = init.length - 1; i < last; i++) { | 397 for (int i = 0, last = init.length - 1; i < last; i++) { |
| 377 final node = init[i]; | 398 final node = init[i]; |
| 378 if (node is SuperConstructorInvocation) { | 399 if (node is SuperConstructorInvocation) { |
| 379 _recordMessage(new InvalidSuperInvocation(node)); | 400 _recordMessage(new InvalidSuperInvocation(node)); |
| 401 } |
| 380 } | 402 } |
| 381 } | 403 }); |
| 382 } | 404 } |
| 383 | 405 |
| 384 @override | 406 @override |
| 385 visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | 407 visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 386 var field = node.fieldName; | 408 var field = node.fieldName; |
| 387 DartType staticType = _rules.elementType(field.staticElement); | 409 DartType staticType = _rules.elementType(field.staticElement); |
| 388 node.expression = checkAssignment(node.expression, staticType); | 410 node.expression = checkAssignment(node.expression, staticType); |
| 389 node.visitChildren(this); | 411 node.visitChildren(this); |
| 390 } | 412 } |
| 391 | 413 |
| 414 @override visitListLiteral(ListLiteral node) { |
| 415 var type = _rules.provider.dynamicType; |
| 416 if (node.typeArguments != null) { |
| 417 var targs = node.typeArguments.arguments; |
| 418 if (targs.length > 0) type = targs[0].type; |
| 419 } |
| 420 var elements = node.elements; |
| 421 for (int i = 0; i < elements.length; i++) { |
| 422 elements[i] = checkArgument(elements[i], type); |
| 423 } |
| 424 super.visitListLiteral(node); |
| 425 } |
| 426 |
| 427 @override visitMapLiteral(MapLiteral node) { |
| 428 var ktype = _rules.provider.dynamicType; |
| 429 var vtype = _rules.provider.dynamicType; |
| 430 if (node.typeArguments != null) { |
| 431 var targs = node.typeArguments.arguments; |
| 432 if (targs.length > 0) ktype = targs[0].type; |
| 433 if (targs.length > 1) vtype = targs[1].type; |
| 434 } |
| 435 var entries = node.entries; |
| 436 for (int i = 0; i < entries.length; i++) { |
| 437 var entry = entries[i]; |
| 438 entry.key = checkArgument(entry.key, ktype); |
| 439 entry.value = checkArgument(entry.value, vtype); |
| 440 } |
| 441 super.visitMapLiteral(node); |
| 442 } |
| 443 |
| 392 // Check invocations | 444 // Check invocations |
| 393 bool checkArgumentList(ArgumentList node, FunctionType type) { | 445 bool checkArgumentList(ArgumentList node, FunctionType type) { |
| 394 NodeList<Expression> list = node.arguments; | 446 NodeList<Expression> list = node.arguments; |
| 395 int len = list.length; | 447 int len = list.length; |
| 396 for (int i = 0; i < len; ++i) { | 448 for (int i = 0; i < len; ++i) { |
| 397 Expression arg = list[i]; | 449 Expression arg = list[i]; |
| 398 ParameterElement element = node.getStaticParameterElementFor(arg); | 450 ParameterElement element = node.getStaticParameterElementFor(arg); |
| 399 if (element == null) { | 451 if (element == null) { |
| 400 if (type.parameters.length < len) { | 452 if (type.parameters.length < len) { |
| 401 // We found an argument mismatch, the analyzer will report this too, | 453 // We found an argument mismatch, the analyzer will report this too, |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 509 visitPrefixedIdentifier(PrefixedIdentifier node) { | 561 visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 510 final target = node.prefix; | 562 final target = node.prefix; |
| 511 // Check if the prefix is a library - PrefixElement denotes a library | 563 // Check if the prefix is a library - PrefixElement denotes a library |
| 512 // access. | 564 // access. |
| 513 if (target.staticElement is! PrefixElement && _rules.isDynamicGet(target)) { | 565 if (target.staticElement is! PrefixElement && _rules.isDynamicGet(target)) { |
| 514 _recordDynamicInvoke(node); | 566 _recordDynamicInvoke(node); |
| 515 } | 567 } |
| 516 node.visitChildren(this); | 568 node.visitChildren(this); |
| 517 } | 569 } |
| 518 | 570 |
| 519 visitDefaultFormalParameter(DefaultFormalParameter node) { | 571 @override visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 520 // Check that defaults have the proper subtype. | 572 _visitMaybeConst(node, (node) { |
| 521 var parameter = node.parameter; | 573 // Check that defaults have the proper subtype. |
| 522 var parameterType = _rules.elementType(parameter.element); | 574 var parameter = node.parameter; |
| 523 assert(parameterType != null); | 575 var parameterType = _rules.elementType(parameter.element); |
| 524 var defaultValue = node.defaultValue; | 576 assert(parameterType != null); |
| 525 var defaultType; | 577 var defaultValue = node.defaultValue; |
| 526 if (defaultValue == null) { | 578 var defaultType; |
| 527 // TODO(vsm): Should this be null? | 579 if (defaultValue == null) { |
| 528 defaultType = _rules.provider.bottomType; | 580 // TODO(vsm): Should this be null? |
| 529 } else { | 581 defaultType = _rules.provider.bottomType; |
| 530 defaultType = _rules.getStaticType(defaultValue); | 582 } else { |
| 531 } | 583 defaultType = _rules.getStaticType(defaultValue); |
| 584 } |
| 532 | 585 |
| 533 // If defaultType is bottom, this enforces that parameterType is not | 586 // If defaultType is bottom, this enforces that parameterType is not |
| 534 // non-nullable. | 587 // non-nullable. |
| 535 if (!_rules.isSubTypeOf(defaultType, parameterType)) { | 588 if (!_rules.isSubTypeOf(defaultType, parameterType)) { |
| 536 var staticInfo = (defaultValue == null) | 589 var staticInfo = (defaultValue == null) |
| 537 ? new InvalidVariableDeclaration( | 590 ? new InvalidVariableDeclaration( |
| 538 _rules, node.identifier, parameterType) | 591 _rules, node.identifier, parameterType) |
| 539 : new StaticTypeError(_rules, defaultValue, parameterType); | 592 : new StaticTypeError(_rules, defaultValue, parameterType); |
| 540 _recordMessage(staticInfo); | 593 _recordMessage(staticInfo); |
| 541 } | 594 } |
| 542 node.visitChildren(this); | 595 node.visitChildren(this); |
| 596 }); |
| 543 } | 597 } |
| 544 | 598 |
| 545 visitFieldFormalParameter(FieldFormalParameter node) { | 599 visitFieldFormalParameter(FieldFormalParameter node) { |
| 546 var element = node.element; | 600 var element = node.element; |
| 547 var typeName = node.type; | 601 var typeName = node.type; |
| 548 if (typeName != null) { | 602 if (typeName != null) { |
| 549 var type = _rules.elementType(element); | 603 var type = _rules.elementType(element); |
| 550 var fieldElement = | 604 var fieldElement = |
| 551 node.identifier.staticElement as FieldFormalParameterElement; | 605 node.identifier.staticElement as FieldFormalParameterElement; |
| 552 var fieldType = _rules.elementType(fieldElement.field); | 606 var fieldType = _rules.elementType(fieldElement.field); |
| 553 if (!_rules.isSubTypeOf(type, fieldType)) { | 607 if (!_rules.isSubTypeOf(type, fieldType)) { |
| 554 var staticInfo = | 608 var staticInfo = |
| 555 new InvalidParameterDeclaration(_rules, node, fieldType); | 609 new InvalidParameterDeclaration(_rules, node, fieldType); |
| 556 _recordMessage(staticInfo); | 610 _recordMessage(staticInfo); |
| 557 } | 611 } |
| 558 } | 612 } |
| 559 node.visitChildren(this); | 613 node.visitChildren(this); |
| 560 } | 614 } |
| 561 | 615 |
| 562 @override | 616 @override |
| 563 visitInstanceCreationExpression(InstanceCreationExpression node) { | 617 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 564 var arguments = node.argumentList; | 618 _visitMaybeConst(node, (node) { |
| 565 var element = node.staticElement; | 619 var arguments = node.argumentList; |
| 566 if (element != null) { | 620 var element = node.staticElement; |
| 567 var type = _rules.elementType(node.staticElement); | 621 if (element != null) { |
| 568 checkArgumentList(arguments, type); | 622 var type = _rules.elementType(node.staticElement); |
| 569 } else { | 623 checkArgumentList(arguments, type); |
| 570 _recordMessage(new MissingTypeError(node)); | 624 } else { |
| 571 } | 625 _recordMessage(new MissingTypeError(node)); |
| 572 node.visitChildren(this); | 626 } |
| 627 node.visitChildren(this); |
| 628 }); |
| 573 } | 629 } |
| 574 | 630 |
| 575 @override | 631 @override |
| 576 visitVariableDeclarationList(VariableDeclarationList node) { | 632 visitVariableDeclarationList(VariableDeclarationList node) { |
| 577 TypeName type = node.type; | 633 _visitMaybeConst(node, (node) { |
| 578 if (type == null) { | 634 TypeName type = node.type; |
| 579 // No checks are needed when the type is var. Although internally the | 635 if (type == null) { |
| 580 // typing rules may have inferred a more precise type for the variable | 636 // No checks are needed when the type is var. Although internally the |
| 581 // based on the initializer. | 637 // typing rules may have inferred a more precise type for the variable |
| 582 } else { | 638 // based on the initializer. |
| 583 var dartType = getType(type); | 639 } else { |
| 584 for (VariableDeclaration variable in node.variables) { | 640 var dartType = getType(type); |
| 585 var initializer = variable.initializer; | 641 for (VariableDeclaration variable in node.variables) { |
| 586 if (initializer != null) { | 642 var initializer = variable.initializer; |
| 587 variable.initializer = checkAssignment(initializer, dartType); | 643 if (initializer != null) { |
| 588 } else if (_rules.maybeNonNullableType(dartType)) { | 644 variable.initializer = checkAssignment(initializer, dartType); |
| 589 var element = variable.element; | 645 } else if (_rules.maybeNonNullableType(dartType)) { |
| 590 if (element is FieldElement && !element.isStatic) { | 646 var element = variable.element; |
| 591 // Initialized - possibly implicitly - during construction. | 647 if (element is FieldElement && !element.isStatic) { |
| 592 // Handle this via a runtime check during code generation. | 648 // Initialized - possibly implicitly - during construction. |
| 649 // Handle this via a runtime check during code generation. |
| 593 | 650 |
| 594 // TODO(vsm): Detect statically whether this can fail and | 651 // TODO(vsm): Detect statically whether this can fail and |
| 595 // report a static error (must fail) or warning (can fail). | 652 // report a static error (must fail) or warning (can fail). |
| 596 } else { | 653 } else { |
| 597 var staticInfo = | 654 var staticInfo = |
| 598 new InvalidVariableDeclaration(_rules, variable, dartType); | 655 new InvalidVariableDeclaration(_rules, variable, dartType); |
| 599 _recordMessage(staticInfo); | 656 _recordMessage(staticInfo); |
| 657 } |
| 600 } | 658 } |
| 601 } | 659 } |
| 602 } | 660 } |
| 603 } | 661 node.visitChildren(this); |
| 604 node.visitChildren(this); | 662 }); |
| 663 } |
| 664 |
| 665 @override |
| 666 visitVariableDeclaration(VariableDeclaration node) { |
| 667 _visitMaybeConst(node, super.visitVariableDeclaration); |
| 605 } | 668 } |
| 606 | 669 |
| 607 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { | 670 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { |
| 608 var type = getType(typeName); | 671 var type = getType(typeName); |
| 609 if (!_rules.isGroundType(type)) { | 672 if (!_rules.isGroundType(type)) { |
| 610 _recordMessage(new InvalidRuntimeCheckError(node, type)); | 673 _recordMessage(new InvalidRuntimeCheckError(node, type)); |
| 611 } | 674 } |
| 612 } | 675 } |
| 613 | 676 |
| 614 visitAsExpression(AsExpression node) { | 677 visitAsExpression(AsExpression node) { |
| 615 node.visitChildren(this); | 678 node.visitChildren(this); |
| 616 } | 679 } |
| 617 | 680 |
| 618 visitIsExpression(IsExpression node) { | 681 visitIsExpression(IsExpression node) { |
| 619 _checkRuntimeTypeCheck(node, node.type); | 682 _checkRuntimeTypeCheck(node, node.type); |
| 620 node.visitChildren(this); | 683 node.visitChildren(this); |
| 621 } | 684 } |
| 622 | 685 |
| 623 DartType getType(TypeName name) { | 686 DartType getType(TypeName name) { |
| 624 return (name == null) ? _rules.provider.dynamicType : name.type; | 687 return (name == null) ? _rules.provider.dynamicType : name.type; |
| 625 } | 688 } |
| 626 | 689 |
| 627 Expression checkAssignment(Expression expr, DartType type) { | 690 Expression checkAssignment(Expression expr, DartType type) { |
| 628 final staticInfo = _rules.checkAssignment(expr, type); | 691 final staticInfo = _rules.checkAssignment(expr, type, _constantContext); |
| 629 _recordMessage(staticInfo); | 692 _recordMessage(staticInfo); |
| 630 if (staticInfo is Conversion) expr = staticInfo; | 693 if (staticInfo is Conversion) expr = staticInfo; |
| 631 return expr; | 694 return expr; |
| 632 } | 695 } |
| 633 | 696 |
| 634 DartType _specializedBinaryReturnType( | 697 DartType _specializedBinaryReturnType( |
| 635 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { | 698 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { |
| 636 // This special cases binary return types as per 16.26 and 16.27 of the | 699 // This special cases binary return types as per 16.26 and 16.27 of the |
| 637 // Dart language spec. | 700 // Dart language spec. |
| 638 switch (op) { | 701 switch (op) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 692 } else { | 755 } else { |
| 693 // Static type error | 756 // Static type error |
| 694 staticInfo = new StaticTypeError(_rules, expr, lhsType); | 757 staticInfo = new StaticTypeError(_rules, expr, lhsType); |
| 695 } | 758 } |
| 696 _recordMessage(staticInfo); | 759 _recordMessage(staticInfo); |
| 697 } | 760 } |
| 698 | 761 |
| 699 // Check the rhs type | 762 // Check the rhs type |
| 700 if (staticInfo is! Conversion) { | 763 if (staticInfo is! Conversion) { |
| 701 var paramType = paramTypes.first; | 764 var paramType = paramTypes.first; |
| 702 staticInfo = _rules.checkAssignment(expr.rightHandSide, paramType); | 765 staticInfo = _rules.checkAssignment( |
| 766 expr.rightHandSide, paramType, _constantContext); |
| 703 _recordMessage(staticInfo); | 767 _recordMessage(staticInfo); |
| 704 if (staticInfo is Conversion) expr.rightHandSide = staticInfo; | 768 if (staticInfo is Conversion) expr.rightHandSide = staticInfo; |
| 705 } | 769 } |
| 706 } | 770 } |
| 707 } | 771 } |
| 708 | 772 |
| 709 void _recordDynamicInvoke(AstNode node) { | 773 void _recordDynamicInvoke(AstNode node) { |
| 710 _reporter.log(new DynamicInvoke(_rules, node)); | 774 _reporter.log(new DynamicInvoke(_rules, node)); |
| 711 } | 775 } |
| 712 | 776 |
| 713 void _recordMessage(StaticInfo info) { | 777 void _recordMessage(StaticInfo info) { |
| 714 if (info == null) return; | 778 if (info == null) return; |
| 715 if (info.level >= logger.Level.SEVERE) _failure = true; | 779 if (info.level >= logger.Level.SEVERE) _failure = true; |
| 716 _reporter.log(info); | 780 _reporter.log(info); |
| 717 } | 781 } |
| 718 } | 782 } |
| OLD | NEW |