Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(224)

Side by Side Diff: lib/src/checker/checker.dart

Issue 1235503010: fixes #219, able to compile multiple entry points (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
326 void _recordMessage(StaticInfo info) { 326 void _recordMessage(StaticInfo info) {
327 if (info == null) return; 327 if (info == null) return;
328 var error = info.toAnalysisError(); 328 var error = info.toAnalysisError();
329 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; 329 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
330 _reporter.onError(error); 330 _reporter.onError(error);
331 } 331 }
332 } 332 }
333 333
334 /// Checks the body of functions and properties. 334 /// Checks the body of functions and properties.
335 class CodeChecker extends RecursiveAstVisitor { 335 class CodeChecker extends RecursiveAstVisitor {
336 final TypeRules _rules; 336 final TypeRules rules;
337 final AnalysisErrorListener _reporter; 337 final AnalysisErrorListener reporter;
338 final _OverrideChecker _overrideChecker; 338 final _OverrideChecker _overrideChecker;
339 bool _failure = false; 339 bool _failure = false;
340 bool get failure => _failure || _overrideChecker._failure; 340 bool get failure => _failure || _overrideChecker._failure;
341 341
342 CodeChecker(TypeRules rules, AnalysisErrorListener reporter, 342 CodeChecker(TypeRules rules, AnalysisErrorListener reporter,
343 StrongModeOptions options) 343 StrongModeOptions options)
344 : _rules = rules, 344 : rules = rules,
345 _reporter = reporter, 345 reporter = reporter,
346 _overrideChecker = new _OverrideChecker(rules, reporter, options); 346 _overrideChecker = new _OverrideChecker(rules, reporter, options);
347 347
348 @override 348 @override
349 void visitCompilationUnit(CompilationUnit unit) { 349 void visitCompilationUnit(CompilationUnit unit) {
350 void report(Expression expr) { 350 void report(Expression expr) {
351 _reporter.onError(new MissingTypeError(expr).toAnalysisError()); 351 reporter.onError(new MissingTypeError(expr).toAnalysisError());
352 } 352 }
353 var callback = _rules.reportMissingType; 353 var callback = rules.reportMissingType;
354 _rules.reportMissingType = report; 354 rules.reportMissingType = report;
355 unit.visitChildren(this); 355 unit.visitChildren(this);
356 _rules.reportMissingType = callback; 356 rules.reportMissingType = callback;
357 } 357 }
358 358
359 @override 359 @override
360 void visitComment(Comment node) { 360 void visitComment(Comment node) {
361 // skip, no need to do typechecking inside comments (they may contain 361 // skip, no need to do typechecking inside comments (they may contain
362 // comment references which would require resolution). 362 // comment references which would require resolution).
363 } 363 }
364 364
365 @override 365 @override
366 void visitClassDeclaration(ClassDeclaration node) { 366 void visitClassDeclaration(ClassDeclaration node) {
367 _overrideChecker.check(node); 367 _overrideChecker.check(node);
368 super.visitClassDeclaration(node); 368 super.visitClassDeclaration(node);
369 } 369 }
370 370
371 @override 371 @override
372 void visitAssignmentExpression(AssignmentExpression node) { 372 void visitAssignmentExpression(AssignmentExpression node) {
373 var token = node.operator; 373 var token = node.operator;
374 if (token.type != TokenType.EQ) { 374 if (token.type != TokenType.EQ) {
375 _checkCompoundAssignment(node); 375 _checkCompoundAssignment(node);
376 } else { 376 } else {
377 DartType staticType = _rules.getStaticType(node.leftHandSide); 377 DartType staticType = rules.getStaticType(node.leftHandSide);
378 checkAssignment(node.rightHandSide, staticType); 378 checkAssignment(node.rightHandSide, staticType);
379 } 379 }
380 node.visitChildren(this); 380 node.visitChildren(this);
381 } 381 }
382 382
383 /// Check constructor declaration to ensure correct super call placement. 383 /// Check constructor declaration to ensure correct super call placement.
384 @override 384 @override
385 void visitConstructorDeclaration(ConstructorDeclaration node) { 385 void visitConstructorDeclaration(ConstructorDeclaration node) {
386 node.visitChildren(this); 386 node.visitChildren(this);
387 387
388 final init = node.initializers; 388 final init = node.initializers;
389 for (int i = 0, last = init.length - 1; i < last; i++) { 389 for (int i = 0, last = init.length - 1; i < last; i++) {
390 final node = init[i]; 390 final node = init[i];
391 if (node is SuperConstructorInvocation) { 391 if (node is SuperConstructorInvocation) {
392 _recordMessage(new InvalidSuperInvocation(node)); 392 _recordMessage(new InvalidSuperInvocation(node));
393 } 393 }
394 } 394 }
395 } 395 }
396 396
397 @override 397 @override
398 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { 398 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
399 var field = node.fieldName; 399 var field = node.fieldName;
400 DartType staticType = _rules.elementType(field.staticElement); 400 DartType staticType = rules.elementType(field.staticElement);
401 checkAssignment(node.expression, staticType); 401 checkAssignment(node.expression, staticType);
402 node.visitChildren(this); 402 node.visitChildren(this);
403 } 403 }
404 404
405 @override 405 @override
406 void visitForEachStatement(ForEachStatement node) { 406 void visitForEachStatement(ForEachStatement node) {
407 // Check that the expression is an Iterable. 407 // Check that the expression is an Iterable.
408 var expr = node.iterable; 408 var expr = node.iterable;
409 var iterableType = _rules.provider.iterableType; 409 var iterableType = rules.provider.iterableType;
410 var loopVariable = node.identifier != null 410 var loopVariable = node.identifier != null
411 ? node.identifier 411 ? node.identifier
412 : node.loopVariable.identifier; 412 : node.loopVariable.identifier;
413 var iteratorType = loopVariable.staticType; 413 var iteratorType = loopVariable.staticType;
414 var checkedType = iterableType.substitute4([iteratorType]); 414 var checkedType = iterableType.substitute4([iteratorType]);
415 checkAssignment(expr, checkedType); 415 checkAssignment(expr, checkedType);
416 node.visitChildren(this); 416 node.visitChildren(this);
417 } 417 }
418 418
419 @override 419 @override
(...skipping 26 matching lines...) Expand all
446 void visitSwitchStatement(SwitchStatement node) { 446 void visitSwitchStatement(SwitchStatement node) {
447 // SwitchStatement defines a boolean conversion to check the result of the 447 // SwitchStatement defines a boolean conversion to check the result of the
448 // case value == the switch value, but in dev_compiler we require a boolean 448 // case value == the switch value, but in dev_compiler we require a boolean
449 // return type from an overridden == operator (because Object.==), so 449 // return type from an overridden == operator (because Object.==), so
450 // checking in SwitchStatement shouldn't be necessary. 450 // checking in SwitchStatement shouldn't be necessary.
451 node.visitChildren(this); 451 node.visitChildren(this);
452 } 452 }
453 453
454 @override 454 @override
455 void visitListLiteral(ListLiteral node) { 455 void visitListLiteral(ListLiteral node) {
456 var type = _rules.provider.dynamicType; 456 var type = rules.provider.dynamicType;
457 if (node.typeArguments != null) { 457 if (node.typeArguments != null) {
458 var targs = node.typeArguments.arguments; 458 var targs = node.typeArguments.arguments;
459 if (targs.length > 0) type = targs[0].type; 459 if (targs.length > 0) type = targs[0].type;
460 } 460 }
461 var elements = node.elements; 461 var elements = node.elements;
462 for (int i = 0; i < elements.length; i++) { 462 for (int i = 0; i < elements.length; i++) {
463 checkArgument(elements[i], type); 463 checkArgument(elements[i], type);
464 } 464 }
465 super.visitListLiteral(node); 465 super.visitListLiteral(node);
466 } 466 }
467 467
468 @override 468 @override
469 void visitMapLiteral(MapLiteral node) { 469 void visitMapLiteral(MapLiteral node) {
470 var ktype = _rules.provider.dynamicType; 470 var ktype = rules.provider.dynamicType;
471 var vtype = _rules.provider.dynamicType; 471 var vtype = rules.provider.dynamicType;
472 if (node.typeArguments != null) { 472 if (node.typeArguments != null) {
473 var targs = node.typeArguments.arguments; 473 var targs = node.typeArguments.arguments;
474 if (targs.length > 0) ktype = targs[0].type; 474 if (targs.length > 0) ktype = targs[0].type;
475 if (targs.length > 1) vtype = targs[1].type; 475 if (targs.length > 1) vtype = targs[1].type;
476 } 476 }
477 var entries = node.entries; 477 var entries = node.entries;
478 for (int i = 0; i < entries.length; i++) { 478 for (int i = 0; i < entries.length; i++) {
479 var entry = entries[i]; 479 var entry = entries[i];
480 checkArgument(entry.key, ktype); 480 checkArgument(entry.key, ktype);
481 checkArgument(entry.value, vtype); 481 checkArgument(entry.value, vtype);
(...skipping 11 matching lines...) Expand all
493 if (element == null) { 493 if (element == null) {
494 if (type.parameters.length < len) { 494 if (type.parameters.length < len) {
495 // We found an argument mismatch, the analyzer will report this too, 495 // We found an argument mismatch, the analyzer will report this too,
496 // so no need to insert an error for this here. 496 // so no need to insert an error for this here.
497 continue; 497 continue;
498 } 498 }
499 element = type.parameters[i]; 499 element = type.parameters[i];
500 // TODO(vsm): When can this happen? 500 // TODO(vsm): When can this happen?
501 assert(element != null); 501 assert(element != null);
502 } 502 }
503 DartType expectedType = _rules.elementType(element); 503 DartType expectedType = rules.elementType(element);
504 if (expectedType == null) expectedType = _rules.provider.dynamicType; 504 if (expectedType == null) expectedType = rules.provider.dynamicType;
505 checkArgument(arg, expectedType); 505 checkArgument(arg, expectedType);
506 } 506 }
507 } 507 }
508 508
509 void checkArgument(Expression arg, DartType expectedType) { 509 void checkArgument(Expression arg, DartType expectedType) {
510 // Preserve named argument structure, so their immediate parent is the 510 // Preserve named argument structure, so their immediate parent is the
511 // method invocation. 511 // method invocation.
512 if (arg is NamedExpression) { 512 if (arg is NamedExpression) {
513 arg = (arg as NamedExpression).expression; 513 arg = (arg as NamedExpression).expression;
514 } 514 }
515 checkAssignment(arg, expectedType); 515 checkAssignment(arg, expectedType);
516 } 516 }
517 517
518 void checkFunctionApplication( 518 void checkFunctionApplication(
519 Expression node, Expression f, ArgumentList list) { 519 Expression node, Expression f, ArgumentList list) {
520 if (_rules.isDynamicCall(f)) { 520 if (rules.isDynamicCall(f)) {
521 // If f is Function and this is a method invocation, we should have 521 // If f is Function and this is a method invocation, we should have
522 // gotten an analyzer error, so no need to issue another error. 522 // gotten an analyzer error, so no need to issue another error.
523 _recordDynamicInvoke(node, f); 523 _recordDynamicInvoke(node, f);
524 } else { 524 } else {
525 checkArgumentList(list, _rules.getTypeAsCaller(f)); 525 checkArgumentList(list, rules.getTypeAsCaller(f));
526 } 526 }
527 } 527 }
528 528
529 @override 529 @override
530 visitMethodInvocation(MethodInvocation node) { 530 visitMethodInvocation(MethodInvocation node) {
531 var target = node.realTarget; 531 var target = node.realTarget;
532 if (_rules.isDynamicTarget(target)) { 532 if (rules.isDynamicTarget(target)) {
533 _recordDynamicInvoke(node, target); 533 _recordDynamicInvoke(node, target);
534 534
535 // Mark the tear-off as being dynamic, too. This lets us distinguish 535 // Mark the tear-off as being dynamic, too. This lets us distinguish
536 // cases like: 536 // cases like:
537 // 537 //
538 // dynamic d; 538 // dynamic d;
539 // d.someMethod(...); // the whole method call must be a dynamic send. 539 // d.someMethod(...); // the whole method call must be a dynamic send.
540 // 540 //
541 // ... from case like: 541 // ... from case like:
542 // 542 //
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 var type = node.staticElement.type; 575 var type = node.staticElement.type;
576 checkArgumentList(node.argumentList, type); 576 checkArgumentList(node.argumentList, type);
577 } 577 }
578 node.visitChildren(this); 578 node.visitChildren(this);
579 } 579 }
580 580
581 DartType _getExpectedReturnType(FunctionBody body, bool yieldStar) { 581 DartType _getExpectedReturnType(FunctionBody body, bool yieldStar) {
582 FunctionType functionType; 582 FunctionType functionType;
583 var parent = body.parent; 583 var parent = body.parent;
584 if (parent is Declaration) { 584 if (parent is Declaration) {
585 functionType = _rules.elementType(parent.element); 585 functionType = rules.elementType(parent.element);
586 } else { 586 } else {
587 assert(parent is FunctionExpression); 587 assert(parent is FunctionExpression);
588 functionType = _rules.getStaticType(parent); 588 functionType = rules.getStaticType(parent);
589 } 589 }
590 590
591 var type = functionType.returnType; 591 var type = functionType.returnType;
592 var provider = _rules.provider; 592 var provider = rules.provider;
593 593
594 InterfaceType expectedType = null; 594 InterfaceType expectedType = null;
595 if (body.isAsynchronous) { 595 if (body.isAsynchronous) {
596 if (body.isGenerator) { 596 if (body.isGenerator) {
597 // Stream<T> -> T 597 // Stream<T> -> T
598 expectedType = provider.streamType; 598 expectedType = provider.streamType;
599 } else { 599 } else {
600 // Future<T> -> T 600 // Future<T> -> T
601 // TODO(vsm): Revisit with issue #228. 601 // TODO(vsm): Revisit with issue #228.
602 expectedType = provider.futureType; 602 expectedType = provider.futureType;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
664 } 664 }
665 665
666 @override 666 @override
667 void visitYieldStatement(YieldStatement node) { 667 void visitYieldStatement(YieldStatement node) {
668 _checkReturnOrYield(node.expression, node, node.star != null); 668 _checkReturnOrYield(node.expression, node, node.star != null);
669 node.visitChildren(this); 669 node.visitChildren(this);
670 } 670 }
671 671
672 @override 672 @override
673 void visitAwaitExpression(AwaitExpression node) { 673 void visitAwaitExpression(AwaitExpression node) {
674 checkAssignment(node.expression, _rules.provider.futureDynamicType); 674 checkAssignment(node.expression, rules.provider.futureDynamicType);
675 node.visitChildren(this); 675 node.visitChildren(this);
676 } 676 }
677 677
678 @override 678 @override
679 void visitPropertyAccess(PropertyAccess node) { 679 void visitPropertyAccess(PropertyAccess node) {
680 var target = node.realTarget; 680 var target = node.realTarget;
681 if (_rules.isDynamicTarget(target)) { 681 if (rules.isDynamicTarget(target)) {
682 _recordDynamicInvoke(node, target); 682 _recordDynamicInvoke(node, target);
683 } 683 }
684 node.visitChildren(this); 684 node.visitChildren(this);
685 } 685 }
686 686
687 @override 687 @override
688 void visitPrefixedIdentifier(PrefixedIdentifier node) { 688 void visitPrefixedIdentifier(PrefixedIdentifier node) {
689 final target = node.prefix; 689 final target = node.prefix;
690 if (_rules.isDynamicTarget(target)) { 690 if (rules.isDynamicTarget(target)) {
691 _recordDynamicInvoke(node, target); 691 _recordDynamicInvoke(node, target);
692 } 692 }
693 node.visitChildren(this); 693 node.visitChildren(this);
694 } 694 }
695 695
696 @override 696 @override
697 void visitDefaultFormalParameter(DefaultFormalParameter node) { 697 void visitDefaultFormalParameter(DefaultFormalParameter node) {
698 // Check that defaults have the proper subtype. 698 // Check that defaults have the proper subtype.
699 var parameter = node.parameter; 699 var parameter = node.parameter;
700 var parameterType = _rules.elementType(parameter.element); 700 var parameterType = rules.elementType(parameter.element);
701 assert(parameterType != null); 701 assert(parameterType != null);
702 var defaultValue = node.defaultValue; 702 var defaultValue = node.defaultValue;
703 if (defaultValue == null) { 703 if (defaultValue == null) {
704 if (_rules.maybeNonNullableType(parameterType)) { 704 if (rules.maybeNonNullableType(parameterType)) {
705 var staticInfo = new InvalidVariableDeclaration( 705 var staticInfo = new InvalidVariableDeclaration(
706 _rules, node.identifier, parameterType); 706 rules, node.identifier, parameterType);
707 _recordMessage(staticInfo); 707 _recordMessage(staticInfo);
708 } 708 }
709 } else { 709 } else {
710 checkAssignment(defaultValue, parameterType); 710 checkAssignment(defaultValue, parameterType);
711 } 711 }
712 712
713 node.visitChildren(this); 713 node.visitChildren(this);
714 } 714 }
715 715
716 @override 716 @override
717 void visitFieldFormalParameter(FieldFormalParameter node) { 717 void visitFieldFormalParameter(FieldFormalParameter node) {
718 var element = node.element; 718 var element = node.element;
719 var typeName = node.type; 719 var typeName = node.type;
720 if (typeName != null) { 720 if (typeName != null) {
721 var type = _rules.elementType(element); 721 var type = rules.elementType(element);
722 var fieldElement = 722 var fieldElement =
723 node.identifier.staticElement as FieldFormalParameterElement; 723 node.identifier.staticElement as FieldFormalParameterElement;
724 var fieldType = _rules.elementType(fieldElement.field); 724 var fieldType = rules.elementType(fieldElement.field);
725 if (!_rules.isSubTypeOf(type, fieldType)) { 725 if (!rules.isSubTypeOf(type, fieldType)) {
726 var staticInfo = 726 var staticInfo =
727 new InvalidParameterDeclaration(_rules, node, fieldType); 727 new InvalidParameterDeclaration(rules, node, fieldType);
728 _recordMessage(staticInfo); 728 _recordMessage(staticInfo);
729 } 729 }
730 } 730 }
731 node.visitChildren(this); 731 node.visitChildren(this);
732 } 732 }
733 733
734 @override 734 @override
735 void visitInstanceCreationExpression(InstanceCreationExpression node) { 735 void visitInstanceCreationExpression(InstanceCreationExpression node) {
736 var arguments = node.argumentList; 736 var arguments = node.argumentList;
737 var element = node.staticElement; 737 var element = node.staticElement;
738 if (element != null) { 738 if (element != null) {
739 var type = _rules.elementType(node.staticElement); 739 var type = rules.elementType(node.staticElement);
740 checkArgumentList(arguments, type); 740 checkArgumentList(arguments, type);
741 } else { 741 } else {
742 _recordMessage(new MissingTypeError(node)); 742 _recordMessage(new MissingTypeError(node));
743 } 743 }
744 node.visitChildren(this); 744 node.visitChildren(this);
745 } 745 }
746 746
747 @override 747 @override
748 void visitVariableDeclarationList(VariableDeclarationList node) { 748 void visitVariableDeclarationList(VariableDeclarationList node) {
749 TypeName type = node.type; 749 TypeName type = node.type;
750 if (type == null) { 750 if (type == null) {
751 // No checks are needed when the type is var. Although internally the 751 // No checks are needed when the type is var. Although internally the
752 // typing rules may have inferred a more precise type for the variable 752 // typing rules may have inferred a more precise type for the variable
753 // based on the initializer. 753 // based on the initializer.
754 } else { 754 } else {
755 var dartType = getType(type); 755 var dartType = getType(type);
756 for (VariableDeclaration variable in node.variables) { 756 for (VariableDeclaration variable in node.variables) {
757 var initializer = variable.initializer; 757 var initializer = variable.initializer;
758 if (initializer != null) { 758 if (initializer != null) {
759 checkAssignment(initializer, dartType); 759 checkAssignment(initializer, dartType);
760 } else if (_rules.maybeNonNullableType(dartType)) { 760 } else if (rules.maybeNonNullableType(dartType)) {
761 var element = variable.element; 761 var element = variable.element;
762 if (element is FieldElement && !element.isStatic) { 762 if (element is FieldElement && !element.isStatic) {
763 // Initialized - possibly implicitly - during construction. 763 // Initialized - possibly implicitly - during construction.
764 // Handle this via a runtime check during code generation. 764 // Handle this via a runtime check during code generation.
765 765
766 // TODO(vsm): Detect statically whether this can fail and 766 // TODO(vsm): Detect statically whether this can fail and
767 // report a static error (must fail) or warning (can fail). 767 // report a static error (must fail) or warning (can fail).
768 } else { 768 } else {
769 var staticInfo = 769 var staticInfo =
770 new InvalidVariableDeclaration(_rules, variable, dartType); 770 new InvalidVariableDeclaration(rules, variable, dartType);
771 _recordMessage(staticInfo); 771 _recordMessage(staticInfo);
772 } 772 }
773 } 773 }
774 } 774 }
775 } 775 }
776 node.visitChildren(this); 776 node.visitChildren(this);
777 } 777 }
778 778
779 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { 779 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) {
780 var type = getType(typeName); 780 var type = getType(typeName);
781 if (!_rules.isGroundType(type)) { 781 if (!rules.isGroundType(type)) {
782 _recordMessage(new InvalidRuntimeCheckError(node, type)); 782 _recordMessage(new InvalidRuntimeCheckError(node, type));
783 } 783 }
784 } 784 }
785 785
786 @override 786 @override
787 void visitAsExpression(AsExpression node) { 787 void visitAsExpression(AsExpression node) {
788 // We could do the same check as the IsExpression below, but that is 788 // We could do the same check as the IsExpression below, but that is
789 // potentially too conservative. Instead, at runtime, we must fail hard 789 // potentially too conservative. Instead, at runtime, we must fail hard
790 // if the Dart as and the DDC as would return different values. 790 // if the Dart as and the DDC as would return different values.
791 node.visitChildren(this); 791 node.visitChildren(this);
(...skipping 19 matching lines...) Expand all
811 void visitPostfixExpression(PostfixExpression node) { 811 void visitPostfixExpression(PostfixExpression node) {
812 _checkUnary(node); 812 _checkUnary(node);
813 node.visitChildren(this); 813 node.visitChildren(this);
814 } 814 }
815 815
816 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { 816 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) {
817 var op = node.operator; 817 var op = node.operator;
818 if (op.isUserDefinableOperator || 818 if (op.isUserDefinableOperator ||
819 op.type == TokenType.PLUS_PLUS || 819 op.type == TokenType.PLUS_PLUS ||
820 op.type == TokenType.MINUS_MINUS) { 820 op.type == TokenType.MINUS_MINUS) {
821 if (_rules.isDynamicTarget(node.operand)) { 821 if (rules.isDynamicTarget(node.operand)) {
822 _recordDynamicInvoke(node, node.operand); 822 _recordDynamicInvoke(node, node.operand);
823 } 823 }
824 // For ++ and --, even if it is not dynamic, we still need to check 824 // For ++ and --, even if it is not dynamic, we still need to check
825 // that the user defined method accepts an `int` as the RHS. 825 // that the user defined method accepts an `int` as the RHS.
826 // We assume Analyzer has done this already. 826 // We assume Analyzer has done this already.
827 } 827 }
828 } 828 }
829 829
830 @override 830 @override
831 void visitBinaryExpression(BinaryExpression node) { 831 void visitBinaryExpression(BinaryExpression node) {
832 var op = node.operator; 832 var op = node.operator;
833 if (op.isUserDefinableOperator) { 833 if (op.isUserDefinableOperator) {
834 if (_rules.isDynamicTarget(node.leftOperand)) { 834 if (rules.isDynamicTarget(node.leftOperand)) {
835 // Dynamic invocation 835 // Dynamic invocation
836 // TODO(vsm): Move this logic to the resolver? 836 // TODO(vsm): Move this logic to the resolver?
837 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { 837 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) {
838 _recordDynamicInvoke(node, node.leftOperand); 838 _recordDynamicInvoke(node, node.leftOperand);
839 } 839 }
840 } else { 840 } else {
841 var element = node.staticElement; 841 var element = node.staticElement;
842 // Method invocation. 842 // Method invocation.
843 if (element is MethodElement) { 843 if (element is MethodElement) {
844 var type = element.type as FunctionType; 844 var type = element.type as FunctionType;
(...skipping 25 matching lines...) Expand all
870 870
871 @override 871 @override
872 void visitConditionalExpression(ConditionalExpression node) { 872 void visitConditionalExpression(ConditionalExpression node) {
873 checkBoolean(node.condition); 873 checkBoolean(node.condition);
874 node.visitChildren(this); 874 node.visitChildren(this);
875 } 875 }
876 876
877 @override 877 @override
878 void visitIndexExpression(IndexExpression node) { 878 void visitIndexExpression(IndexExpression node) {
879 var target = node.realTarget; 879 var target = node.realTarget;
880 if (_rules.isDynamicTarget(target)) { 880 if (rules.isDynamicTarget(target)) {
881 _recordDynamicInvoke(node, target); 881 _recordDynamicInvoke(node, target);
882 } else { 882 } else {
883 var element = node.staticElement; 883 var element = node.staticElement;
884 if (element is MethodElement) { 884 if (element is MethodElement) {
885 var type = element.type as FunctionType; 885 var type = element.type as FunctionType;
886 // Analyzer should enforce number of parameter types, but check in 886 // Analyzer should enforce number of parameter types, but check in
887 // case we have erroneous input. 887 // case we have erroneous input.
888 if (type.normalParameterTypes.isNotEmpty) { 888 if (type.normalParameterTypes.isNotEmpty) {
889 checkArgument(node.index, type.normalParameterTypes[0]); 889 checkArgument(node.index, type.normalParameterTypes[0]);
890 } 890 }
891 } else { 891 } else {
892 // TODO(vsm): Assert that the analyzer found an error here? 892 // TODO(vsm): Assert that the analyzer found an error here?
893 } 893 }
894 } 894 }
895 node.visitChildren(this); 895 node.visitChildren(this);
896 } 896 }
897 897
898 DartType getType(TypeName name) { 898 DartType getType(TypeName name) {
899 return (name == null) ? _rules.provider.dynamicType : name.type; 899 return (name == null) ? rules.provider.dynamicType : name.type;
900 } 900 }
901 901
902 /// Analyzer checks boolean conversions, but we need to check too, because 902 /// Analyzer checks boolean conversions, but we need to check too, because
903 /// it uses the default assignability rules that allow `dynamic` and `Object` 903 /// it uses the default assignability rules that allow `dynamic` and `Object`
904 /// to be assigned to bool with no message. 904 /// to be assigned to bool with no message.
905 void checkBoolean(Expression expr) => 905 void checkBoolean(Expression expr) =>
906 checkAssignment(expr, _rules.provider.boolType); 906 checkAssignment(expr, rules.provider.boolType);
907 907
908 void checkAssignment(Expression expr, DartType type) { 908 void checkAssignment(Expression expr, DartType type) {
909 if (expr is ParenthesizedExpression) { 909 if (expr is ParenthesizedExpression) {
910 checkAssignment(expr.expression, type); 910 checkAssignment(expr.expression, type);
911 } else { 911 } else {
912 _recordMessage(_rules.checkAssignment(expr, type)); 912 _recordMessage(rules.checkAssignment(expr, type));
913 } 913 }
914 } 914 }
915 915
916 DartType _specializedBinaryReturnType( 916 DartType _specializedBinaryReturnType(
917 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { 917 TokenType op, DartType t1, DartType t2, DartType normalReturnType) {
918 // This special cases binary return types as per 16.26 and 16.27 of the 918 // This special cases binary return types as per 16.26 and 16.27 of the
919 // Dart language spec. 919 // Dart language spec.
920 switch (op) { 920 switch (op) {
921 case TokenType.PLUS: 921 case TokenType.PLUS:
922 case TokenType.MINUS: 922 case TokenType.MINUS:
923 case TokenType.STAR: 923 case TokenType.STAR:
924 case TokenType.TILDE_SLASH: 924 case TokenType.TILDE_SLASH:
925 case TokenType.PERCENT: 925 case TokenType.PERCENT:
926 case TokenType.PLUS_EQ: 926 case TokenType.PLUS_EQ:
927 case TokenType.MINUS_EQ: 927 case TokenType.MINUS_EQ:
928 case TokenType.STAR_EQ: 928 case TokenType.STAR_EQ:
929 case TokenType.TILDE_SLASH_EQ: 929 case TokenType.TILDE_SLASH_EQ:
930 case TokenType.PERCENT_EQ: 930 case TokenType.PERCENT_EQ:
931 if (_rules.isIntType(t1) && _rules.isIntType(t2)) return t1; 931 if (rules.isIntType(t1) && rules.isIntType(t2)) return t1;
932 if (_rules.isDoubleType(t1) && _rules.isDoubleType(t2)) return t1; 932 if (rules.isDoubleType(t1) && rules.isDoubleType(t2)) return t1;
933 // This particular combo is not spelled out in the spec, but all 933 // This particular combo is not spelled out in the spec, but all
934 // implementations and analyzer seem to follow this. 934 // implementations and analyzer seem to follow this.
935 if (_rules.isDoubleType(t1) && _rules.isIntType(t2)) return t1; 935 if (rules.isDoubleType(t1) && rules.isIntType(t2)) return t1;
936 } 936 }
937 return normalReturnType; 937 return normalReturnType;
938 } 938 }
939 939
940 void _checkCompoundAssignment(AssignmentExpression expr) { 940 void _checkCompoundAssignment(AssignmentExpression expr) {
941 var op = expr.operator.type; 941 var op = expr.operator.type;
942 assert(op.isAssignmentOperator && op != TokenType.EQ); 942 assert(op.isAssignmentOperator && op != TokenType.EQ);
943 var methodElement = expr.staticElement; 943 var methodElement = expr.staticElement;
944 if (methodElement == null) { 944 if (methodElement == null) {
945 // Dynamic invocation 945 // Dynamic invocation
946 _recordDynamicInvoke(expr, expr.leftHandSide); 946 _recordDynamicInvoke(expr, expr.leftHandSide);
947 } else { 947 } else {
948 // Sanity check the operator 948 // Sanity check the operator
949 assert(methodElement.isOperator); 949 assert(methodElement.isOperator);
950 var functionType = methodElement.type; 950 var functionType = methodElement.type;
951 var paramTypes = functionType.normalParameterTypes; 951 var paramTypes = functionType.normalParameterTypes;
952 assert(paramTypes.length == 1); 952 assert(paramTypes.length == 1);
953 assert(functionType.namedParameterTypes.isEmpty); 953 assert(functionType.namedParameterTypes.isEmpty);
954 assert(functionType.optionalParameterTypes.isEmpty); 954 assert(functionType.optionalParameterTypes.isEmpty);
955 955
956 // Check the lhs type 956 // Check the lhs type
957 var staticInfo; 957 var staticInfo;
958 var rhsType = _rules.getStaticType(expr.rightHandSide); 958 var rhsType = rules.getStaticType(expr.rightHandSide);
959 var lhsType = _rules.getStaticType(expr.leftHandSide); 959 var lhsType = rules.getStaticType(expr.leftHandSide);
960 var returnType = _specializedBinaryReturnType( 960 var returnType = _specializedBinaryReturnType(
961 op, lhsType, rhsType, functionType.returnType); 961 op, lhsType, rhsType, functionType.returnType);
962 962
963 if (!_rules.isSubTypeOf(returnType, lhsType)) { 963 if (!rules.isSubTypeOf(returnType, lhsType)) {
964 final numType = _rules.provider.numType; 964 final numType = rules.provider.numType;
965 // Try to fix up the numerical case if possible. 965 // Try to fix up the numerical case if possible.
966 if (_rules.isSubTypeOf(lhsType, numType) && 966 if (rules.isSubTypeOf(lhsType, numType) &&
967 _rules.isSubTypeOf(lhsType, rhsType)) { 967 rules.isSubTypeOf(lhsType, rhsType)) {
968 // This is also slightly different from spec, but allows us to keep 968 // This is also slightly different from spec, but allows us to keep
969 // compound operators in the int += num and num += dynamic cases. 969 // compound operators in the int += num and num += dynamic cases.
970 staticInfo = DownCast.create( 970 staticInfo = DownCast.create(
971 _rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType)); 971 rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType));
972 rhsType = lhsType; 972 rhsType = lhsType;
973 } else { 973 } else {
974 // Static type error 974 // Static type error
975 staticInfo = new StaticTypeError(_rules, expr, lhsType); 975 staticInfo = new StaticTypeError(rules, expr, lhsType);
976 } 976 }
977 _recordMessage(staticInfo); 977 _recordMessage(staticInfo);
978 } 978 }
979 979
980 // Check the rhs type 980 // Check the rhs type
981 if (staticInfo is! CoercionInfo) { 981 if (staticInfo is! CoercionInfo) {
982 var paramType = paramTypes.first; 982 var paramType = paramTypes.first;
983 staticInfo = _rules.checkAssignment(expr.rightHandSide, paramType); 983 staticInfo = rules.checkAssignment(expr.rightHandSide, paramType);
984 _recordMessage(staticInfo); 984 _recordMessage(staticInfo);
985 } 985 }
986 } 986 }
987 } 987 }
988 988
989 void _recordDynamicInvoke(AstNode node, AstNode target) { 989 void _recordDynamicInvoke(AstNode node, AstNode target) {
990 _reporter.onError(new DynamicInvoke(_rules, node).toAnalysisError()); 990 reporter.onError(new DynamicInvoke(rules, node).toAnalysisError());
991 // TODO(jmesserly): we may eventually want to record if the whole operation 991 // TODO(jmesserly): we may eventually want to record if the whole operation
992 // (node) was dynamic, rather than the target, but this is an easier fit 992 // (node) was dynamic, rather than the target, but this is an easier fit
993 // with what we used to do. 993 // with what we used to do.
994 DynamicInvoke.set(target, true); 994 DynamicInvoke.set(target, true);
995 } 995 }
996 996
997 void _recordMessage(StaticInfo info) { 997 void _recordMessage(StaticInfo info) {
998 if (info == null) return; 998 if (info == null) return;
999 var error = info.toAnalysisError(); 999 var error = info.toAnalysisError();
1000 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; 1000 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
1001 _reporter.onError(error); 1001 reporter.onError(error);
1002 1002
1003 if (info is CoercionInfo) { 1003 if (info is CoercionInfo) {
1004 assert(CoercionInfo.get(info.node) == null); 1004 assert(CoercionInfo.get(info.node) == null);
1005 CoercionInfo.set(info.node, info); 1005 CoercionInfo.set(info.node, info);
1006 } 1006 }
1007 } 1007 }
1008 } 1008 }
OLDNEW
« no previous file with comments | « lib/src/analysis_context.dart ('k') | lib/src/checker/rules.dart » ('j') | lib/src/compiler.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698