| 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/src/generated/ast.dart'; | 10 import 'package:analyzer/src/generated/ast.dart'; |
| 11 import 'package:analyzer/src/generated/element.dart'; | 11 import 'package:analyzer/src/generated/element.dart'; |
| 12 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 12 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; | 13 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; |
| 14 import 'package:analyzer/src/generated/type_system.dart'; |
| 13 | 15 |
| 14 import 'info.dart'; | 16 import 'info.dart'; |
| 15 import 'rules.dart'; | |
| 16 | 17 |
| 17 /// Checks for overriding declarations of fields and methods. This is used to | 18 /// Checks for overriding declarations of fields and methods. This is used to |
| 18 /// check overrides between classes and superclasses, interfaces, and mixin | 19 /// check overrides between classes and superclasses, interfaces, and mixin |
| 19 /// applications. | 20 /// applications. |
| 20 class _OverrideChecker { | 21 class _OverrideChecker { |
| 21 bool _failure = false; | 22 bool _failure = false; |
| 22 final TypeRules _rules; | 23 final StrongTypeSystemImpl rules; |
| 23 final AnalysisErrorListener _reporter; | 24 final AnalysisErrorListener _reporter; |
| 24 | 25 |
| 25 _OverrideChecker(this._rules, this._reporter); | 26 _OverrideChecker(this.rules, this._reporter); |
| 26 | 27 |
| 27 void check(ClassDeclaration node) { | 28 void check(ClassDeclaration node) { |
| 28 if (node.element.type.isObject) return; | 29 if (node.element.type.isObject) return; |
| 29 _checkSuperOverrides(node); | 30 _checkSuperOverrides(node); |
| 30 _checkMixinApplicationOverrides(node); | 31 _checkMixinApplicationOverrides(node); |
| 31 _checkAllInterfaceOverrides(node); | 32 _checkAllInterfaceOverrides(node); |
| 32 } | 33 } |
| 33 | 34 |
| 34 /// Check overrides from mixin applications themselves. For example, in: | 35 /// Check overrides from mixin applications themselves. For example, in: |
| 35 /// | 36 /// |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 /// class A extends B with C implements E { ... } | 293 /// class A extends B with C implements E { ... } |
| 293 /// ^ | 294 /// ^ |
| 294 /// | 295 /// |
| 295 /// When checking for overrides from a type and it's super types, [node] is | 296 /// When checking for overrides from a type and it's super types, [node] is |
| 296 /// the AST node that defines [element]. This is used to determine whether the | 297 /// the AST node that defines [element]. This is used to determine whether the |
| 297 /// type of the element could be inferred from the types in the super classes. | 298 /// type of the element could be inferred from the types in the super classes. |
| 298 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, | 299 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, |
| 299 AstNode node, AstNode errorLocation, bool isSubclass) { | 300 AstNode node, AstNode errorLocation, bool isSubclass) { |
| 300 assert(!element.isStatic); | 301 assert(!element.isStatic); |
| 301 | 302 |
| 302 FunctionType subType = _rules.elementType(element); | 303 FunctionType subType = _elementType(element); |
| 303 // TODO(vsm): Test for generic | 304 // TODO(vsm): Test for generic |
| 304 FunctionType baseType = _getMemberType(type, element); | 305 FunctionType baseType = _getMemberType(type, element); |
| 305 if (baseType == null) return false; | 306 if (baseType == null) return false; |
| 306 | 307 |
| 307 if (isSubclass && element is PropertyAccessorElement) { | 308 if (isSubclass && element is PropertyAccessorElement) { |
| 308 // Disallow any overriding if the base class defines this member | 309 // Disallow any overriding if the base class defines this member |
| 309 // as a field. We effectively treat fields as final / non-virtual. | 310 // as a field. We effectively treat fields as final / non-virtual. |
| 310 PropertyInducingElement field = _getMemberField(type, element); | 311 PropertyInducingElement field = _getMemberField(type, element); |
| 311 if (field != null) { | 312 if (field != null) { |
| 312 _recordMessage(new InvalidFieldOverride( | 313 _recordMessage(new InvalidFieldOverride( |
| 313 errorLocation, element, type, subType, baseType)); | 314 errorLocation, element, type, subType, baseType)); |
| 314 } | 315 } |
| 315 } | 316 } |
| 316 if (!_rules.isAssignable(subType, baseType)) { | 317 if (!rules.isSubtypeOf(subType, baseType)) { |
| 317 // See whether non-assignable cases fit one of our common patterns: | 318 // See whether non-subtype cases fit one of our common patterns: |
| 318 // | 319 // |
| 319 // Common pattern 1: Inferable return type (on getters and methods) | 320 // Common pattern 1: Inferable return type (on getters and methods) |
| 320 // class A { | 321 // class A { |
| 321 // int get foo => ...; | 322 // int get foo => ...; |
| 322 // String toString() { ... } | 323 // String toString() { ... } |
| 323 // } | 324 // } |
| 324 // class B extends A { | 325 // class B extends A { |
| 325 // get foo => e; // no type specified. | 326 // get foo => e; // no type specified. |
| 326 // toString() { ... } // no return type specified. | 327 // toString() { ... } // no return type specified. |
| 327 // } | 328 // } |
| 328 _recordMessage(new InvalidMethodOverride( | 329 _recordMessage(new InvalidMethodOverride( |
| 329 errorLocation, element, type, subType, baseType)); | 330 errorLocation, element, type, subType, baseType)); |
| 330 } | 331 } |
| 331 return true; | 332 return true; |
| 332 } | 333 } |
| 333 | 334 |
| 334 void _recordMessage(StaticInfo info) { | 335 void _recordMessage(StaticInfo info) { |
| 335 if (info == null) return; | 336 if (info == null) return; |
| 336 var error = info.toAnalysisError(); | 337 var error = info.toAnalysisError(); |
| 337 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; | 338 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; |
| 338 _reporter.onError(error); | 339 _reporter.onError(error); |
| 339 } | 340 } |
| 340 } | 341 } |
| 341 | 342 |
| 342 /// Checks the body of functions and properties. | 343 /// Checks the body of functions and properties. |
| 343 class CodeChecker extends RecursiveAstVisitor { | 344 class CodeChecker extends RecursiveAstVisitor { |
| 344 final TypeRules rules; | 345 final StrongTypeSystemImpl rules; |
| 346 final TypeProvider typeProvider; |
| 345 final AnalysisErrorListener reporter; | 347 final AnalysisErrorListener reporter; |
| 346 final _OverrideChecker _overrideChecker; | 348 final _OverrideChecker _overrideChecker; |
| 347 final bool _hints; | 349 final bool _hints; |
| 348 | 350 |
| 349 bool _failure = false; | 351 bool _failure = false; |
| 350 bool get failure => _failure || _overrideChecker._failure; | 352 bool get failure => _failure || _overrideChecker._failure; |
| 351 | 353 |
| 352 void reset() { | 354 void reset() { |
| 353 _failure = false; | 355 _failure = false; |
| 354 _overrideChecker._failure = false; | 356 _overrideChecker._failure = false; |
| 355 } | 357 } |
| 356 | 358 |
| 357 CodeChecker(TypeRules rules, AnalysisErrorListener reporter, | 359 CodeChecker(this.typeProvider, StrongTypeSystemImpl rules, AnalysisErrorListen
er reporter, |
| 358 {bool hints: false}) | 360 {bool hints: false}) |
| 359 : rules = rules, | 361 : rules = rules, |
| 360 reporter = reporter, | 362 reporter = reporter, |
| 361 _hints = hints, | 363 _hints = hints, |
| 362 _overrideChecker = new _OverrideChecker(rules, reporter); | 364 _overrideChecker = new _OverrideChecker(rules, reporter); |
| 363 | 365 |
| 364 @override | 366 @override |
| 365 void visitComment(Comment node) { | 367 void visitComment(Comment node) { |
| 366 // skip, no need to do typechecking inside comments (they may contain | 368 // skip, no need to do typechecking inside comments (they may contain |
| 367 // comment references which would require resolution). | 369 // comment references which would require resolution). |
| (...skipping 28 matching lines...) Expand all Loading... |
| 396 if (node is SuperConstructorInvocation) { | 398 if (node is SuperConstructorInvocation) { |
| 397 _recordMessage(new InvalidSuperInvocation(node)); | 399 _recordMessage(new InvalidSuperInvocation(node)); |
| 398 } | 400 } |
| 399 } | 401 } |
| 400 } | 402 } |
| 401 | 403 |
| 402 @override | 404 @override |
| 403 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | 405 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 404 var field = node.fieldName; | 406 var field = node.fieldName; |
| 405 var element = field.staticElement; | 407 var element = field.staticElement; |
| 406 DartType staticType = rules.elementType(element); | 408 DartType staticType = _elementType(element); |
| 407 checkAssignment(node.expression, staticType); | 409 checkAssignment(node.expression, staticType); |
| 408 node.visitChildren(this); | 410 node.visitChildren(this); |
| 409 } | 411 } |
| 410 | 412 |
| 411 @override | 413 @override |
| 412 void visitForEachStatement(ForEachStatement node) { | 414 void visitForEachStatement(ForEachStatement node) { |
| 413 // Check that the expression is an Iterable. | 415 // Check that the expression is an Iterable. |
| 414 var expr = node.iterable; | 416 var expr = node.iterable; |
| 415 var iterableType = node.awaitKeyword != null | 417 var iterableType = node.awaitKeyword != null |
| 416 ? rules.provider.streamType | 418 ? typeProvider.streamType |
| 417 : rules.provider.iterableType; | 419 : typeProvider.iterableType; |
| 418 var loopVariable = node.identifier != null | 420 var loopVariable = node.identifier != null |
| 419 ? node.identifier | 421 ? node.identifier |
| 420 : node.loopVariable?.identifier; | 422 : node.loopVariable?.identifier; |
| 421 if (loopVariable != null) { | 423 if (loopVariable != null) { |
| 422 var iteratorType = loopVariable.staticType; | 424 var iteratorType = loopVariable.staticType; |
| 423 var checkedType = iterableType.substitute4([iteratorType]); | 425 var checkedType = iterableType.substitute4([iteratorType]); |
| 424 checkAssignment(expr, checkedType); | 426 checkAssignment(expr, checkedType); |
| 425 } | 427 } |
| 426 node.visitChildren(this); | 428 node.visitChildren(this); |
| 427 } | 429 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 456 void visitSwitchStatement(SwitchStatement node) { | 458 void visitSwitchStatement(SwitchStatement node) { |
| 457 // SwitchStatement defines a boolean conversion to check the result of the | 459 // SwitchStatement defines a boolean conversion to check the result of the |
| 458 // case value == the switch value, but in dev_compiler we require a boolean | 460 // case value == the switch value, but in dev_compiler we require a boolean |
| 459 // return type from an overridden == operator (because Object.==), so | 461 // return type from an overridden == operator (because Object.==), so |
| 460 // checking in SwitchStatement shouldn't be necessary. | 462 // checking in SwitchStatement shouldn't be necessary. |
| 461 node.visitChildren(this); | 463 node.visitChildren(this); |
| 462 } | 464 } |
| 463 | 465 |
| 464 @override | 466 @override |
| 465 void visitListLiteral(ListLiteral node) { | 467 void visitListLiteral(ListLiteral node) { |
| 466 var type = rules.provider.dynamicType; | 468 var type = DynamicTypeImpl.instance; |
| 467 if (node.typeArguments != null) { | 469 if (node.typeArguments != null) { |
| 468 var targs = node.typeArguments.arguments; | 470 var targs = node.typeArguments.arguments; |
| 469 if (targs.length > 0) type = targs[0].type; | 471 if (targs.length > 0) type = targs[0].type; |
| 470 } else if (node.staticType is InterfaceType) { | 472 } else if (node.staticType is InterfaceType) { |
| 471 InterfaceType listT = node.staticType; | 473 InterfaceType listT = node.staticType; |
| 472 var targs = listT.typeArguments; | 474 var targs = listT.typeArguments; |
| 473 if (targs != null && targs.length > 0) type = targs[0]; | 475 if (targs != null && targs.length > 0) type = targs[0]; |
| 474 } | 476 } |
| 475 var elements = node.elements; | 477 var elements = node.elements; |
| 476 for (int i = 0; i < elements.length; i++) { | 478 for (int i = 0; i < elements.length; i++) { |
| 477 checkArgument(elements[i], type); | 479 checkArgument(elements[i], type); |
| 478 } | 480 } |
| 479 super.visitListLiteral(node); | 481 super.visitListLiteral(node); |
| 480 } | 482 } |
| 481 | 483 |
| 482 @override | 484 @override |
| 483 void visitMapLiteral(MapLiteral node) { | 485 void visitMapLiteral(MapLiteral node) { |
| 484 var ktype = rules.provider.dynamicType; | 486 var ktype = DynamicTypeImpl.instance; |
| 485 var vtype = rules.provider.dynamicType; | 487 var vtype = DynamicTypeImpl.instance; |
| 486 if (node.typeArguments != null) { | 488 if (node.typeArguments != null) { |
| 487 var targs = node.typeArguments.arguments; | 489 var targs = node.typeArguments.arguments; |
| 488 if (targs.length > 0) ktype = targs[0].type; | 490 if (targs.length > 0) ktype = targs[0].type; |
| 489 if (targs.length > 1) vtype = targs[1].type; | 491 if (targs.length > 1) vtype = targs[1].type; |
| 490 } else if (node.staticType is InterfaceType) { | 492 } else if (node.staticType is InterfaceType) { |
| 491 InterfaceType mapT = node.staticType; | 493 InterfaceType mapT = node.staticType; |
| 492 var targs = mapT.typeArguments; | 494 var targs = mapT.typeArguments; |
| 493 if (targs != null) { | 495 if (targs != null) { |
| 494 if (targs.length > 0) ktype = targs[0]; | 496 if (targs.length > 0) ktype = targs[0]; |
| 495 if (targs.length > 1) vtype = targs[1]; | 497 if (targs.length > 1) vtype = targs[1]; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 514 if (element == null) { | 516 if (element == null) { |
| 515 if (type.parameters.length < len) { | 517 if (type.parameters.length < len) { |
| 516 // We found an argument mismatch, the analyzer will report this too, | 518 // We found an argument mismatch, the analyzer will report this too, |
| 517 // so no need to insert an error for this here. | 519 // so no need to insert an error for this here. |
| 518 continue; | 520 continue; |
| 519 } | 521 } |
| 520 element = type.parameters[i]; | 522 element = type.parameters[i]; |
| 521 // TODO(vsm): When can this happen? | 523 // TODO(vsm): When can this happen? |
| 522 assert(element != null); | 524 assert(element != null); |
| 523 } | 525 } |
| 524 DartType expectedType = rules.elementType(element); | 526 DartType expectedType = _elementType(element); |
| 525 if (expectedType == null) expectedType = rules.provider.dynamicType; | 527 if (expectedType == null) expectedType = DynamicTypeImpl.instance; |
| 526 checkArgument(arg, expectedType); | 528 checkArgument(arg, expectedType); |
| 527 } | 529 } |
| 528 } | 530 } |
| 529 | 531 |
| 530 void checkArgument(Expression arg, DartType expectedType) { | 532 void checkArgument(Expression arg, DartType expectedType) { |
| 531 // Preserve named argument structure, so their immediate parent is the | 533 // Preserve named argument structure, so their immediate parent is the |
| 532 // method invocation. | 534 // method invocation. |
| 533 if (arg is NamedExpression) { | 535 if (arg is NamedExpression) { |
| 534 arg = (arg as NamedExpression).expression; | 536 arg = (arg as NamedExpression).expression; |
| 535 } | 537 } |
| 536 checkAssignment(arg, expectedType); | 538 checkAssignment(arg, expectedType); |
| 537 } | 539 } |
| 538 | 540 |
| 539 void checkFunctionApplication( | 541 void checkFunctionApplication( |
| 540 Expression node, Expression f, ArgumentList list) { | 542 Expression node, Expression f, ArgumentList list) { |
| 541 if (rules.isDynamicCall(f)) { | 543 if (_isDynamicCall(f)) { |
| 542 // If f is Function and this is a method invocation, we should have | 544 // If f is Function and this is a method invocation, we should have |
| 543 // gotten an analyzer error, so no need to issue another error. | 545 // gotten an analyzer error, so no need to issue another error. |
| 544 _recordDynamicInvoke(node, f); | 546 _recordDynamicInvoke(node, f); |
| 545 } else { | 547 } else { |
| 546 checkArgumentList(list, rules.getTypeAsCaller(f)); | 548 checkArgumentList(list, _getTypeAsCaller(f)); |
| 547 } | 549 } |
| 548 } | 550 } |
| 549 | 551 |
| 550 @override | 552 @override |
| 551 visitMethodInvocation(MethodInvocation node) { | 553 visitMethodInvocation(MethodInvocation node) { |
| 552 var target = node.realTarget; | 554 var target = node.realTarget; |
| 553 if (rules.isDynamicTarget(target) && | 555 if (_isDynamicTarget(target) && |
| 554 !_isObjectMethod(node, node.methodName)) { | 556 !_isObjectMethod(node, node.methodName)) { |
| 555 _recordDynamicInvoke(node, target); | 557 _recordDynamicInvoke(node, target); |
| 556 | 558 |
| 557 // Mark the tear-off as being dynamic, too. This lets us distinguish | 559 // Mark the tear-off as being dynamic, too. This lets us distinguish |
| 558 // cases like: | 560 // cases like: |
| 559 // | 561 // |
| 560 // dynamic d; | 562 // dynamic d; |
| 561 // d.someMethod(...); // the whole method call must be a dynamic send. | 563 // d.someMethod(...); // the whole method call must be a dynamic send. |
| 562 // | 564 // |
| 563 // ... from case like: | 565 // ... from case like: |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 599 if (element != null) { | 601 if (element != null) { |
| 600 var type = node.staticElement.type; | 602 var type = node.staticElement.type; |
| 601 checkArgumentList(node.argumentList, type); | 603 checkArgumentList(node.argumentList, type); |
| 602 } | 604 } |
| 603 node.visitChildren(this); | 605 node.visitChildren(this); |
| 604 } | 606 } |
| 605 | 607 |
| 606 void _checkReturnOrYield(Expression expression, AstNode node, | 608 void _checkReturnOrYield(Expression expression, AstNode node, |
| 607 {bool yieldStar: false}) { | 609 {bool yieldStar: false}) { |
| 608 var body = node.getAncestor((n) => n is FunctionBody); | 610 var body = node.getAncestor((n) => n is FunctionBody); |
| 609 var type = rules.getExpectedReturnType(body, yieldStar: yieldStar); | 611 var type = _getExpectedReturnType(body, yieldStar: yieldStar); |
| 610 if (type == null) { | 612 if (type == null) { |
| 611 // We have a type mismatch: the async/async*/sync* modifier does | 613 // We have a type mismatch: the async/async*/sync* modifier does |
| 612 // not match the return or yield type. We should have already gotten an | 614 // not match the return or yield type. We should have already gotten an |
| 613 // analyzer error in this case. | 615 // analyzer error in this case. |
| 614 return; | 616 return; |
| 615 } | 617 } |
| 616 InterfaceType futureType = rules.provider.futureType; | 618 InterfaceType futureType = typeProvider.futureType; |
| 617 DartType actualType = expression.staticType; | 619 DartType actualType = expression.staticType; |
| 618 if (body.isAsynchronous && | 620 if (body.isAsynchronous && |
| 619 !body.isGenerator && | 621 !body.isGenerator && |
| 620 actualType is InterfaceType && | 622 actualType is InterfaceType && |
| 621 actualType.element == futureType.element) { | 623 actualType.element == futureType.element) { |
| 622 type = futureType.substitute4([type]); | 624 type = futureType.substitute4([type]); |
| 623 } | 625 } |
| 624 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. | 626 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. |
| 625 if (expression != null) checkAssignment(expression, type); | 627 if (expression != null) checkAssignment(expression, type); |
| 626 } | 628 } |
| 627 | 629 |
| 630 /// Gets the expected return type of the given function [body], either from |
| 631 /// a normal return/yield, or from a yield*. |
| 632 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
| 633 FunctionType functionType; |
| 634 var parent = body.parent; |
| 635 if (parent is Declaration) { |
| 636 functionType = _elementType(parent.element); |
| 637 } else { |
| 638 assert(parent is FunctionExpression); |
| 639 functionType = parent.staticType ?? DynamicTypeImpl.instance; |
| 640 } |
| 641 |
| 642 var type = functionType.returnType; |
| 643 |
| 644 InterfaceType expectedType = null; |
| 645 if (body.isAsynchronous) { |
| 646 if (body.isGenerator) { |
| 647 // Stream<T> -> T |
| 648 expectedType = typeProvider.streamType; |
| 649 } else { |
| 650 // Future<T> -> T |
| 651 // TODO(vsm): Revisit with issue #228. |
| 652 expectedType = typeProvider.futureType; |
| 653 } |
| 654 } else { |
| 655 if (body.isGenerator) { |
| 656 // Iterable<T> -> T |
| 657 expectedType = typeProvider.iterableType; |
| 658 } else { |
| 659 // T -> T |
| 660 return type; |
| 661 } |
| 662 } |
| 663 if (yieldStar) { |
| 664 if (type.isDynamic) { |
| 665 // Ensure it's at least a Stream / Iterable. |
| 666 return expectedType.substitute4([typeProvider.dynamicType]); |
| 667 } else { |
| 668 // Analyzer will provide a separate error if expected type |
| 669 // is not compatible with type. |
| 670 return type; |
| 671 } |
| 672 } |
| 673 if (type.isDynamic) { |
| 674 return type; |
| 675 } else if (type is InterfaceType && type.element == expectedType.element) { |
| 676 return type.typeArguments[0]; |
| 677 } else { |
| 678 // Malformed type - fallback on analyzer error. |
| 679 return null; |
| 680 } |
| 681 } |
| 682 |
| 628 @override | 683 @override |
| 629 void visitExpressionFunctionBody(ExpressionFunctionBody node) { | 684 void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 630 _checkReturnOrYield(node.expression, node); | 685 _checkReturnOrYield(node.expression, node); |
| 631 node.visitChildren(this); | 686 node.visitChildren(this); |
| 632 } | 687 } |
| 633 | 688 |
| 634 @override | 689 @override |
| 635 void visitReturnStatement(ReturnStatement node) { | 690 void visitReturnStatement(ReturnStatement node) { |
| 636 _checkReturnOrYield(node.expression, node); | 691 _checkReturnOrYield(node.expression, node); |
| 637 node.visitChildren(this); | 692 node.visitChildren(this); |
| 638 } | 693 } |
| 639 | 694 |
| 640 @override | 695 @override |
| 641 void visitYieldStatement(YieldStatement node) { | 696 void visitYieldStatement(YieldStatement node) { |
| 642 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); | 697 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); |
| 643 node.visitChildren(this); | 698 node.visitChildren(this); |
| 644 } | 699 } |
| 645 | 700 |
| 646 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { | 701 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { |
| 647 if ((rules.isDynamicTarget(target) || field.staticElement == null) && | 702 if ((_isDynamicTarget(target) || field.staticElement == null) && |
| 648 !_isObjectProperty(target, field)) { | 703 !_isObjectProperty(target, field)) { |
| 649 _recordDynamicInvoke(node, target); | 704 _recordDynamicInvoke(node, target); |
| 650 } | 705 } |
| 651 node.visitChildren(this); | 706 node.visitChildren(this); |
| 652 } | 707 } |
| 653 | 708 |
| 654 @override | 709 @override |
| 655 void visitPropertyAccess(PropertyAccess node) { | 710 void visitPropertyAccess(PropertyAccess node) { |
| 656 _checkFieldAccess(node, node.realTarget, node.propertyName); | 711 _checkFieldAccess(node, node.realTarget, node.propertyName); |
| 657 } | 712 } |
| 658 | 713 |
| 659 @override | 714 @override |
| 660 void visitPrefixedIdentifier(PrefixedIdentifier node) { | 715 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 661 _checkFieldAccess(node, node.prefix, node.identifier); | 716 _checkFieldAccess(node, node.prefix, node.identifier); |
| 662 } | 717 } |
| 663 | 718 |
| 664 @override | 719 @override |
| 665 void visitDefaultFormalParameter(DefaultFormalParameter node) { | 720 void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 666 // Check that defaults have the proper subtype. | 721 // Check that defaults have the proper subtype. |
| 667 var parameter = node.parameter; | 722 var parameter = node.parameter; |
| 668 var parameterType = rules.elementType(parameter.element); | 723 var parameterType = _elementType(parameter.element); |
| 669 assert(parameterType != null); | 724 assert(parameterType != null); |
| 670 var defaultValue = node.defaultValue; | 725 var defaultValue = node.defaultValue; |
| 671 if (defaultValue != null) { | 726 if (defaultValue != null) { |
| 672 checkAssignment(defaultValue, parameterType); | 727 checkAssignment(defaultValue, parameterType); |
| 673 } | 728 } |
| 674 | 729 |
| 675 node.visitChildren(this); | 730 node.visitChildren(this); |
| 676 } | 731 } |
| 677 | 732 |
| 678 @override | 733 @override |
| 679 void visitFieldFormalParameter(FieldFormalParameter node) { | 734 void visitFieldFormalParameter(FieldFormalParameter node) { |
| 680 var element = node.element; | 735 var element = node.element; |
| 681 var typeName = node.type; | 736 var typeName = node.type; |
| 682 if (typeName != null) { | 737 if (typeName != null) { |
| 683 var type = rules.elementType(element); | 738 var type = _elementType(element); |
| 684 var fieldElement = | 739 var fieldElement = |
| 685 node.identifier.staticElement as FieldFormalParameterElement; | 740 node.identifier.staticElement as FieldFormalParameterElement; |
| 686 var fieldType = rules.elementType(fieldElement.field); | 741 var fieldType = _elementType(fieldElement.field); |
| 687 if (!rules.isSubTypeOf(type, fieldType)) { | 742 if (!rules.isSubtypeOf(type, fieldType)) { |
| 688 var staticInfo = | 743 var staticInfo = |
| 689 new InvalidParameterDeclaration(rules, node, fieldType); | 744 new InvalidParameterDeclaration(rules, node, fieldType); |
| 690 _recordMessage(staticInfo); | 745 _recordMessage(staticInfo); |
| 691 } | 746 } |
| 692 } | 747 } |
| 693 node.visitChildren(this); | 748 node.visitChildren(this); |
| 694 } | 749 } |
| 695 | 750 |
| 696 @override | 751 @override |
| 697 void visitInstanceCreationExpression(InstanceCreationExpression node) { | 752 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 698 var arguments = node.argumentList; | 753 var arguments = node.argumentList; |
| 699 var element = node.staticElement; | 754 var element = node.staticElement; |
| 700 if (element != null) { | 755 if (element != null) { |
| 701 var type = rules.elementType(node.staticElement); | 756 var type = _elementType(node.staticElement); |
| 702 checkArgumentList(arguments, type); | 757 checkArgumentList(arguments, type); |
| 703 } | 758 } |
| 704 node.visitChildren(this); | 759 node.visitChildren(this); |
| 705 } | 760 } |
| 706 | 761 |
| 707 @override | 762 @override |
| 708 void visitVariableDeclarationList(VariableDeclarationList node) { | 763 void visitVariableDeclarationList(VariableDeclarationList node) { |
| 709 TypeName type = node.type; | 764 TypeName type = node.type; |
| 710 if (type == null) { | 765 if (type == null) { |
| 711 // No checks are needed when the type is var. Although internally the | 766 // No checks are needed when the type is var. Although internally the |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 758 void visitPostfixExpression(PostfixExpression node) { | 813 void visitPostfixExpression(PostfixExpression node) { |
| 759 _checkUnary(node); | 814 _checkUnary(node); |
| 760 node.visitChildren(this); | 815 node.visitChildren(this); |
| 761 } | 816 } |
| 762 | 817 |
| 763 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { | 818 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { |
| 764 var op = node.operator; | 819 var op = node.operator; |
| 765 if (op.isUserDefinableOperator || | 820 if (op.isUserDefinableOperator || |
| 766 op.type == TokenType.PLUS_PLUS || | 821 op.type == TokenType.PLUS_PLUS || |
| 767 op.type == TokenType.MINUS_MINUS) { | 822 op.type == TokenType.MINUS_MINUS) { |
| 768 if (rules.isDynamicTarget(node.operand)) { | 823 if (_isDynamicTarget(node.operand)) { |
| 769 _recordDynamicInvoke(node, node.operand); | 824 _recordDynamicInvoke(node, node.operand); |
| 770 } | 825 } |
| 771 // For ++ and --, even if it is not dynamic, we still need to check | 826 // For ++ and --, even if it is not dynamic, we still need to check |
| 772 // that the user defined method accepts an `int` as the RHS. | 827 // that the user defined method accepts an `int` as the RHS. |
| 773 // We assume Analyzer has done this already. | 828 // We assume Analyzer has done this already. |
| 774 } | 829 } |
| 775 } | 830 } |
| 776 | 831 |
| 777 @override | 832 @override |
| 778 void visitBinaryExpression(BinaryExpression node) { | 833 void visitBinaryExpression(BinaryExpression node) { |
| 779 var op = node.operator; | 834 var op = node.operator; |
| 780 if (op.isUserDefinableOperator) { | 835 if (op.isUserDefinableOperator) { |
| 781 if (rules.isDynamicTarget(node.leftOperand)) { | 836 if (_isDynamicTarget(node.leftOperand)) { |
| 782 // Dynamic invocation | 837 // Dynamic invocation |
| 783 // TODO(vsm): Move this logic to the resolver? | 838 // TODO(vsm): Move this logic to the resolver? |
| 784 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { | 839 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { |
| 785 _recordDynamicInvoke(node, node.leftOperand); | 840 _recordDynamicInvoke(node, node.leftOperand); |
| 786 } | 841 } |
| 787 } else { | 842 } else { |
| 788 var element = node.staticElement; | 843 var element = node.staticElement; |
| 789 // Method invocation. | 844 // Method invocation. |
| 790 if (element is MethodElement) { | 845 if (element is MethodElement) { |
| 791 var type = element.type; | 846 var type = element.type; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 819 | 874 |
| 820 @override | 875 @override |
| 821 void visitConditionalExpression(ConditionalExpression node) { | 876 void visitConditionalExpression(ConditionalExpression node) { |
| 822 checkBoolean(node.condition); | 877 checkBoolean(node.condition); |
| 823 node.visitChildren(this); | 878 node.visitChildren(this); |
| 824 } | 879 } |
| 825 | 880 |
| 826 @override | 881 @override |
| 827 void visitIndexExpression(IndexExpression node) { | 882 void visitIndexExpression(IndexExpression node) { |
| 828 var target = node.realTarget; | 883 var target = node.realTarget; |
| 829 if (rules.isDynamicTarget(target)) { | 884 if (_isDynamicTarget(target)) { |
| 830 _recordDynamicInvoke(node, target); | 885 _recordDynamicInvoke(node, target); |
| 831 } else { | 886 } else { |
| 832 var element = node.staticElement; | 887 var element = node.staticElement; |
| 833 if (element is MethodElement) { | 888 if (element is MethodElement) { |
| 834 var type = element.type; | 889 var type = element.type; |
| 835 // Analyzer should enforce number of parameter types, but check in | 890 // Analyzer should enforce number of parameter types, but check in |
| 836 // case we have erroneous input. | 891 // case we have erroneous input. |
| 837 if (type.normalParameterTypes.isNotEmpty) { | 892 if (type.normalParameterTypes.isNotEmpty) { |
| 838 checkArgument(node.index, type.normalParameterTypes[0]); | 893 checkArgument(node.index, type.normalParameterTypes[0]); |
| 839 } | 894 } |
| 840 } else { | 895 } else { |
| 841 // TODO(vsm): Assert that the analyzer found an error here? | 896 // TODO(vsm): Assert that the analyzer found an error here? |
| 842 } | 897 } |
| 843 } | 898 } |
| 844 node.visitChildren(this); | 899 node.visitChildren(this); |
| 845 } | 900 } |
| 846 | 901 |
| 847 DartType getType(TypeName name) { | 902 DartType getType(TypeName name) { |
| 848 return (name == null) ? rules.provider.dynamicType : name.type; | 903 return (name == null) ? DynamicTypeImpl.instance : name.type; |
| 849 } | 904 } |
| 850 | 905 |
| 851 /// Analyzer checks boolean conversions, but we need to check too, because | 906 /// Analyzer checks boolean conversions, but we need to check too, because |
| 852 /// it uses the default assignability rules that allow `dynamic` and `Object` | 907 /// it uses the default assignability rules that allow `dynamic` and `Object` |
| 853 /// to be assigned to bool with no message. | 908 /// to be assigned to bool with no message. |
| 854 void checkBoolean(Expression expr) => | 909 void checkBoolean(Expression expr) => |
| 855 checkAssignment(expr, rules.provider.boolType); | 910 checkAssignment(expr, typeProvider.boolType); |
| 856 | 911 |
| 857 void checkAssignment(Expression expr, DartType type) { | 912 void checkAssignment(Expression expr, DartType type) { |
| 858 if (expr is ParenthesizedExpression) { | 913 if (expr is ParenthesizedExpression) { |
| 859 checkAssignment(expr.expression, type); | 914 checkAssignment(expr.expression, type); |
| 860 } else { | 915 } else { |
| 861 _recordMessage(rules.checkAssignment(expr, type)); | 916 _recordMessage(_checkAssignment(expr, type)); |
| 862 } | 917 } |
| 863 } | 918 } |
| 864 | 919 |
| 920 StaticInfo _checkAssignment(Expression expr, DartType toT) { |
| 921 final fromT = expr.staticType ?? DynamicTypeImpl.instance; |
| 922 final Coercion c = _coerceTo(fromT, toT); |
| 923 if (c is Identity) return null; |
| 924 if (c is CoercionError) return new StaticTypeError(rules, expr, toT); |
| 925 var reason = null; |
| 926 |
| 927 var errors = <String>[]; |
| 928 |
| 929 var ok = _inferExpression(expr, toT, errors); |
| 930 if (ok) return InferredType.create(rules, expr, toT); |
| 931 reason = (errors.isNotEmpty) ? errors.first : null; |
| 932 |
| 933 if (c is Cast) return DownCast.create(rules, expr, c, reason: reason); |
| 934 assert(false); |
| 935 return null; |
| 936 } |
| 937 |
| 938 /// Checks if we can perform downwards inference on [e] tp get type [t]. |
| 939 /// If it is not possible, this will add a message to [errors]. |
| 940 bool _inferExpression(Expression e, DartType t, List<String> errors) { |
| 941 DartType staticType = e.staticType ?? DynamicTypeImpl.instance; |
| 942 if (rules.isSubtypeOf(staticType, t)) { |
| 943 return true; |
| 944 } |
| 945 errors.add("$e cannot be typed as $t"); |
| 946 return false; |
| 947 } |
| 948 |
| 949 // Produce a coercion which coerces something of type fromT |
| 950 // to something of type toT. |
| 951 // Returns the error coercion if the types cannot be coerced |
| 952 // according to our current criteria. |
| 953 Coercion _coerceTo(DartType fromT, DartType toT) { |
| 954 // We can use anything as void |
| 955 if (toT.isVoid) return Coercion.identity(toT); |
| 956 |
| 957 // fromT <: toT, no coercion needed |
| 958 if (rules.isSubtypeOf(fromT, toT)) return Coercion.identity(toT); |
| 959 |
| 960 // TODO(vsm): We can get rid of the second clause if we disallow |
| 961 // all sideways casts - see TODO below. |
| 962 // ------- |
| 963 // Note: a function type is never assignable to a class per the Dart |
| 964 // spec - even if it has a compatible call method. We disallow as |
| 965 // well for consistency. |
| 966 if ((fromT is FunctionType && rules.getCallMethodType(toT) != null) || |
| 967 (toT is FunctionType && rules.getCallMethodType(fromT) != null)) { |
| 968 return Coercion.error(); |
| 969 } |
| 970 |
| 971 // Downcast if toT <: fromT |
| 972 if (rules.isSubtypeOf(toT, fromT)) return Coercion.cast(fromT, toT); |
| 973 |
| 974 // TODO(vsm): Once we have generic methods, we should delete this |
| 975 // workaround. These sideways casts are always ones we warn about |
| 976 // - i.e., we think they are likely to fail at runtime. |
| 977 // ------- |
| 978 // Downcast if toT <===> fromT |
| 979 // The intention here is to allow casts that are sideways in the restricted |
| 980 // type system, but allowed in the regular dart type system, since these |
| 981 // are likely to succeed. The canonical example is List<dynamic> and |
| 982 // Iterable<T> for some concrete T (e.g. Object). These are unrelated |
| 983 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. |
| 984 if (fromT.isAssignableTo(toT)) { |
| 985 return Coercion.cast(fromT, toT); |
| 986 } |
| 987 |
| 988 return Coercion.error(); |
| 989 } |
| 990 |
| 865 DartType _specializedBinaryReturnType( | 991 DartType _specializedBinaryReturnType( |
| 866 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { | 992 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { |
| 867 // This special cases binary return types as per 16.26 and 16.27 of the | 993 // This special cases binary return types as per 16.26 and 16.27 of the |
| 868 // Dart language spec. | 994 // Dart language spec. |
| 869 switch (op) { | 995 switch (op) { |
| 870 case TokenType.PLUS: | 996 case TokenType.PLUS: |
| 871 case TokenType.MINUS: | 997 case TokenType.MINUS: |
| 872 case TokenType.STAR: | 998 case TokenType.STAR: |
| 873 case TokenType.TILDE_SLASH: | 999 case TokenType.TILDE_SLASH: |
| 874 case TokenType.PERCENT: | 1000 case TokenType.PERCENT: |
| 875 case TokenType.PLUS_EQ: | 1001 case TokenType.PLUS_EQ: |
| 876 case TokenType.MINUS_EQ: | 1002 case TokenType.MINUS_EQ: |
| 877 case TokenType.STAR_EQ: | 1003 case TokenType.STAR_EQ: |
| 878 case TokenType.TILDE_SLASH_EQ: | 1004 case TokenType.TILDE_SLASH_EQ: |
| 879 case TokenType.PERCENT_EQ: | 1005 case TokenType.PERCENT_EQ: |
| 880 if (t1 == rules.provider.intType && | 1006 if (t1 == typeProvider.intType && |
| 881 t2 == rules.provider.intType) return t1; | 1007 t2 == typeProvider.intType) return t1; |
| 882 if (t1 == rules.provider.doubleType && | 1008 if (t1 == typeProvider.doubleType && |
| 883 t2 == rules.provider.doubleType) return t1; | 1009 t2 == typeProvider.doubleType) return t1; |
| 884 // This particular combo is not spelled out in the spec, but all | 1010 // This particular combo is not spelled out in the spec, but all |
| 885 // implementations and analyzer seem to follow this. | 1011 // implementations and analyzer seem to follow this. |
| 886 if (t1 == rules.provider.doubleType && | 1012 if (t1 == typeProvider.doubleType && |
| 887 t2 == rules.provider.intType) return t1; | 1013 t2 == typeProvider.intType) return t1; |
| 888 } | 1014 } |
| 889 return normalReturnType; | 1015 return normalReturnType; |
| 890 } | 1016 } |
| 891 | 1017 |
| 892 void _checkCompoundAssignment(AssignmentExpression expr) { | 1018 void _checkCompoundAssignment(AssignmentExpression expr) { |
| 893 var op = expr.operator.type; | 1019 var op = expr.operator.type; |
| 894 assert(op.isAssignmentOperator && op != TokenType.EQ); | 1020 assert(op.isAssignmentOperator && op != TokenType.EQ); |
| 895 var methodElement = expr.staticElement; | 1021 var methodElement = expr.staticElement; |
| 896 if (methodElement == null) { | 1022 if (methodElement == null) { |
| 897 // Dynamic invocation | 1023 // Dynamic invocation |
| 898 _recordDynamicInvoke(expr, expr.leftHandSide); | 1024 _recordDynamicInvoke(expr, expr.leftHandSide); |
| 899 } else { | 1025 } else { |
| 900 // Sanity check the operator | 1026 // Sanity check the operator |
| 901 assert(methodElement.isOperator); | 1027 assert(methodElement.isOperator); |
| 902 var functionType = methodElement.type; | 1028 var functionType = methodElement.type; |
| 903 var paramTypes = functionType.normalParameterTypes; | 1029 var paramTypes = functionType.normalParameterTypes; |
| 904 assert(paramTypes.length == 1); | 1030 assert(paramTypes.length == 1); |
| 905 assert(functionType.namedParameterTypes.isEmpty); | 1031 assert(functionType.namedParameterTypes.isEmpty); |
| 906 assert(functionType.optionalParameterTypes.isEmpty); | 1032 assert(functionType.optionalParameterTypes.isEmpty); |
| 907 | 1033 |
| 908 // Check the lhs type | 1034 // Check the lhs type |
| 909 var staticInfo; | 1035 var staticInfo; |
| 910 var rhsType = _getStaticType(expr.rightHandSide); | 1036 var rhsType = _getStaticType(expr.rightHandSide); |
| 911 var lhsType = _getStaticType(expr.leftHandSide); | 1037 var lhsType = _getStaticType(expr.leftHandSide); |
| 912 var returnType = _specializedBinaryReturnType( | 1038 var returnType = _specializedBinaryReturnType( |
| 913 op, lhsType, rhsType, functionType.returnType); | 1039 op, lhsType, rhsType, functionType.returnType); |
| 914 | 1040 |
| 915 if (!rules.isSubTypeOf(returnType, lhsType)) { | 1041 if (!rules.isSubtypeOf(returnType, lhsType)) { |
| 916 final numType = rules.provider.numType; | 1042 final numType = typeProvider.numType; |
| 917 // Try to fix up the numerical case if possible. | 1043 // Try to fix up the numerical case if possible. |
| 918 if (rules.isSubTypeOf(lhsType, numType) && | 1044 if (rules.isSubtypeOf(lhsType, numType) && |
| 919 rules.isSubTypeOf(lhsType, rhsType)) { | 1045 rules.isSubtypeOf(lhsType, rhsType)) { |
| 920 // This is also slightly different from spec, but allows us to keep | 1046 // This is also slightly different from spec, but allows us to keep |
| 921 // compound operators in the int += num and num += dynamic cases. | 1047 // compound operators in the int += num and num += dynamic cases. |
| 922 staticInfo = DownCast.create( | 1048 staticInfo = DownCast.create( |
| 923 rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType)); | 1049 rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType)); |
| 924 rhsType = lhsType; | 1050 rhsType = lhsType; |
| 925 } else { | 1051 } else { |
| 926 // Static type error | 1052 // Static type error |
| 927 staticInfo = new StaticTypeError(rules, expr, lhsType); | 1053 staticInfo = new StaticTypeError(rules, expr, lhsType); |
| 928 } | 1054 } |
| 929 _recordMessage(staticInfo); | 1055 _recordMessage(staticInfo); |
| 930 } | 1056 } |
| 931 | 1057 |
| 932 // Check the rhs type | 1058 // Check the rhs type |
| 933 if (staticInfo is! CoercionInfo) { | 1059 if (staticInfo is! CoercionInfo) { |
| 934 var paramType = paramTypes.first; | 1060 var paramType = paramTypes.first; |
| 935 staticInfo = rules.checkAssignment(expr.rightHandSide, paramType); | 1061 staticInfo = _checkAssignment(expr.rightHandSide, paramType); |
| 936 _recordMessage(staticInfo); | 1062 _recordMessage(staticInfo); |
| 937 } | 1063 } |
| 938 } | 1064 } |
| 939 } | 1065 } |
| 940 | 1066 |
| 941 bool _isObjectGetter(Expression target, SimpleIdentifier id) { | 1067 bool _isObjectGetter(Expression target, SimpleIdentifier id) { |
| 942 PropertyAccessorElement element = | 1068 PropertyAccessorElement element = |
| 943 rules.provider.objectType.element.getGetter(id.name); | 1069 typeProvider.objectType.element.getGetter(id.name); |
| 944 return (element != null && !element.isStatic); | 1070 return (element != null && !element.isStatic); |
| 945 } | 1071 } |
| 946 | 1072 |
| 947 bool _isObjectMethod(Expression target, SimpleIdentifier id) { | 1073 bool _isObjectMethod(Expression target, SimpleIdentifier id) { |
| 948 MethodElement element = | 1074 MethodElement element = |
| 949 rules.provider.objectType.element.getMethod(id.name); | 1075 typeProvider.objectType.element.getMethod(id.name); |
| 950 return (element != null && !element.isStatic); | 1076 return (element != null && !element.isStatic); |
| 951 } | 1077 } |
| 952 | 1078 |
| 953 bool _isObjectProperty(Expression target, SimpleIdentifier id) { | 1079 bool _isObjectProperty(Expression target, SimpleIdentifier id) { |
| 954 return _isObjectGetter(target, id) || _isObjectMethod(target, id); | 1080 return _isObjectGetter(target, id) || _isObjectMethod(target, id); |
| 955 } | 1081 } |
| 956 | 1082 |
| 957 DartType _getStaticType(Expression expr) { | 1083 DartType _getStaticType(Expression expr) { |
| 958 return expr.staticType ?? rules.provider.dynamicType; | 1084 return expr.staticType ?? DynamicTypeImpl.instance; |
| 959 } | 1085 } |
| 960 | 1086 |
| 961 void _recordDynamicInvoke(AstNode node, AstNode target) { | 1087 void _recordDynamicInvoke(AstNode node, AstNode target) { |
| 962 if (_hints) { | 1088 if (_hints) { |
| 963 reporter.onError(new DynamicInvoke(rules, node).toAnalysisError()); | 1089 reporter.onError(new DynamicInvoke(rules, node).toAnalysisError()); |
| 964 } | 1090 } |
| 965 // TODO(jmesserly): we may eventually want to record if the whole operation | 1091 // TODO(jmesserly): we may eventually want to record if the whole operation |
| 966 // (node) was dynamic, rather than the target, but this is an easier fit | 1092 // (node) was dynamic, rather than the target, but this is an easier fit |
| 967 // with what we used to do. | 1093 // with what we used to do. |
| 968 DynamicInvoke.set(target, true); | 1094 DynamicInvoke.set(target, true); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 979 | 1105 |
| 980 if (info is CoercionInfo) { | 1106 if (info is CoercionInfo) { |
| 981 // TODO(jmesserly): if we're run again on the same AST, we'll produce the | 1107 // TODO(jmesserly): if we're run again on the same AST, we'll produce the |
| 982 // same annotations. This should be harmless. This might go away once | 1108 // same annotations. This should be harmless. This might go away once |
| 983 // CodeChecker is integrated better with analyzer, as it will know that | 1109 // CodeChecker is integrated better with analyzer, as it will know that |
| 984 // checking has already been performed. | 1110 // checking has already been performed. |
| 985 // assert(CoercionInfo.get(info.node) == null); | 1111 // assert(CoercionInfo.get(info.node) == null); |
| 986 CoercionInfo.set(info.node, info); | 1112 CoercionInfo.set(info.node, info); |
| 987 } | 1113 } |
| 988 } | 1114 } |
| 1115 |
| 1116 bool _isLibraryPrefix(Expression node) => |
| 1117 node is SimpleIdentifier && node.staticElement is PrefixElement; |
| 1118 |
| 1119 /// Returns `true` if the target expression is dynamic. |
| 1120 bool _isDynamicTarget(Expression node) { |
| 1121 if (node == null) return false; |
| 1122 |
| 1123 if (_isLibraryPrefix(node)) return false; |
| 1124 |
| 1125 // Null type happens when we have unknown identifiers, like a dart: import |
| 1126 // that doesn't resolve. |
| 1127 var type = node.staticType; |
| 1128 return type == null || type.isDynamic; |
| 1129 } |
| 1130 |
| 1131 /// Returns `true` if the expression is a dynamic function call or method |
| 1132 /// invocation. |
| 1133 bool _isDynamicCall(Expression call) { |
| 1134 var ft = _getTypeAsCaller(call); |
| 1135 // TODO(leafp): This will currently return true if t is Function |
| 1136 // This is probably the most correct thing to do for now, since |
| 1137 // this code is also used by the back end. Maybe revisit at some |
| 1138 // point? |
| 1139 if (ft == null) return true; |
| 1140 // Dynamic as the parameter type is treated as bottom. A function with |
| 1141 // a dynamic parameter type requires a dynamic call in general. |
| 1142 // However, as an optimization, if we have an original definition, we know |
| 1143 // dynamic is reified as Object - in this case a regular call is fine. |
| 1144 if (call is SimpleIdentifier) { |
| 1145 var element = call.staticElement; |
| 1146 if (element is FunctionElement || element is MethodElement) { |
| 1147 // An original declaration. |
| 1148 return false; |
| 1149 } |
| 1150 } |
| 1151 |
| 1152 return rules.anyParameterType(ft, (pt) => pt.isDynamic); |
| 1153 } |
| 1154 |
| 1155 /// Given an expression, return its type assuming it is |
| 1156 /// in the caller position of a call (that is, accounting |
| 1157 /// for the possibility of a call method). Returns null |
| 1158 /// if expression is not statically callable. |
| 1159 FunctionType _getTypeAsCaller(Expression applicand) { |
| 1160 var t = applicand.staticType ?? DynamicTypeImpl.instance; |
| 1161 if (t is InterfaceType) { |
| 1162 return rules.getCallMethodType(t); |
| 1163 } |
| 1164 if (t is FunctionType) return t; |
| 1165 return null; |
| 1166 } |
| 989 } | 1167 } |
| 990 | 1168 |
| 991 // Return the field on type corresponding to member, or null if none | 1169 // Return the field on type corresponding to member, or null if none |
| 992 // exists or the "field" is actually a getter/setter. | 1170 // exists or the "field" is actually a getter/setter. |
| 993 PropertyInducingElement _getMemberField( | 1171 PropertyInducingElement _getMemberField( |
| 994 InterfaceType type, PropertyAccessorElement member) { | 1172 InterfaceType type, PropertyAccessorElement member) { |
| 995 String memberName = member.name; | 1173 String memberName = member.name; |
| 996 PropertyInducingElement field; | 1174 PropertyInducingElement field; |
| 997 if (member.isGetter) { | 1175 if (member.isGetter) { |
| 998 // The subclass member is an explicit getter or a field | 1176 // The subclass member is an explicit getter or a field |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1047 baseMethod = type.getSetter(memberName); | 1225 baseMethod = type.getSetter(memberName); |
| 1048 } else { | 1226 } else { |
| 1049 baseMethod = type.getMethod(memberName); | 1227 baseMethod = type.getMethod(memberName); |
| 1050 } | 1228 } |
| 1051 } catch (e) { | 1229 } catch (e) { |
| 1052 // TODO(sigmund): remove this try-catch block (see issue #48). | 1230 // TODO(sigmund): remove this try-catch block (see issue #48). |
| 1053 } | 1231 } |
| 1054 if (baseMethod == null || baseMethod.isStatic) return null; | 1232 if (baseMethod == null || baseMethod.isStatic) return null; |
| 1055 return baseMethod.type; | 1233 return baseMethod.type; |
| 1056 } | 1234 } |
| 1057 ; | |
| 1058 return f; | 1235 return f; |
| 1059 } | 1236 } |
| 1237 |
| 1238 |
| 1239 DartType _elementType(Element e) { |
| 1240 if (e == null) { |
| 1241 // Malformed code - just return dynamic. |
| 1242 return DynamicTypeImpl.instance; |
| 1243 } |
| 1244 return (e as dynamic).type; |
| 1245 } |
| OLD | NEW |