OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // 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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |