@@ -337,7 +337,6 @@ class CodeChecker extends RecursiveAstVisitor {
337
337
final TypeRules _rules;
338
338
final CheckerReporter _reporter;
339
339
final _OverrideChecker _overrideChecker;
340
- bool _constantContext = false ;
341
340
bool _failure = false ;
342
341
bool get failure => _failure || _overrideChecker._failure;
343
342
@@ -358,27 +357,6 @@ class CodeChecker extends RecursiveAstVisitor {
358
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
360
@override
383
361
void visitComment (Comment node) {
384
362
// skip, no need to do typechecking inside comments (they may contain
@@ -406,17 +384,15 @@ class CodeChecker extends RecursiveAstVisitor {
406
384
/// Check constructor declaration to ensure correct super call placement.
407
385
@override
408
386
void visitConstructorDeclaration (ConstructorDeclaration node) {
409
- _visitMaybeConst (node, (node) {
410
- node.visitChildren (this );
411
-
412
- final init = node.initializers;
413
- for (int i = 0 , last = init.length - 1 ; i < last; i++ ) {
414
- final node = init[i];
415
- if (node is SuperConstructorInvocation ) {
416
- _recordMessage (new InvalidSuperInvocation (node));
417
- }
387
+ node.visitChildren (this );
388
+
389
+ final init = node.initializers;
390
+ for (int i = 0 , last = init.length - 1 ; i < last; i++ ) {
391
+ final node = init[i];
392
+ if (node is SuperConstructorInvocation ) {
393
+ _recordMessage (new InvalidSuperInvocation (node));
418
394
}
419
- });
395
+ }
420
396
}
421
397
422
398
@override
@@ -603,40 +579,94 @@ class CodeChecker extends RecursiveAstVisitor {
603
579
node.visitChildren (this );
604
580
}
605
581
606
- AstNode _getOwnerFunction (AstNode node) {
607
- var parent = node.parent;
608
- while (parent is ! FunctionExpression &&
609
- parent is ! MethodDeclaration &&
610
- parent is ! ConstructorDeclaration ) {
611
- parent = parent.parent;
582
+ DartType _getExpectedReturnType (FunctionBody body, bool yieldStar) {
583
+ FunctionType functionType;
584
+ var parent = body.parent;
585
+ if (parent is Declaration ) {
586
+ functionType = _rules.elementType (parent.element);
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) {
617
- if (node is Declaration ) {
618
- return _rules.elementType (node.element);
592
+ var type = functionType.returnType;
593
+ var provider = _rules.provider;
594
+
595
+ InterfaceType expectedType = null ;
596
+ if (body.isAsynchronous) {
597
+ if (body.isGenerator) {
598
+ // Stream<T> -> T
599
+ expectedType = provider.streamType;
600
+ } else {
601
+ // Future<T> -> T
602
+ // TODO(vsm): Revisit with issue #228.
603
+ expectedType = provider.futureType;
604
+ }
619
605
} else {
620
- assert (node is FunctionExpression );
621
- return _rules.getStaticType (node);
606
+ if (body.isGenerator) {
607
+ // Iterable<T> -> T
608
+ expectedType = provider.iterableType;
609
+ } else {
610
+ // T -> T
611
+ return type;
612
+ }
613
+ }
614
+ if (yieldStar) {
615
+ if (type.isDynamic) {
616
+ // Ensure it's at least a Stream / Iterable.
617
+ return expectedType.substitute4 ([provider.dynamicType]);
618
+ } else {
619
+ // Analyzer will provide a separate error if expected type
620
+ // is not compatible with type.
621
+ return type;
622
+ }
623
+ }
624
+ if (type.isDynamic) {
625
+ return type;
626
+ } else if (type is InterfaceType && type.element == expectedType.element) {
627
+ return type.typeArguments[0 ];
628
+ } else {
629
+ // Malformed type - fallback on analyzer error.
630
+ return null ;
631
+ }
632
+ }
633
+
634
+ FunctionBody _getFunctionBody (AstNode node) {
635
+ while (node is ! FunctionBody ) {
636
+ node = node.parent;
622
637
}
638
+ return node as FunctionBody ;
623
639
}
624
640
625
- void _checkReturn (Expression expression, AstNode node) {
626
- var type = _getFunctionType (_getOwnerFunction (node)).returnType;
641
+ void _checkReturnOrYield (Expression expression, AstNode node,
642
+ [bool yieldStar = false ]) {
643
+ var body = _getFunctionBody (node);
644
+ var type = _getExpectedReturnType (body, yieldStar);
645
+ if (type == null ) {
646
+ // We have a type mismatch: the async/async*/sync* modifier does
647
+ // not match the return or yield type. We should have already gotten an
648
+ // analyzer error in this case.
649
+ return ;
650
+ }
627
651
// TODO(vsm): Enforce void or dynamic (to void?) when expression is null.
628
652
if (expression != null ) checkAssignment (expression, type);
629
653
}
630
654
631
655
@override
632
656
void visitExpressionFunctionBody (ExpressionFunctionBody node) {
633
- _checkReturn (node.expression, node);
657
+ _checkReturnOrYield (node.expression, node);
634
658
node.visitChildren (this );
635
659
}
636
660
637
661
@override
638
662
void visitReturnStatement (ReturnStatement node) {
639
- _checkReturn (node.expression, node);
663
+ _checkReturnOrYield (node.expression, node);
664
+ node.visitChildren (this );
665
+ }
666
+
667
+ @override
668
+ void visitYieldStatement (YieldStatement node) {
669
+ _checkReturnOrYield (node.expression, node, node.star != null );
640
670
node.visitChildren (this );
641
671
}
642
672
@@ -660,24 +690,22 @@ class CodeChecker extends RecursiveAstVisitor {
660
690
661
691
@override
662
692
void visitDefaultFormalParameter (DefaultFormalParameter node) {
663
- _visitMaybeConst (node, (node) {
664
- // Check that defaults have the proper subtype.
665
- var parameter = node.parameter;
666
- var parameterType = _rules.elementType (parameter.element);
667
- assert (parameterType != null );
668
- var defaultValue = node.defaultValue;
669
- if (defaultValue == null ) {
670
- if (_rules.maybeNonNullableType (parameterType)) {
671
- var staticInfo = new InvalidVariableDeclaration (
672
- _rules, node.identifier, parameterType);
673
- _recordMessage (staticInfo);
674
- }
675
- } else {
676
- checkAssignment (defaultValue, parameterType);
693
+ // Check that defaults have the proper subtype.
694
+ var parameter = node.parameter;
695
+ var parameterType = _rules.elementType (parameter.element);
696
+ assert (parameterType != null );
697
+ var defaultValue = node.defaultValue;
698
+ if (defaultValue == null ) {
699
+ if (_rules.maybeNonNullableType (parameterType)) {
700
+ var staticInfo = new InvalidVariableDeclaration (
701
+ _rules, node.identifier, parameterType);
702
+ _recordMessage (staticInfo);
677
703
}
704
+ } else {
705
+ checkAssignment (defaultValue, parameterType);
706
+ }
678
707
679
- node.visitChildren (this );
680
- });
708
+ node.visitChildren (this );
681
709
}
682
710
683
711
@override
@@ -700,56 +728,47 @@ class CodeChecker extends RecursiveAstVisitor {
700
728
701
729
@override
702
730
void visitInstanceCreationExpression (InstanceCreationExpression node) {
703
- _visitMaybeConst (node, (node) {
704
- var arguments = node.argumentList;
705
- var element = node.staticElement;
706
- if (element != null ) {
707
- var type = _rules.elementType (node.staticElement);
708
- checkArgumentList (arguments, type);
709
- } else {
710
- _recordMessage (new MissingTypeError (node));
711
- }
712
- node.visitChildren (this );
713
- });
731
+ var arguments = node.argumentList;
732
+ var element = node.staticElement;
733
+ if (element != null ) {
734
+ var type = _rules.elementType (node.staticElement);
735
+ checkArgumentList (arguments, type);
736
+ } else {
737
+ _recordMessage (new MissingTypeError (node));
738
+ }
739
+ node.visitChildren (this );
714
740
}
715
741
716
742
@override
717
743
void visitVariableDeclarationList (VariableDeclarationList node) {
718
- _visitMaybeConst (node, (node) {
719
- TypeName type = node.type;
720
- if (type == null ) {
721
- // No checks are needed when the type is var. Although internally the
722
- // typing rules may have inferred a more precise type for the variable
723
- // based on the initializer.
724
- } else {
725
- var dartType = getType (type);
726
- for (VariableDeclaration variable in node.variables) {
727
- var initializer = variable.initializer;
728
- if (initializer != null ) {
729
- checkAssignment (initializer, dartType);
730
- } else if (_rules.maybeNonNullableType (dartType)) {
731
- var element = variable.element;
732
- if (element is FieldElement && ! element.isStatic) {
733
- // Initialized - possibly implicitly - during construction.
734
- // Handle this via a runtime check during code generation.
735
-
736
- // TODO(vsm): Detect statically whether this can fail and
737
- // report a static error (must fail) or warning (can fail).
738
- } else {
739
- var staticInfo =
740
- new InvalidVariableDeclaration (_rules, variable, dartType);
741
- _recordMessage (staticInfo);
742
- }
744
+ TypeName type = node.type;
745
+ if (type == null ) {
746
+ // No checks are needed when the type is var. Although internally the
747
+ // typing rules may have inferred a more precise type for the variable
748
+ // based on the initializer.
749
+ } else {
750
+ var dartType = getType (type);
751
+ for (VariableDeclaration variable in node.variables) {
752
+ var initializer = variable.initializer;
753
+ if (initializer != null ) {
754
+ checkAssignment (initializer, dartType);
755
+ } else if (_rules.maybeNonNullableType (dartType)) {
756
+ var element = variable.element;
757
+ if (element is FieldElement && ! element.isStatic) {
758
+ // Initialized - possibly implicitly - during construction.
759
+ // Handle this via a runtime check during code generation.
760
+
761
+ // TODO(vsm): Detect statically whether this can fail and
762
+ // report a static error (must fail) or warning (can fail).
763
+ } else {
764
+ var staticInfo =
765
+ new InvalidVariableDeclaration (_rules, variable, dartType);
766
+ _recordMessage (staticInfo);
743
767
}
744
768
}
745
769
}
746
- node.visitChildren (this );
747
- });
748
- }
749
-
750
- @override
751
- void visitVariableDeclaration (VariableDeclaration node) {
752
- _visitMaybeConst (node, super .visitVariableDeclaration);
770
+ }
771
+ node.visitChildren (this );
753
772
}
754
773
755
774
void _checkRuntimeTypeCheck (AstNode node, TypeName typeName) {
@@ -885,7 +904,7 @@ class CodeChecker extends RecursiveAstVisitor {
885
904
if (expr is ParenthesizedExpression ) {
886
905
checkAssignment (expr.expression, type);
887
906
} else {
888
- _recordMessage (_rules.checkAssignment (expr, type, _constantContext ));
907
+ _recordMessage (_rules.checkAssignment (expr, type));
889
908
}
890
909
}
891
910
@@ -956,8 +975,7 @@ class CodeChecker extends RecursiveAstVisitor {
956
975
// Check the rhs type
957
976
if (staticInfo is ! CoercionInfo ) {
958
977
var paramType = paramTypes.first;
959
- staticInfo = _rules.checkAssignment (
960
- expr.rightHandSide, paramType, _constantContext);
978
+ staticInfo = _rules.checkAssignment (expr.rightHandSide, paramType);
961
979
_recordMessage (staticInfo);
962
980
}
963
981
}
0 commit comments