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

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

Issue 1507933002: Refactor strong mode to remove duplicate TypeRules (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pkg/analyzer/lib/src/task/dart.dart ('k') | pkg/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
6 // refactored to fit into analyzer. 6 // refactored to fit into analyzer.
7 library analyzer.src.task.strong.checker; 7 library analyzer.src.task.strong.checker;
8 8
9 import 'package:analyzer/analyzer.dart'; 9 import 'package:analyzer/analyzer.dart';
10 import 'package:analyzer/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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/task/dart.dart ('k') | pkg/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698