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 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)) { | |
|
vsm
2015/02/27 00:20:16
Would be nice if we had generic methods. :-)
Leaf
2015/02/27 00:44:21
Acknowledged.
| |
| 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 _visitConstructorDeclaration(ConstructorDeclaration node) { |
| 371 @override | |
| 372 visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 373 node.visitChildren(this); | 391 node.visitChildren(this); |
| 374 | 392 |
| 375 final init = node.initializers; | 393 final init = node.initializers; |
| 376 for (int i = 0, last = init.length - 1; i < last; i++) { | 394 for (int i = 0, last = init.length - 1; i < last; i++) { |
| 377 final node = init[i]; | 395 final node = init[i]; |
| 378 if (node is SuperConstructorInvocation) { | 396 if (node is SuperConstructorInvocation) { |
| 379 _recordMessage(new InvalidSuperInvocation(node)); | 397 _recordMessage(new InvalidSuperInvocation(node)); |
| 380 } | 398 } |
| 381 } | 399 } |
| 382 } | 400 } |
| 383 | 401 |
| 402 /// Check constructor declaration to ensure correct super call placement. | |
| 403 @override | |
| 404 visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 405 _visitMaybeConst(node, _visitConstructorDeclaration); | |
|
vsm
2015/02/27 00:20:16
Perhaps just inline the closure here instead of a
Leaf
2015/02/27 00:44:21
Done.
| |
| 406 } | |
| 407 | |
| 408 @override | |
| 409 visitInstanceCreationExpression(InstanceCreationExpression node) { | |
| 410 _visitMaybeConst(node, super.visitInstanceCreationExpression); | |
| 411 } | |
| 412 | |
| 413 @override visitListLiteral(ListLiteral node) { | |
| 414 var type = _rules.provider.dynamicType; | |
| 415 if (node.typeArguments != null) { | |
| 416 var targs = node.typeArguments.arguments; | |
| 417 if (targs.length > 0) type = targs[0].type; | |
| 418 } | |
| 419 var elements = node.elements; | |
| 420 for (int i = 0; i < elements.length; i++) { | |
| 421 elements[i] = checkArgument(elements[i], type); | |
| 422 } | |
| 423 super.visitListLiteral(node); | |
| 424 } | |
| 425 | |
| 426 @override visitMapLiteral(MapLiteral node) { | |
| 427 var ktype = _rules.provider.dynamicType; | |
| 428 var vtype = _rules.provider.dynamicType; | |
| 429 if (node.typeArguments != null) { | |
| 430 var targs = node.typeArguments.arguments; | |
| 431 if (targs.length > 0) ktype = targs[0].type; | |
| 432 if (targs.length > 1) vtype = targs[1].type; | |
| 433 } | |
| 434 var entries = node.entries; | |
| 435 for (int i = 0; i < entries.length; i++) { | |
| 436 var entry = entries[i]; | |
| 437 entry.key = checkArgument(entry.key, ktype); | |
| 438 entry.value = checkArgument(entry.value, vtype); | |
| 439 } | |
| 440 super.visitMapLiteral(node); | |
| 441 } | |
| 442 | |
| 384 // Check invocations | 443 // Check invocations |
| 385 bool checkArgumentList(ArgumentList node, FunctionType type) { | 444 bool checkArgumentList(ArgumentList node, FunctionType type) { |
| 386 NodeList<Expression> list = node.arguments; | 445 NodeList<Expression> list = node.arguments; |
| 387 int len = list.length; | 446 int len = list.length; |
| 388 for (int i = 0; i < len; ++i) { | 447 for (int i = 0; i < len; ++i) { |
| 389 Expression arg = list[i]; | 448 Expression arg = list[i]; |
| 390 ParameterElement element = node.getStaticParameterElementFor(arg); | 449 ParameterElement element = node.getStaticParameterElementFor(arg); |
| 391 if (element == null) { | 450 if (element == null) { |
| 392 if (type.parameters.length < len) { | 451 if (type.parameters.length < len) { |
| 393 // We found an argument mismatch, the analyzer will report this too, | 452 // We found an argument mismatch, the analyzer will report this too, |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 501 visitPrefixedIdentifier(PrefixedIdentifier node) { | 560 visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 502 final target = node.prefix; | 561 final target = node.prefix; |
| 503 // Check if the prefix is a library - PrefixElement denotes a library | 562 // Check if the prefix is a library - PrefixElement denotes a library |
| 504 // access. | 563 // access. |
| 505 if (target.staticElement is! PrefixElement && _rules.isDynamicGet(target)) { | 564 if (target.staticElement is! PrefixElement && _rules.isDynamicGet(target)) { |
| 506 _recordDynamicInvoke(node); | 565 _recordDynamicInvoke(node); |
| 507 } | 566 } |
| 508 node.visitChildren(this); | 567 node.visitChildren(this); |
| 509 } | 568 } |
| 510 | 569 |
| 511 visitDefaultFormalParameter(DefaultFormalParameter node) { | 570 _visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 512 // Check that defaults have the proper subtype. | 571 // Check that defaults have the proper subtype. |
| 513 var parameter = node.parameter; | 572 var parameter = node.parameter; |
| 514 var parameterType = _rules.elementType(parameter.element); | 573 var parameterType = _rules.elementType(parameter.element); |
| 515 assert(parameterType != null); | 574 assert(parameterType != null); |
| 516 var defaultValue = node.defaultValue; | 575 var defaultValue = node.defaultValue; |
| 517 var defaultType; | 576 var defaultType; |
| 518 if (defaultValue == null) { | 577 if (defaultValue == null) { |
| 519 // TODO(vsm): Should this be null? | 578 // TODO(vsm): Should this be null? |
| 520 defaultType = _rules.provider.bottomType; | 579 defaultType = _rules.provider.bottomType; |
| 521 } else { | 580 } else { |
| 522 defaultType = _rules.getStaticType(defaultValue); | 581 defaultType = _rules.getStaticType(defaultValue); |
| 523 } | 582 } |
| 524 | 583 |
| 525 // If defaultType is bottom, this enforces that parameterType is not | 584 // If defaultType is bottom, this enforces that parameterType is not |
| 526 // non-nullable. | 585 // non-nullable. |
| 527 if (!_rules.isSubTypeOf(defaultType, parameterType)) { | 586 if (!_rules.isSubTypeOf(defaultType, parameterType)) { |
| 528 var staticInfo = (defaultValue == null) | 587 var staticInfo = (defaultValue == null) |
| 529 ? new InvalidVariableDeclaration( | 588 ? new InvalidVariableDeclaration( |
| 530 _rules, node.identifier, parameterType) | 589 _rules, node.identifier, parameterType) |
| 531 : new StaticTypeError(_rules, defaultValue, parameterType); | 590 : new StaticTypeError(_rules, defaultValue, parameterType); |
| 532 _recordMessage(staticInfo); | 591 _recordMessage(staticInfo); |
| 533 } | 592 } |
| 534 node.visitChildren(this); | 593 node.visitChildren(this); |
| 535 } | 594 } |
| 536 | 595 |
| 537 visitVariableDeclarationList(VariableDeclarationList node) { | 596 @override visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 597 _visitMaybeConst(node, _visitDefaultFormalParameter); | |
| 598 } | |
| 599 | |
| 600 _visitVariableDeclarationList(VariableDeclarationList node) { | |
| 538 TypeName type = node.type; | 601 TypeName type = node.type; |
| 539 if (type == null) { | 602 if (type == null) { |
| 540 // No checks are needed when the type is var. Although internally the | 603 // No checks are needed when the type is var. Although internally the |
| 541 // typing rules may have inferred a more precise type for the variable | 604 // typing rules may have inferred a more precise type for the variable |
| 542 // based on the initializer. | 605 // based on the initializer. |
| 543 } else { | 606 } else { |
| 544 var dartType = getType(type); | 607 var dartType = getType(type); |
| 545 for (VariableDeclaration variable in node.variables) { | 608 for (VariableDeclaration variable in node.variables) { |
| 546 var initializer = variable.initializer; | 609 var initializer = variable.initializer; |
| 547 if (initializer != null) { | 610 if (initializer != null) { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 558 var staticInfo = | 621 var staticInfo = |
| 559 new InvalidVariableDeclaration(_rules, variable, dartType); | 622 new InvalidVariableDeclaration(_rules, variable, dartType); |
| 560 _recordMessage(staticInfo); | 623 _recordMessage(staticInfo); |
| 561 } | 624 } |
| 562 } | 625 } |
| 563 } | 626 } |
| 564 } | 627 } |
| 565 node.visitChildren(this); | 628 node.visitChildren(this); |
| 566 } | 629 } |
| 567 | 630 |
| 631 @override | |
| 632 visitVariableDeclarationList(VariableDeclarationList node) { | |
| 633 _visitMaybeConst(node, _visitVariableDeclarationList); | |
| 634 } | |
| 635 | |
| 636 @override | |
| 637 visitVariableDeclaration(VariableDeclaration node) { | |
| 638 _visitMaybeConst(node, super.visitVariableDeclaration); | |
| 639 } | |
| 640 | |
| 568 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { | 641 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { |
| 569 var type = getType(typeName); | 642 var type = getType(typeName); |
| 570 if (!_rules.isGroundType(type)) { | 643 if (!_rules.isGroundType(type)) { |
| 571 _recordMessage(new InvalidRuntimeCheckError(node, type)); | 644 _recordMessage(new InvalidRuntimeCheckError(node, type)); |
| 572 } | 645 } |
| 573 } | 646 } |
| 574 | 647 |
| 575 visitAsExpression(AsExpression node) { | 648 visitAsExpression(AsExpression node) { |
| 576 node.visitChildren(this); | 649 node.visitChildren(this); |
| 577 } | 650 } |
| 578 | 651 |
| 579 visitIsExpression(IsExpression node) { | 652 visitIsExpression(IsExpression node) { |
| 580 _checkRuntimeTypeCheck(node, node.type); | 653 _checkRuntimeTypeCheck(node, node.type); |
| 581 node.visitChildren(this); | 654 node.visitChildren(this); |
| 582 } | 655 } |
| 583 | 656 |
| 584 DartType getType(TypeName name) { | 657 DartType getType(TypeName name) { |
| 585 return (name == null) ? _rules.provider.dynamicType : name.type; | 658 return (name == null) ? _rules.provider.dynamicType : name.type; |
| 586 } | 659 } |
| 587 | 660 |
| 588 Expression checkAssignment(Expression expr, DartType type) { | 661 Expression checkAssignment(Expression expr, DartType type) { |
| 589 final staticInfo = _rules.checkAssignment(expr, type); | 662 final staticInfo = _rules.checkAssignment(expr, type, _constantContext); |
| 590 _recordMessage(staticInfo); | 663 _recordMessage(staticInfo); |
| 591 if (staticInfo is Conversion) expr = staticInfo; | 664 if (staticInfo is Conversion) expr = staticInfo; |
| 592 return expr; | 665 return expr; |
| 593 } | 666 } |
| 594 | 667 |
| 595 DartType _specializedBinaryReturnType( | 668 DartType _specializedBinaryReturnType( |
| 596 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { | 669 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { |
| 597 // This special cases binary return types as per 16.26 and 16.27 of the | 670 // This special cases binary return types as per 16.26 and 16.27 of the |
| 598 // Dart language spec. | 671 // Dart language spec. |
| 599 switch (op) { | 672 switch (op) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 653 } else { | 726 } else { |
| 654 // Static type error | 727 // Static type error |
| 655 staticInfo = new StaticTypeError(_rules, expr, lhsType); | 728 staticInfo = new StaticTypeError(_rules, expr, lhsType); |
| 656 } | 729 } |
| 657 _recordMessage(staticInfo); | 730 _recordMessage(staticInfo); |
| 658 } | 731 } |
| 659 | 732 |
| 660 // Check the rhs type | 733 // Check the rhs type |
| 661 if (staticInfo is! Conversion) { | 734 if (staticInfo is! Conversion) { |
| 662 var paramType = paramTypes.first; | 735 var paramType = paramTypes.first; |
| 663 staticInfo = _rules.checkAssignment(expr.rightHandSide, paramType); | 736 staticInfo = _rules.checkAssignment( |
| 737 expr.rightHandSide, paramType, _constantContext); | |
| 664 _recordMessage(staticInfo); | 738 _recordMessage(staticInfo); |
| 665 if (staticInfo is Conversion) expr.rightHandSide = staticInfo; | 739 if (staticInfo is Conversion) expr.rightHandSide = staticInfo; |
| 666 } | 740 } |
| 667 } | 741 } |
| 668 } | 742 } |
| 669 | 743 |
| 670 void _recordDynamicInvoke(AstNode node) { | 744 void _recordDynamicInvoke(AstNode node) { |
| 671 _reporter.log(new DynamicInvoke(_rules, node)); | 745 _reporter.log(new DynamicInvoke(_rules, node)); |
| 672 } | 746 } |
| 673 | 747 |
| 674 void _recordMessage(StaticInfo info) { | 748 void _recordMessage(StaticInfo info) { |
| 675 if (info == null) return; | 749 if (info == null) return; |
| 676 if (info.level >= logger.Level.SEVERE) _failure = true; | 750 if (info.level >= logger.Level.SEVERE) _failure = true; |
| 677 _reporter.log(info); | 751 _reporter.log(info); |
| 678 } | 752 } |
| 679 } | 753 } |
| OLD | NEW |