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