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

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

Issue 2060013002: Refactor strong mode to use standard Analyzer errors (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 6 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 // 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';
11 import 'package:analyzer/dart/ast/token.dart' show TokenType; 11 import 'package:analyzer/dart/ast/token.dart' show TokenType;
12 import 'package:analyzer/dart/ast/visitor.dart'; 12 import 'package:analyzer/dart/ast/visitor.dart';
13 import 'package:analyzer/dart/element/element.dart'; 13 import 'package:analyzer/dart/element/element.dart';
14 import 'package:analyzer/dart/element/type.dart'; 14 import 'package:analyzer/dart/element/type.dart';
15 import 'package:analyzer/src/dart/element/type.dart'; 15 import 'package:analyzer/src/dart/element/type.dart';
16 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; 16 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
17 import 'package:analyzer/src/generated/error.dart' show StrongModeCode;
17 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; 18 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
18 import 'package:analyzer/src/generated/type_system.dart'; 19 import 'package:analyzer/src/generated/type_system.dart';
19 20
20 import 'info.dart'; 21 import 'ast_properties.dart';
21 22
22 DartType _elementType(Element e) { 23 DartType _elementType(Element e) {
23 if (e == null) { 24 if (e == null) {
24 // Malformed code - just return dynamic. 25 // Malformed code - just return dynamic.
25 return DynamicTypeImpl.instance; 26 return DynamicTypeImpl.instance;
26 } 27 }
27 return (e as dynamic).type; 28 return (e as dynamic).type;
28 } 29 }
29 30
30 PropertyInducingElement _getMemberField( 31 PropertyInducingElement _getMemberField(
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 return f; 95 return f;
95 } 96 }
96 97
97 typedef FunctionType _MemberTypeGetter(InterfaceType type); 98 typedef FunctionType _MemberTypeGetter(InterfaceType type);
98 99
99 /// Checks the body of functions and properties. 100 /// Checks the body of functions and properties.
100 class CodeChecker extends RecursiveAstVisitor { 101 class CodeChecker extends RecursiveAstVisitor {
101 final StrongTypeSystemImpl rules; 102 final StrongTypeSystemImpl rules;
102 final TypeProvider typeProvider; 103 final TypeProvider typeProvider;
103 final AnalysisErrorListener reporter; 104 final AnalysisErrorListener reporter;
104 final _OverrideChecker _overrideChecker;
105 final AnalysisOptionsImpl _options; 105 final AnalysisOptionsImpl _options;
106 _OverrideChecker _overrideChecker;
106 107
107 bool _failure = false; 108 bool _failure = false;
108 109
109 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules, 110 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules,
110 AnalysisErrorListener reporter, this._options) 111 AnalysisErrorListener reporter, this._options)
111 : typeProvider = typeProvider, 112 : typeProvider = typeProvider,
112 rules = rules, 113 rules = rules,
113 reporter = reporter, 114 reporter = reporter {
114 _overrideChecker = new _OverrideChecker(typeProvider, rules, reporter); 115 _overrideChecker = new _OverrideChecker(this);
116 }
115 117
116 bool get failure => _failure || _overrideChecker._failure; 118 bool get failure => _failure;
117 119
118 void checkArgument(Expression arg, DartType expectedType) { 120 void checkArgument(Expression arg, DartType expectedType) {
119 // Preserve named argument structure, so their immediate parent is the 121 // Preserve named argument structure, so their immediate parent is the
120 // method invocation. 122 // method invocation.
121 Expression baseExpression = arg is NamedExpression ? arg.expression : arg; 123 Expression baseExpression = arg is NamedExpression ? arg.expression : arg;
122 checkAssignment(baseExpression, expectedType); 124 checkAssignment(baseExpression, expectedType);
123 } 125 }
124 126
125 void checkArgumentList(ArgumentList node, FunctionType type) { 127 void checkArgumentList(ArgumentList node, FunctionType type) {
126 NodeList<Expression> list = node.arguments; 128 NodeList<Expression> list = node.arguments;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 checkArgumentList(list, _getTypeAsCaller(f)); 165 checkArgumentList(list, _getTypeAsCaller(f));
164 } 166 }
165 } 167 }
166 168
167 DartType getType(TypeName name) { 169 DartType getType(TypeName name) {
168 return (name == null) ? DynamicTypeImpl.instance : name.type; 170 return (name == null) ? DynamicTypeImpl.instance : name.type;
169 } 171 }
170 172
171 void reset() { 173 void reset() {
172 _failure = false; 174 _failure = false;
173 _overrideChecker._failure = false;
174 } 175 }
175 176
176 @override 177 @override
177 void visitAsExpression(AsExpression node) { 178 void visitAsExpression(AsExpression node) {
178 // We could do the same check as the IsExpression below, but that is 179 // We could do the same check as the IsExpression below, but that is
179 // potentially too conservative. Instead, at runtime, we must fail hard 180 // potentially too conservative. Instead, at runtime, we must fail hard
180 // if the Dart as and the DDC as would return different values. 181 // if the Dart as and the DDC as would return different values.
181 node.visitChildren(this); 182 node.visitChildren(this);
182 } 183 }
183 184
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
257 258
258 /// Check constructor declaration to ensure correct super call placement. 259 /// Check constructor declaration to ensure correct super call placement.
259 @override 260 @override
260 void visitConstructorDeclaration(ConstructorDeclaration node) { 261 void visitConstructorDeclaration(ConstructorDeclaration node) {
261 node.visitChildren(this); 262 node.visitChildren(this);
262 263
263 final init = node.initializers; 264 final init = node.initializers;
264 for (int i = 0, last = init.length - 1; i < last; i++) { 265 for (int i = 0, last = init.length - 1; i < last; i++) {
265 final node = init[i]; 266 final node = init[i];
266 if (node is SuperConstructorInvocation) { 267 if (node is SuperConstructorInvocation) {
267 _recordMessage(new InvalidSuperInvocation(node)); 268 _recordMessage(node, StrongModeCode.INVALID_SUPER_INVOCATION, [node]);
268 } 269 }
269 } 270 }
270 } 271 }
271 272
272 // Check invocations 273 // Check invocations
273 @override 274 @override
274 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { 275 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
275 var field = node.fieldName; 276 var field = node.fieldName;
276 var element = field.staticElement; 277 var element = field.staticElement;
277 DartType staticType = _elementType(element); 278 DartType staticType = _elementType(element);
(...skipping 30 matching lines...) Expand all
308 @override 309 @override
309 void visitFieldFormalParameter(FieldFormalParameter node) { 310 void visitFieldFormalParameter(FieldFormalParameter node) {
310 var element = node.element; 311 var element = node.element;
311 var typeName = node.type; 312 var typeName = node.type;
312 if (typeName != null) { 313 if (typeName != null) {
313 var type = _elementType(element); 314 var type = _elementType(element);
314 var fieldElement = 315 var fieldElement =
315 node.identifier.staticElement as FieldFormalParameterElement; 316 node.identifier.staticElement as FieldFormalParameterElement;
316 var fieldType = _elementType(fieldElement.field); 317 var fieldType = _elementType(fieldElement.field);
317 if (!rules.isSubtypeOf(type, fieldType)) { 318 if (!rules.isSubtypeOf(type, fieldType)) {
318 var staticInfo = 319 _recordMessage(node, StrongModeCode.INVALID_PARAMETER_DECLARATION,
319 new InvalidParameterDeclaration(rules, node, fieldType); 320 [node, fieldType]);
320 _recordMessage(staticInfo);
321 } 321 }
322 } 322 }
323 node.visitChildren(this); 323 node.visitChildren(this);
324 } 324 }
325 325
326 @override 326 @override
327 void visitForEachStatement(ForEachStatement node) { 327 void visitForEachStatement(ForEachStatement node) {
328 var loopVariable = node.identifier ?? node.loopVariable?.identifier; 328 var loopVariable = node.identifier ?? node.loopVariable?.identifier;
329 329
330 // Safely handle malformed statements. 330 // Safely handle malformed statements.
331 if (loopVariable != null) { 331 if (loopVariable != null) {
332 // Find the element type of the sequence. 332 // Find the element type of the sequence.
333 var sequenceInterface = node.awaitKeyword != null 333 var sequenceInterface = node.awaitKeyword != null
334 ? typeProvider.streamType 334 ? typeProvider.streamType
335 : typeProvider.iterableType; 335 : typeProvider.iterableType;
336 var iterableType = _getStaticType(node.iterable); 336 var iterableType = _getStaticType(node.iterable);
337 var elementType = 337 var elementType =
338 rules.mostSpecificTypeArgument(iterableType, sequenceInterface); 338 rules.mostSpecificTypeArgument(iterableType, sequenceInterface);
339 339
340 // If the sequence is not an Iterable (or Stream for await for) but is a 340 // If the sequence is not an Iterable (or Stream for await for) but is a
341 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then 341 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then
342 // we'll do a separate cast of the dynamic element to the variable's type. 342 // we'll do a separate cast of the dynamic element to the variable's type.
343 if (elementType == null) { 343 if (elementType == null) {
344 var sequenceType = 344 var sequenceType =
345 sequenceInterface.instantiate([DynamicTypeImpl.instance]); 345 sequenceInterface.instantiate([DynamicTypeImpl.instance]);
346 346
347 if (rules.isSubtypeOf(sequenceType, iterableType)) { 347 if (rules.isSubtypeOf(sequenceType, iterableType)) {
348 _recordMessage(DownCast.create( 348 _recordImplicitCast(node.iterable, iterableType, sequenceType);
349 rules, node.iterable, iterableType, sequenceType, _options));
350 elementType = DynamicTypeImpl.instance; 349 elementType = DynamicTypeImpl.instance;
351 } 350 }
352 } 351 }
353 352
354 // If the sequence doesn't implement the interface at all, [ErrorVerifier] 353 // If the sequence doesn't implement the interface at all, [ErrorVerifier]
355 // will report the error, so ignore it here. 354 // will report the error, so ignore it here.
356 if (elementType != null) { 355 if (elementType != null) {
357 // Insert a cast from the sequence's element type to the loop variable's 356 // Insert a cast from the sequence's element type to the loop variable's
358 // if needed. 357 // if needed.
359 _checkDowncast(loopVariable, _getStaticType(loopVariable), 358 _checkDowncast(loopVariable, _getStaticType(loopVariable),
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 // dynamic d; 492 // dynamic d;
494 // d.someMethod(...); // the whole method call must be a dynamic send. 493 // d.someMethod(...); // the whole method call must be a dynamic send.
495 // 494 //
496 // ... from case like: 495 // ... from case like:
497 // 496 //
498 // SomeType s; 497 // SomeType s;
499 // s.someDynamicField(...); // static get, followed by dynamic call. 498 // s.someDynamicField(...); // static get, followed by dynamic call.
500 // 499 //
501 // The first case is handled here, the second case is handled below when 500 // The first case is handled here, the second case is handled below when
502 // we call [checkFunctionApplication]. 501 // we call [checkFunctionApplication].
503 DynamicInvoke.set(node.methodName, true); 502 setIsDynamicInvoke(node.methodName, true);
504 } else { 503 } else {
505 checkFunctionApplication(node, node.methodName, node.argumentList); 504 checkFunctionApplication(node, node.methodName, node.argumentList);
506 } 505 }
507 node.visitChildren(this); 506 node.visitChildren(this);
508 } 507 }
509 508
510 @override 509 @override
511 void visitPostfixExpression(PostfixExpression node) { 510 void visitPostfixExpression(PostfixExpression node) {
512 _checkUnary(node); 511 _checkUnary(node);
513 node.visitChildren(this); 512 node.visitChildren(this);
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
612 } else { 611 } else {
613 // Sanity check the operator. 612 // Sanity check the operator.
614 assert(methodElement.isOperator); 613 assert(methodElement.isOperator);
615 var functionType = methodElement.type; 614 var functionType = methodElement.type;
616 var paramTypes = functionType.normalParameterTypes; 615 var paramTypes = functionType.normalParameterTypes;
617 assert(paramTypes.length == 1); 616 assert(paramTypes.length == 1);
618 assert(functionType.namedParameterTypes.isEmpty); 617 assert(functionType.namedParameterTypes.isEmpty);
619 assert(functionType.optionalParameterTypes.isEmpty); 618 assert(functionType.optionalParameterTypes.isEmpty);
620 619
621 // Check the LHS type. 620 // Check the LHS type.
622 var staticInfo;
623 var rhsType = _getStaticType(expr.rightHandSide); 621 var rhsType = _getStaticType(expr.rightHandSide);
624 var lhsType = _getStaticType(expr.leftHandSide); 622 var lhsType = _getStaticType(expr.leftHandSide);
625 var returnType = rules.refineBinaryExpressionType( 623 var returnType = rules.refineBinaryExpressionType(
626 typeProvider, lhsType, op, rhsType, functionType.returnType); 624 typeProvider, lhsType, op, rhsType, functionType.returnType);
627 625
628 if (!rules.isSubtypeOf(returnType, lhsType)) { 626 if (!rules.isSubtypeOf(returnType, lhsType)) {
629 final numType = typeProvider.numType; 627 final numType = typeProvider.numType;
630 // Try to fix up the numerical case if possible. 628 // Try to fix up the numerical case if possible.
631 if (rules.isSubtypeOf(lhsType, numType) && 629 if (rules.isSubtypeOf(lhsType, numType) &&
632 rules.isSubtypeOf(lhsType, rhsType)) { 630 rules.isSubtypeOf(lhsType, rhsType)) {
633 // This is also slightly different from spec, but allows us to keep 631 // This is also slightly different from spec, but allows us to keep
634 // compound operators in the int += num and num += dynamic cases. 632 // compound operators in the int += num and num += dynamic cases.
635 staticInfo = DownCast.create( 633 _recordImplicitCast(expr.rightHandSide, rhsType, lhsType);
636 rules, expr.rightHandSide, rhsType, lhsType, _options);
637 rhsType = lhsType;
638 } else { 634 } else {
639 staticInfo = new StaticTypeError(expr, lhsType); 635 _recordMessage(expr, StrongModeCode.STATIC_TYPE_ERROR,
636 [expr, returnType, lhsType]);
640 } 637 }
641 _recordMessage(staticInfo); 638 } else {
642 } 639 // Check the RHS type.
643 640 //
644 // Check the rhs type 641 // This is only needed if we didn't already need a cast, and avoids
645 if (staticInfo is! CoercionInfo) { 642 // emitting two messages for the same expression.
646 var paramType = paramTypes.first; 643 _checkDowncast(expr.rightHandSide, paramTypes.first);
647 _checkDowncast(expr.rightHandSide, paramType);
648 } 644 }
649 } 645 }
650 } 646 }
651 647
652 /// Records a [DownCast] of [expr] from [from] to [to], if there is one. 648 /// Records a [DownCast] of [expr] from [from] to [to], if there is one.
653 /// 649 ///
654 /// If [from] is omitted, uses the static type of [expr]. 650 /// If [from] is omitted, uses the static type of [expr].
655 /// 651 ///
656 /// If [expr] does not require a downcast because it is not related to [to] 652 /// If [expr] does not require a downcast because it is not related to [to]
657 /// or is already a subtype of it, does nothing. 653 /// or is already a subtype of it, does nothing.
(...skipping 14 matching lines...) Expand all
672 // Note: a function type is never assignable to a class per the Dart 668 // Note: a function type is never assignable to a class per the Dart
673 // spec - even if it has a compatible call method. We disallow as 669 // spec - even if it has a compatible call method. We disallow as
674 // well for consistency. 670 // well for consistency.
675 if ((from is FunctionType && rules.getCallMethodType(to) != null) || 671 if ((from is FunctionType && rules.getCallMethodType(to) != null) ||
676 (to is FunctionType && rules.getCallMethodType(from) != null)) { 672 (to is FunctionType && rules.getCallMethodType(from) != null)) {
677 return; 673 return;
678 } 674 }
679 675
680 // Downcast if toT <: fromT 676 // Downcast if toT <: fromT
681 if (rules.isSubtypeOf(to, from)) { 677 if (rules.isSubtypeOf(to, from)) {
682 _recordMessage(DownCast.create(rules, expr, from, to, _options)); 678 _recordImplicitCast(expr, from, to);
683 return; 679 return;
684 } 680 }
685 681
686 // TODO(vsm): Once we have generic methods, we should delete this 682 // TODO(vsm): Once we have generic methods, we should delete this
687 // workaround. These sideways casts are always ones we warn about 683 // workaround. These sideways casts are always ones we warn about
688 // - i.e., we think they are likely to fail at runtime. 684 // - i.e., we think they are likely to fail at runtime.
689 // ------- 685 // -------
690 // Downcast if toT <===> fromT 686 // Downcast if toT <===> fromT
691 // The intention here is to allow casts that are sideways in the restricted 687 // The intention here is to allow casts that are sideways in the restricted
692 // type system, but allowed in the regular dart type system, since these 688 // type system, but allowed in the regular dart type system, since these
693 // are likely to succeed. The canonical example is List<dynamic> and 689 // are likely to succeed. The canonical example is List<dynamic> and
694 // Iterable<T> for some concrete T (e.g. Object). These are unrelated 690 // Iterable<T> for some concrete T (e.g. Object). These are unrelated
695 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. 691 // in the restricted system, but List<dynamic> <: Iterable<T> in dart.
696 if (from.isAssignableTo(to)) { 692 if (from.isAssignableTo(to)) {
697 _recordMessage(DownCast.create(rules, expr, from, to, _options)); 693 _recordImplicitCast(expr, from, to);
698 } 694 }
699 } 695 }
700 696
701 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { 697 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) {
702 if ((_isDynamicTarget(target) || field.staticElement == null) && 698 if ((_isDynamicTarget(target) || field.staticElement == null) &&
703 !_isObjectProperty(target, field)) { 699 !_isObjectProperty(target, field)) {
704 _recordDynamicInvoke(node, target); 700 _recordDynamicInvoke(node, target);
705 } 701 }
706 node.visitChildren(this); 702 node.visitChildren(this);
707 } 703 }
(...skipping 16 matching lines...) Expand all
724 actualType.element == futureType.element) { 720 actualType.element == futureType.element) {
725 type = futureType.instantiate([type]); 721 type = futureType.instantiate([type]);
726 } 722 }
727 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. 723 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null.
728 if (expression != null) checkAssignment(expression, type); 724 if (expression != null) checkAssignment(expression, type);
729 } 725 }
730 726
731 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { 727 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) {
732 var type = getType(typeName); 728 var type = getType(typeName);
733 if (!rules.isGroundType(type)) { 729 if (!rules.isGroundType(type)) {
734 _recordMessage(new NonGroundTypeCheckInfo(node, type)); 730 _recordMessage(node, StrongModeCode.NON_GROUND_TYPE_CHECK_INFO, [type]);
735 } 731 }
736 } 732 }
737 733
738 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { 734 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) {
739 var op = node.operator; 735 var op = node.operator;
740 if (op.isUserDefinableOperator || 736 if (op.isUserDefinableOperator ||
741 op.type == TokenType.PLUS_PLUS || 737 op.type == TokenType.PLUS_PLUS ||
742 op.type == TokenType.MINUS_MINUS) { 738 op.type == TokenType.MINUS_MINUS) {
743 if (_isDynamicTarget(node.operand)) { 739 if (_isDynamicTarget(node.operand)) {
744 _recordDynamicInvoke(node, node.operand); 740 _recordDynamicInvoke(node, node.operand);
745 } 741 }
746 // For ++ and --, even if it is not dynamic, we still need to check 742 // For ++ and --, even if it is not dynamic, we still need to check
747 // that the user defined method accepts an `int` as the RHS. 743 // that the user defined method accepts an `int` as the RHS.
748 // We assume Analyzer has done this already. 744 // We assume Analyzer has done this already.
749 } 745 }
750 } 746 }
751 747
748 /// Records an implicit cast for the [expression] from [fromType] to [toType].
749 ///
750 /// This will emit the appropriate error/warning/hint message as well as mark
751 /// the AST node.
752 void _recordImplicitCast(
753 Expression expression, DartType fromType, DartType toType) {
754 // toT <:_R fromT => to <: fromT
755 // NB: classes with call methods are subtypes of function
756 // types, but the function type is not assignable to the class
757 assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType));
758
759 // Inference "casts":
760 if (expression is Literal || expression is FunctionExpression) {
761 // fromT should be an exact type - this will almost certainly fail at
762 // runtime.
763 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR,
764 [expression, fromType, toType]);
765 return;
766 }
767
768 if (expression is InstanceCreationExpression) {
769 ConstructorElement e = expression.staticElement;
770 if (e == null || !e.isFactory) {
771 // fromT should be an exact type - this will almost certainly fail at
772 // runtime.
773
774 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR,
775 [expression, fromType, toType]);
776 return;
777 }
778 }
779
780 if (isKnownFunction(expression)) {
781 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR,
782 [expression, fromType, toType]);
783 return;
784 }
785
786 // TODO(vsm): Change this to an assert when we have generic methods and
787 // fix TypeRules._coerceTo to disallow implicit sideways casts.
788 bool downCastComposite = false;
789 if (!rules.isSubtypeOf(toType, fromType)) {
790 assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType));
791 downCastComposite = true;
792 }
793
794 // Composite cast: these are more likely to fail.
795 if (!rules.isGroundType(toType)) {
796 // This cast is (probably) due to our different treatment of dynamic.
797 // It may be more likely to fail at runtime.
798 if (fromType is InterfaceType) {
799 // For class types, we'd like to allow non-generic down casts, e.g.,
800 // Iterable<T> to List<T>. The intuition here is that raw (generic)
801 // casts are problematic, and we should complain about those.
802 var typeArgs = fromType.typeArguments;
803 downCastComposite =
804 typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic);
805 } else {
806 downCastComposite = true;
807 }
808 }
809
810 var parent = expression.parent;
811 ErrorCode errorCode;
812 if (downCastComposite) {
813 errorCode = StrongModeCode.DOWN_CAST_COMPOSITE;
814 } else if (fromType.isDynamic) {
815 errorCode = StrongModeCode.DYNAMIC_CAST;
816 } else if (parent is VariableDeclaration &&
817 parent.initializer == expression) {
818 errorCode = StrongModeCode.ASSIGNMENT_CAST;
819 } else {
820 errorCode = StrongModeCode.DOWN_CAST_IMPLICIT;
821 }
822
823 _recordMessage(expression, errorCode, [fromType, toType]);
824 setImplicitCast(expression, toType);
825 }
826
752 // Produce a coercion which coerces something of type fromT 827 // Produce a coercion which coerces something of type fromT
753 // to something of type toT. 828 // to something of type toT.
754 // Returns the error coercion if the types cannot be coerced 829 // Returns the error coercion if the types cannot be coerced
755 // according to our current criteria. 830 // according to our current criteria.
756 /// Gets the expected return type of the given function [body], either from 831 /// Gets the expected return type of the given function [body], either from
757 /// a normal return/yield, or from a yield*. 832 /// a normal return/yield, or from a yield*.
758 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { 833 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
759 FunctionType functionType; 834 FunctionType functionType;
760 var parent = body.parent; 835 var parent = body.parent;
761 if (parent is Declaration) { 836 if (parent is Declaration) {
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
804 } else { 879 } else {
805 // Malformed type - fallback on analyzer error. 880 // Malformed type - fallback on analyzer error.
806 return null; 881 return null;
807 } 882 }
808 } 883 }
809 884
810 DartType _getStaticType(Expression expr) { 885 DartType _getStaticType(Expression expr) {
811 DartType t = expr.staticType ?? DynamicTypeImpl.instance; 886 DartType t = expr.staticType ?? DynamicTypeImpl.instance;
812 887
813 // Remove fuzzy arrow if possible. 888 // Remove fuzzy arrow if possible.
814 if (t is FunctionType && StaticInfo.isKnownFunction(expr)) { 889 if (t is FunctionType && isKnownFunction(expr)) {
815 t = rules.functionTypeToConcreteType(typeProvider, t); 890 t = rules.functionTypeToConcreteType(typeProvider, t);
816 } 891 }
817 892
818 return t; 893 return t;
819 } 894 }
820 895
821 /// Given an expression, return its type assuming it is 896 /// Given an expression, return its type assuming it is
822 /// in the caller position of a call (that is, accounting 897 /// in the caller position of a call (that is, accounting
823 /// for the possibility of a call method). Returns null 898 /// for the possibility of a call method). Returns null
824 /// if expression is not statically callable. 899 /// if expression is not statically callable.
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
886 961
887 bool _isObjectMethod(Expression target, SimpleIdentifier id) { 962 bool _isObjectMethod(Expression target, SimpleIdentifier id) {
888 MethodElement element = typeProvider.objectType.element.getMethod(id.name); 963 MethodElement element = typeProvider.objectType.element.getMethod(id.name);
889 return (element != null && !element.isStatic); 964 return (element != null && !element.isStatic);
890 } 965 }
891 966
892 bool _isObjectProperty(Expression target, SimpleIdentifier id) { 967 bool _isObjectProperty(Expression target, SimpleIdentifier id) {
893 return _isObjectGetter(target, id) || _isObjectMethod(target, id); 968 return _isObjectGetter(target, id) || _isObjectMethod(target, id);
894 } 969 }
895 970
896 void _recordDynamicInvoke(AstNode node, AstNode target) { 971 void _recordDynamicInvoke(AstNode node, Expression target) {
897 if (_options.strongModeHints) { 972 _recordMessage(node, StrongModeCode.DYNAMIC_INVOKE, [node]);
898 reporter.onError(new DynamicInvoke(node).toAnalysisError());
899 }
900 // TODO(jmesserly): we may eventually want to record if the whole operation 973 // TODO(jmesserly): we may eventually want to record if the whole operation
901 // (node) was dynamic, rather than the target, but this is an easier fit 974 // (node) was dynamic, rather than the target, but this is an easier fit
902 // with what we used to do. 975 // with what we used to do.
903 DynamicInvoke.set(target, true); 976 setIsDynamicInvoke(target, true);
904 } 977 }
905 978
906 void _recordMessage(StaticInfo info) { 979 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) {
907 if (info == null) return; 980 var severity = errorCode.errorSeverity;
908 var error = info.toAnalysisError();
909 var severity = error.errorCode.errorSeverity;
910 if (severity == ErrorSeverity.ERROR) _failure = true; 981 if (severity == ErrorSeverity.ERROR) _failure = true;
911 if (severity != ErrorSeverity.INFO || _options.strongModeHints) { 982 if (severity != ErrorSeverity.INFO || _options.strongModeHints) {
983 int begin = node is AnnotatedNode
984 ? node.firstTokenAfterCommentAndMetadata.offset
985 : node.offset;
986 int length = node.end - begin;
987 var source = (node.root as CompilationUnit).element.source;
988 var error =
989 new AnalysisError(source, begin, length, errorCode, arguments);
912 reporter.onError(error); 990 reporter.onError(error);
913 } 991 }
992 }
993 }
914 994
915 if (info is CoercionInfo) { 995 bool isKnownFunction(Expression expression) {
916 // TODO(jmesserly): if we're run again on the same AST, we'll produce the 996 Element element = null;
917 // same annotations. This should be harmless. This might go away once 997 if (expression is FunctionExpression) {
918 // CodeChecker is integrated better with analyzer, as it will know that 998 return true;
919 // checking has already been performed. 999 } else if (expression is PropertyAccess) {
920 // assert(CoercionInfo.get(info.node) == null); 1000 element = expression.propertyName.staticElement;
921 CoercionInfo.set(info.node, info); 1001 } else if (expression is Identifier) {
922 } 1002 element = expression.staticElement;
923 } 1003 }
1004 // First class functions and static methods, where we know the original
1005 // declaration, will have an exact type, so we know a downcast will fail.
1006 return element is FunctionElement ||
1007 element is MethodElement && element.isStatic;
924 } 1008 }
925 1009
926 /// Checks for overriding declarations of fields and methods. This is used to 1010 /// Checks for overriding declarations of fields and methods. This is used to
927 /// check overrides between classes and superclasses, interfaces, and mixin 1011 /// check overrides between classes and superclasses, interfaces, and mixin
928 /// applications. 1012 /// applications.
929 class _OverrideChecker { 1013 class _OverrideChecker {
930 bool _failure = false;
931 final StrongTypeSystemImpl rules; 1014 final StrongTypeSystemImpl rules;
932 final TypeProvider _typeProvider; 1015 final TypeProvider _typeProvider;
933 final AnalysisErrorListener _reporter; 1016 final CodeChecker _checker;
934 1017
935 _OverrideChecker(this._typeProvider, this.rules, this._reporter); 1018 _OverrideChecker(CodeChecker checker)
1019 : _checker = checker,
1020 rules = checker.rules,
1021 _typeProvider = checker.typeProvider;
936 1022
937 void check(ClassDeclaration node) { 1023 void check(ClassDeclaration node) {
938 if (node.element.type.isObject) return; 1024 if (node.element.type.isObject) return;
939 _checkSuperOverrides(node); 1025 _checkSuperOverrides(node);
940 _checkMixinApplicationOverrides(node); 1026 _checkMixinApplicationOverrides(node);
941 _checkAllInterfaceOverrides(node); 1027 _checkAllInterfaceOverrides(node);
942 } 1028 }
943 1029
944 /// Checks that implementations correctly override all reachable interfaces. 1030 /// Checks that implementations correctly override all reachable interfaces.
945 /// In particular, we need to check these overrides for the definitions in 1031 /// In particular, we need to check these overrides for the definitions in
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
1189 FunctionType subType = _elementType(element); 1275 FunctionType subType = _elementType(element);
1190 // TODO(vsm): Test for generic 1276 // TODO(vsm): Test for generic
1191 FunctionType baseType = _getMemberType(type, element); 1277 FunctionType baseType = _getMemberType(type, element);
1192 if (baseType == null) return false; 1278 if (baseType == null) return false;
1193 1279
1194 if (isSubclass && element is PropertyAccessorElement) { 1280 if (isSubclass && element is PropertyAccessorElement) {
1195 // Disallow any overriding if the base class defines this member 1281 // Disallow any overriding if the base class defines this member
1196 // as a field. We effectively treat fields as final / non-virtual. 1282 // as a field. We effectively treat fields as final / non-virtual.
1197 PropertyInducingElement field = _getMemberField(type, element); 1283 PropertyInducingElement field = _getMemberField(type, element);
1198 if (field != null) { 1284 if (field != null) {
1199 _recordMessage(new InvalidFieldOverride( 1285 _checker._recordMessage(
1200 errorLocation, element, type, subType, baseType)); 1286 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [
1287 element.enclosingElement.name,
1288 element.name,
1289 subType,
1290 type,
1291 baseType
1292 ]);
1201 } 1293 }
1202 } 1294 }
1203 FunctionType concreteSubType = subType; 1295 FunctionType concreteSubType = subType;
1204 FunctionType concreteBaseType = baseType; 1296 FunctionType concreteBaseType = baseType;
1205 if (element is MethodElement) { 1297 if (element is MethodElement) {
1206 if (concreteSubType.typeFormals.isNotEmpty) { 1298 if (concreteSubType.typeFormals.isNotEmpty) {
1207 if (concreteBaseType.typeFormals.isEmpty) { 1299 if (concreteBaseType.typeFormals.isEmpty) {
1208 concreteSubType = rules.instantiateToBounds(concreteSubType); 1300 concreteSubType = rules.instantiateToBounds(concreteSubType);
1209 } 1301 }
1210 } 1302 }
1211 concreteSubType = 1303 concreteSubType =
1212 rules.typeToConcreteType(_typeProvider, concreteSubType); 1304 rules.typeToConcreteType(_typeProvider, concreteSubType);
1213 concreteBaseType = 1305 concreteBaseType =
1214 rules.typeToConcreteType(_typeProvider, concreteBaseType); 1306 rules.typeToConcreteType(_typeProvider, concreteBaseType);
1215 } 1307 }
1216 if (!rules.isSubtypeOf(concreteSubType, concreteBaseType)) { 1308 if (!rules.isSubtypeOf(concreteSubType, concreteBaseType)) {
1217 // See whether non-subtype cases fit one of our common patterns: 1309 // See whether non-subtype cases fit one of our common patterns:
1218 // 1310 //
1219 // Common pattern 1: Inferable return type (on getters and methods) 1311 // Common pattern 1: Inferable return type (on getters and methods)
1220 // class A { 1312 // class A {
1221 // int get foo => ...; 1313 // int get foo => ...;
1222 // String toString() { ... } 1314 // String toString() { ... }
1223 // } 1315 // }
1224 // class B extends A { 1316 // class B extends A {
1225 // get foo => e; // no type specified. 1317 // get foo => e; // no type specified.
1226 // toString() { ... } // no return type specified. 1318 // toString() { ... } // no return type specified.
1227 // } 1319 // }
1228 _recordMessage(new InvalidMethodOverride( 1320
1229 errorLocation, element, type, subType, baseType)); 1321 ErrorCode errorCode;
1322 if (errorLocation is ExtendsClause) {
1323 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE;
1324 } else if (errorLocation.parent is WithClause) {
1325 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN;
1326 } else {
1327 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE;
1328 }
1329
1330 _checker._recordMessage(errorLocation, errorCode, [
1331 element.enclosingElement.name,
1332 element.name,
1333 subType,
1334 type,
1335 baseType
1336 ]);
1230 } 1337 }
1231 return true; 1338 return true;
1232 } 1339 }
1233 1340
1234 /// Check overrides between a class and its superclasses and mixins. For 1341 /// Check overrides between a class and its superclasses and mixins. For
1235 /// example, in: 1342 /// example, in:
1236 /// 1343 ///
1237 /// A extends B with E, F 1344 /// A extends B with E, F
1238 /// 1345 ///
1239 /// we check A against B, B super classes, E, and F. 1346 /// we check A against B, B super classes, E, and F.
(...skipping 18 matching lines...) Expand all
1258 var current = node.element.type; 1365 var current = node.element.type;
1259 var visited = new Set<InterfaceType>(); 1366 var visited = new Set<InterfaceType>();
1260 do { 1367 do {
1261 visited.add(current); 1368 visited.add(current);
1262 current.mixins.reversed.forEach( 1369 current.mixins.reversed.forEach(
1263 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); 1370 (m) => _checkIndividualOverridesFromClass(node, m, seen, true));
1264 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); 1371 _checkIndividualOverridesFromClass(node, current.superclass, seen, true);
1265 current = current.superclass; 1372 current = current.superclass;
1266 } while (!current.isObject && !visited.contains(current)); 1373 } while (!current.isObject && !visited.contains(current));
1267 } 1374 }
1268
1269 void _recordMessage(StaticInfo info) {
1270 if (info == null) return;
1271 var error = info.toAnalysisError();
1272 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
1273 _reporter.onError(error);
1274 }
1275 } 1375 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698