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

Side by Side Diff: pkg/analyzer/lib/src/task/strong/checker.dart

Issue 1834213002: Improve highlighting for strong mode error (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 4 years, 8 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
6 // refactored to fit into analyzer. 6 // refactored to fit into analyzer.
7 library analyzer.src.task.strong.checker; 7 library analyzer.src.task.strong.checker;
8 8
9 import 'package:analyzer/analyzer.dart'; 9 import 'package:analyzer/analyzer.dart';
10 import 'package:analyzer/dart/ast/ast.dart'; 10 import 'package:analyzer/dart/ast/ast.dart';
(...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 ? typeProvider.streamType 338 ? typeProvider.streamType
339 : typeProvider.iterableType; 339 : typeProvider.iterableType;
340 var iterableType = _getStaticType(node.iterable); 340 var iterableType = _getStaticType(node.iterable);
341 var elementType = 341 var elementType =
342 rules.mostSpecificTypeArgument(iterableType, sequenceInterface); 342 rules.mostSpecificTypeArgument(iterableType, sequenceInterface);
343 343
344 // If the sequence is not an Iterable (or Stream for await for) but is a 344 // If the sequence is not an Iterable (or Stream for await for) but is a
345 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then 345 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then
346 // we'll do a separate cast of the dynamic element to the variable's type. 346 // we'll do a separate cast of the dynamic element to the variable's type.
347 if (elementType == null) { 347 if (elementType == null) {
348 var sequenceType = sequenceInterface.instantiate([DynamicTypeImpl.instan ce]); 348 var sequenceType =
349 sequenceInterface.instantiate([DynamicTypeImpl.instance]);
349 350
350 if (rules.isSubtypeOf(sequenceType, iterableType)) { 351 if (rules.isSubtypeOf(sequenceType, iterableType)) {
351 _recordMessage(DownCast.create( 352 _recordMessage(DownCast.create(
352 rules, node.iterable, iterableType, sequenceType)); 353 rules, node.iterable, iterableType, sequenceType));
353 elementType = DynamicTypeImpl.instance; 354 elementType = DynamicTypeImpl.instance;
354 } 355 }
355 } 356 }
356 357
357 // If the sequence doesn't implement the interface at all, [ErrorVerifier] 358 // If the sequence doesn't implement the interface at all, [ErrorVerifier]
358 // will report the error, so ignore it here. 359 // will report the error, so ignore it here.
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after
629 } 630 }
630 631
631 // Check the rhs type 632 // Check the rhs type
632 if (staticInfo is! CoercionInfo) { 633 if (staticInfo is! CoercionInfo) {
633 var paramType = paramTypes.first; 634 var paramType = paramTypes.first;
634 _checkDowncast(expr.rightHandSide, paramType); 635 _checkDowncast(expr.rightHandSide, paramType);
635 } 636 }
636 } 637 }
637 } 638 }
638 639
640 /// Records a [DownCast] of [expr] from [from] to [to], if there is one.
Bob Nystrom 2016/03/28 19:59:40 Are there changes to this, or did it just move? Is
Brian Wilkerson 2016/03/28 20:34:33 No, there are no changes. I was only expecting cha
641 ///
642 /// If [from] is omitted, uses the static type of [expr].
643 ///
644 /// If [expr] does not require a downcast because it is not related to [to]
645 /// or is already a subtype of it, does nothing.
646 void _checkDowncast(Expression expr, DartType to, {DartType from}) {
647 if (from == null) {
648 from = _getStaticType(expr);
649 }
650
651 // We can use anything as void.
652 if (to.isVoid) return;
653
654 // fromT <: toT, no coercion needed.
655 if (rules.isSubtypeOf(from, to)) return;
656
657 // TODO(vsm): We can get rid of the second clause if we disallow
658 // all sideways casts - see TODO below.
659 // -------
660 // Note: a function type is never assignable to a class per the Dart
661 // spec - even if it has a compatible call method. We disallow as
662 // well for consistency.
663 if ((from is FunctionType && rules.getCallMethodType(to) != null) ||
664 (to is FunctionType && rules.getCallMethodType(from) != null)) {
665 return;
666 }
667
668 // Downcast if toT <: fromT
669 if (rules.isSubtypeOf(to, from)) {
670 _recordMessage(DownCast.create(rules, expr, from, to));
671 return;
672 }
673
674 // TODO(vsm): Once we have generic methods, we should delete this
675 // workaround. These sideways casts are always ones we warn about
676 // - i.e., we think they are likely to fail at runtime.
677 // -------
678 // Downcast if toT <===> fromT
679 // The intention here is to allow casts that are sideways in the restricted
680 // type system, but allowed in the regular dart type system, since these
681 // are likely to succeed. The canonical example is List<dynamic> and
682 // Iterable<T> for some concrete T (e.g. Object). These are unrelated
683 // in the restricted system, but List<dynamic> <: Iterable<T> in dart.
684 if (from.isAssignableTo(to)) {
685 _recordMessage(DownCast.create(rules, expr, from, to));
686 }
687 }
688
639 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { 689 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) {
640 if ((_isDynamicTarget(target) || field.staticElement == null) && 690 if ((_isDynamicTarget(target) || field.staticElement == null) &&
641 !_isObjectProperty(target, field)) { 691 !_isObjectProperty(target, field)) {
642 _recordDynamicInvoke(node, target); 692 _recordDynamicInvoke(node, target);
643 } 693 }
644 node.visitChildren(this); 694 node.visitChildren(this);
645 } 695 }
646 696
647 void _checkReturnOrYield(Expression expression, AstNode node, 697 void _checkReturnOrYield(Expression expression, AstNode node,
648 {bool yieldStar: false}) { 698 {bool yieldStar: false}) {
649 var body = node.getAncestor((n) => n is FunctionBody); 699 FunctionBody body = node.getAncestor((n) => n is FunctionBody);
650 var type = _getExpectedReturnType(body, yieldStar: yieldStar); 700 var type = _getExpectedReturnType(body, yieldStar: yieldStar);
651 if (type == null) { 701 if (type == null) {
652 // We have a type mismatch: the async/async*/sync* modifier does 702 // We have a type mismatch: the async/async*/sync* modifier does
653 // not match the return or yield type. We should have already gotten an 703 // not match the return or yield type. We should have already gotten an
654 // analyzer error in this case. 704 // analyzer error in this case.
655 return; 705 return;
656 } 706 }
657 InterfaceType futureType = typeProvider.futureType; 707 InterfaceType futureType = typeProvider.futureType;
658 DartType actualType = expression?.staticType; 708 DartType actualType = expression?.staticType;
659 if (body.isAsynchronous && 709 if (body.isAsynchronous &&
(...skipping 20 matching lines...) Expand all
680 op.type == TokenType.MINUS_MINUS) { 730 op.type == TokenType.MINUS_MINUS) {
681 if (_isDynamicTarget(node.operand)) { 731 if (_isDynamicTarget(node.operand)) {
682 _recordDynamicInvoke(node, node.operand); 732 _recordDynamicInvoke(node, node.operand);
683 } 733 }
684 // For ++ and --, even if it is not dynamic, we still need to check 734 // For ++ and --, even if it is not dynamic, we still need to check
685 // that the user defined method accepts an `int` as the RHS. 735 // that the user defined method accepts an `int` as the RHS.
686 // We assume Analyzer has done this already. 736 // We assume Analyzer has done this already.
687 } 737 }
688 } 738 }
689 739
690 /// Records a [DownCast] of [expr] from [from] to [to], if there is one.
691 ///
692 /// If [from] is omitted, uses the static type of [expr].
693 ///
694 /// If [expr] does not require a downcast because it is not related to [to]
695 /// or is already a subtype of it, does nothing.
696 void _checkDowncast(Expression expr, DartType to, {DartType from}) {
697 if (from == null) {
698 from = _getStaticType(expr);
699 }
700
701 // We can use anything as void.
702 if (to.isVoid) return;
703
704 // fromT <: toT, no coercion needed.
705 if (rules.isSubtypeOf(from, to)) return;
706
707 // TODO(vsm): We can get rid of the second clause if we disallow
708 // all sideways casts - see TODO below.
709 // -------
710 // Note: a function type is never assignable to a class per the Dart
711 // spec - even if it has a compatible call method. We disallow as
712 // well for consistency.
713 if ((from is FunctionType && rules.getCallMethodType(to) != null) ||
714 (to is FunctionType && rules.getCallMethodType(from) != null)) {
715 return;
716 }
717
718 // Downcast if toT <: fromT
719 if (rules.isSubtypeOf(to, from)) {
720 _recordMessage(DownCast.create(rules, expr, from, to));
721 return;
722 }
723
724 // TODO(vsm): Once we have generic methods, we should delete this
725 // workaround. These sideways casts are always ones we warn about
726 // - i.e., we think they are likely to fail at runtime.
727 // -------
728 // Downcast if toT <===> fromT
729 // The intention here is to allow casts that are sideways in the restricted
730 // type system, but allowed in the regular dart type system, since these
731 // are likely to succeed. The canonical example is List<dynamic> and
732 // Iterable<T> for some concrete T (e.g. Object). These are unrelated
733 // in the restricted system, but List<dynamic> <: Iterable<T> in dart.
734 if (from.isAssignableTo(to)) {
735 _recordMessage(DownCast.create(rules, expr, from, to));
736 }
737 }
738
739 // Produce a coercion which coerces something of type fromT 740 // Produce a coercion which coerces something of type fromT
740 // to something of type toT. 741 // to something of type toT.
741 // Returns the error coercion if the types cannot be coerced 742 // Returns the error coercion if the types cannot be coerced
742 // according to our current criteria. 743 // according to our current criteria.
743 /// Gets the expected return type of the given function [body], either from 744 /// Gets the expected return type of the given function [body], either from
744 /// a normal return/yield, or from a yield*. 745 /// a normal return/yield, or from a yield*.
745 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { 746 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
746 FunctionType functionType; 747 FunctionType functionType;
747 var parent = body.parent; 748 var parent = body.parent;
748 if (parent is Declaration) { 749 if (parent is Declaration) {
749 functionType = _elementType(parent.element); 750 functionType = _elementType(parent.element);
750 } else { 751 } else {
751 assert(parent is FunctionExpression); 752 assert(parent is FunctionExpression);
752 functionType = parent.staticType ?? DynamicTypeImpl.instance; 753 functionType =
754 (parent as FunctionExpression).staticType ?? DynamicTypeImpl.instance;
753 } 755 }
754 756
755 var type = functionType.returnType; 757 var type = functionType.returnType;
756 758
757 InterfaceType expectedType = null; 759 InterfaceType expectedType = null;
758 if (body.isAsynchronous) { 760 if (body.isAsynchronous) {
759 if (body.isGenerator) { 761 if (body.isGenerator) {
760 // Stream<T> -> T 762 // Stream<T> -> T
761 expectedType = typeProvider.streamType; 763 expectedType = typeProvider.streamType;
762 } else { 764 } else {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
797 DartType t = expr.staticType ?? DynamicTypeImpl.instance; 799 DartType t = expr.staticType ?? DynamicTypeImpl.instance;
798 800
799 // Remove fuzzy arrow if possible. 801 // Remove fuzzy arrow if possible.
800 if (t is FunctionType && StaticInfo.isKnownFunction(expr)) { 802 if (t is FunctionType && StaticInfo.isKnownFunction(expr)) {
801 t = _removeFuzz(t); 803 t = _removeFuzz(t);
802 } 804 }
803 805
804 return t; 806 return t;
805 } 807 }
806 808
807 /// Remove "fuzzy arrow" in this function type.
808 ///
809 /// Normally we treat dynamically typed parameters as bottom for function
810 /// types. This allows type tests such as `if (f is SingleArgFunction)`.
811 /// It also requires a dynamic check on the parameter type to call these
812 /// functions.
813 ///
814 /// When we convert to a strict arrow, dynamically typed parameters become
815 /// top. This is safe to do for known functions, like top-level or local
816 /// functions and static methods. Those functions must already be essentially
817 /// treating dynamic as top.
818 ///
819 /// Only the outer-most arrow can be strict. Any others must be fuzzy, because
820 /// we don't know what function value will be passed there.
821 // TODO(jmesserly): should we use a real "fuzzyArrow" bit on the function
822 // type? That would allow us to implement this in the subtype relation.
823 // TODO(jmesserly): we'll need to factor this differently if we want to
824 // move CodeChecker's functionality into existing analyzer. Likely we can
825 // let the Expression have a strict arrow, then in places were we do
826 // inference, convert back to a fuzzy arrow.
827 FunctionType _removeFuzz(FunctionType t) {
828 bool foundFuzz = false;
829 List<ParameterElement> parameters = <ParameterElement>[];
830 for (ParameterElement p in t.parameters) {
831 ParameterElement newP = _removeParameterFuzz(p);
832 parameters.add(newP);
833 if (p != newP) foundFuzz = true;
834 }
835 if (!foundFuzz) {
836 return t;
837 }
838
839 FunctionElementImpl function = new FunctionElementImpl("", -1);
840 function.synthetic = true;
841 function.returnType = t.returnType;
842 function.shareTypeParameters(t.typeFormals);
843 function.shareParameters(parameters);
844 return function.type = new FunctionTypeImpl(function);
845 }
846
847 /// Removes fuzzy arrow, see [_removeFuzz].
848 ParameterElement _removeParameterFuzz(ParameterElement p) {
849 if (p.type.isDynamic) {
850 return new ParameterElementImpl.synthetic(
851 p.name, typeProvider.objectType, p.parameterKind);
852 }
853 return p;
854 }
855
856 /// Given an expression, return its type assuming it is 809 /// Given an expression, return its type assuming it is
857 /// in the caller position of a call (that is, accounting 810 /// in the caller position of a call (that is, accounting
858 /// for the possibility of a call method). Returns null 811 /// for the possibility of a call method). Returns null
859 /// if expression is not statically callable. 812 /// if expression is not statically callable.
860 FunctionType _getTypeAsCaller(Expression node) { 813 FunctionType _getTypeAsCaller(Expression node) {
861 DartType t = node.staticType; 814 DartType t = node.staticType;
862 if (node is SimpleIdentifier) { 815 if (node is SimpleIdentifier) {
863 Expression parent = node.parent; 816 Expression parent = node.parent;
864 if (parent is MethodInvocation) { 817 if (parent is MethodInvocation) {
865 t = parent.staticInvokeType; 818 t = parent.staticInvokeType;
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
948 if (info is CoercionInfo) { 901 if (info is CoercionInfo) {
949 // TODO(jmesserly): if we're run again on the same AST, we'll produce the 902 // TODO(jmesserly): if we're run again on the same AST, we'll produce the
950 // same annotations. This should be harmless. This might go away once 903 // same annotations. This should be harmless. This might go away once
951 // CodeChecker is integrated better with analyzer, as it will know that 904 // CodeChecker is integrated better with analyzer, as it will know that
952 // checking has already been performed. 905 // checking has already been performed.
953 // assert(CoercionInfo.get(info.node) == null); 906 // assert(CoercionInfo.get(info.node) == null);
954 CoercionInfo.set(info.node, info); 907 CoercionInfo.set(info.node, info);
955 } 908 }
956 } 909 }
957 910
911 /// Remove "fuzzy arrow" in this function type.
912 ///
913 /// Normally we treat dynamically typed parameters as bottom for function
914 /// types. This allows type tests such as `if (f is SingleArgFunction)`.
915 /// It also requires a dynamic check on the parameter type to call these
916 /// functions.
917 ///
918 /// When we convert to a strict arrow, dynamically typed parameters become
919 /// top. This is safe to do for known functions, like top-level or local
920 /// functions and static methods. Those functions must already be essentially
921 /// treating dynamic as top.
922 ///
923 /// Only the outer-most arrow can be strict. Any others must be fuzzy, because
924 /// we don't know what function value will be passed there.
925 // TODO(jmesserly): should we use a real "fuzzyArrow" bit on the function
926 // type? That would allow us to implement this in the subtype relation.
927 // TODO(jmesserly): we'll need to factor this differently if we want to
928 // move CodeChecker's functionality into existing analyzer. Likely we can
929 // let the Expression have a strict arrow, then in places were we do
930 // inference, convert back to a fuzzy arrow.
931 FunctionType _removeFuzz(FunctionType t) {
932 bool foundFuzz = false;
933 List<ParameterElement> parameters = <ParameterElement>[];
934 for (ParameterElement p in t.parameters) {
935 ParameterElement newP = _removeParameterFuzz(p);
936 parameters.add(newP);
937 if (p != newP) foundFuzz = true;
938 }
939 if (!foundFuzz) {
940 return t;
941 }
942
943 FunctionElementImpl function = new FunctionElementImpl("", -1);
944 function.synthetic = true;
945 function.returnType = t.returnType;
946 function.shareTypeParameters(t.typeFormals);
947 function.shareParameters(parameters);
948 return function.type = new FunctionTypeImpl(function);
949 }
950
951 /// Removes fuzzy arrow, see [_removeFuzz].
952 ParameterElement _removeParameterFuzz(ParameterElement p) {
953 if (p.type.isDynamic) {
954 return new ParameterElementImpl.synthetic(
955 p.name, typeProvider.objectType, p.parameterKind);
956 }
957 return p;
958 }
959
958 DartType _specializedBinaryReturnType( 960 DartType _specializedBinaryReturnType(
959 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { 961 TokenType op, DartType t1, DartType t2, DartType normalReturnType) {
960 // This special cases binary return types as per 16.26 and 16.27 of the 962 // This special cases binary return types as per 16.26 and 16.27 of the
961 // Dart language spec. 963 // Dart language spec.
962 switch (op) { 964 switch (op) {
963 case TokenType.PLUS: 965 case TokenType.PLUS:
964 case TokenType.MINUS: 966 case TokenType.MINUS:
965 case TokenType.STAR: 967 case TokenType.STAR:
966 case TokenType.TILDE_SLASH: 968 case TokenType.TILDE_SLASH:
967 case TokenType.PERCENT: 969 case TokenType.PERCENT:
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
1051 } 1053 }
1052 1054
1053 /// Check that individual methods and fields in [subType] correctly override 1055 /// Check that individual methods and fields in [subType] correctly override
1054 /// the declarations in [baseType]. 1056 /// the declarations in [baseType].
1055 /// 1057 ///
1056 /// The [errorLocation] node indicates where errors are reported, see 1058 /// The [errorLocation] node indicates where errors are reported, see
1057 /// [_checkSingleOverride] for more details. 1059 /// [_checkSingleOverride] for more details.
1058 _checkIndividualOverridesFromClass(ClassDeclaration node, 1060 _checkIndividualOverridesFromClass(ClassDeclaration node,
1059 InterfaceType baseType, Set<String> seen, bool isSubclass) { 1061 InterfaceType baseType, Set<String> seen, bool isSubclass) {
1060 for (var member in node.members) { 1062 for (var member in node.members) {
1061 if (member is ConstructorDeclaration) continue;
1062 if (member is FieldDeclaration) { 1063 if (member is FieldDeclaration) {
1063 if (member.isStatic) continue; 1064 if (member.isStatic) continue;
1064 for (var variable in member.fields.variables) { 1065 for (var variable in member.fields.variables) {
1065 var element = variable.element as PropertyInducingElement; 1066 var element = variable.element as PropertyInducingElement;
1066 var name = element.name; 1067 var name = element.name;
1067 if (seen.contains(name)) continue; 1068 if (seen.contains(name)) continue;
1068 var getter = element.getter; 1069 var getter = element.getter;
1069 var setter = element.setter; 1070 var setter = element.setter;
1070 bool found = _checkSingleOverride( 1071 bool found = _checkSingleOverride(
1071 getter, baseType, variable, member, isSubclass); 1072 getter, baseType, variable.name, member, isSubclass);
1072 if (!variable.isFinal && 1073 if (!variable.isFinal &&
1073 !variable.isConst && 1074 !variable.isConst &&
1074 _checkSingleOverride( 1075 _checkSingleOverride(
1075 setter, baseType, variable, member, isSubclass)) { 1076 setter, baseType, variable.name, member, isSubclass)) {
1076 found = true; 1077 found = true;
1077 } 1078 }
1078 if (found) seen.add(name); 1079 if (found) seen.add(name);
1079 } 1080 }
1080 } else { 1081 } else if (member is MethodDeclaration) {
1081 if ((member as MethodDeclaration).isStatic) continue; 1082 if (member.isStatic) continue;
1082 var method = (member as MethodDeclaration).element; 1083 var method = member.element;
1083 if (seen.contains(method.name)) continue; 1084 if (seen.contains(method.name)) continue;
1084 if (_checkSingleOverride( 1085 if (_checkSingleOverride(
1085 method, baseType, member, member, isSubclass)) { 1086 method, baseType, member.name, member, isSubclass)) {
1086 seen.add(method.name); 1087 seen.add(method.name);
1087 } 1088 }
1088 } 1089 }
Bob Nystrom 2016/03/28 19:59:40 Maybe leave a comment or assert that the only rema
Brian Wilkerson 2016/03/28 20:34:33 I'll do that.
1089 } 1090 }
1090 } 1091 }
1091 1092
1092 /// Check that individual methods and fields in [subType] correctly override 1093 /// Check that individual methods and fields in [subType] correctly override
1093 /// the declarations in [baseType]. 1094 /// the declarations in [baseType].
1094 /// 1095 ///
1095 /// The [errorLocation] node indicates where errors are reported, see 1096 /// The [errorLocation] node indicates where errors are reported, see
1096 /// [_checkSingleOverride] for more details. 1097 /// [_checkSingleOverride] for more details.
1097 /// 1098 ///
1098 /// The set [seen] is used to avoid reporting overrides more than once. It 1099 /// The set [seen] is used to avoid reporting overrides more than once. It
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
1299 } while (!current.isObject && !visited.contains(current)); 1300 } while (!current.isObject && !visited.contains(current));
1300 } 1301 }
1301 1302
1302 void _recordMessage(StaticInfo info) { 1303 void _recordMessage(StaticInfo info) {
1303 if (info == null) return; 1304 if (info == null) return;
1304 var error = info.toAnalysisError(); 1305 var error = info.toAnalysisError();
1305 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; 1306 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
1306 _reporter.onError(error); 1307 _reporter.onError(error);
1307 } 1308 }
1308 } 1309 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698