Move DDC to analyzer-based checker
diff --git a/lib/src/checker/checker.dart b/lib/src/checker/checker.dart
index 0af5675..10bae9c 100644
--- a/lib/src/checker/checker.dart
+++ b/lib/src/checker/checker.dart
@@ -4,945 +4,4 @@
library dev_compiler.src.checker.checker;
-import 'package:analyzer/analyzer.dart';
-import 'package:analyzer/src/generated/ast.dart';
-import 'package:analyzer/src/generated/element.dart';
-import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType;
-
-import '../info.dart';
-import '../utils.dart' show getMemberType;
-import 'rules.dart';
-
-/// Checks for overriding declarations of fields and methods. This is used to
-/// check overrides between classes and superclasses, interfaces, and mixin
-/// applications.
-class _OverrideChecker {
- bool _failure = false;
- final TypeRules _rules;
- final AnalysisErrorListener _reporter;
-
- _OverrideChecker(this._rules, this._reporter);
-
- void check(ClassDeclaration node) {
- if (node.element.type.isObject) return;
- _checkSuperOverrides(node);
- _checkMixinApplicationOverrides(node);
- _checkAllInterfaceOverrides(node);
- }
-
- /// Check overrides from mixin applications themselves. For example, in:
- ///
- /// A extends B with E, F
- ///
- /// we check:
- ///
- /// B & E against B (equivalently how E overrides B)
- /// B & E & F against B & E (equivalently how F overrides both B and E)
- void _checkMixinApplicationOverrides(ClassDeclaration node) {
- var type = node.element.type;
- var parent = type.superclass;
- var mixins = type.mixins;
-
- // Check overrides from applying mixins
- for (int i = 0; i < mixins.length; i++) {
- var seen = new Set<String>();
- var current = mixins[i];
- var errorLocation = node.withClause.mixinTypes[i];
- for (int j = i - 1; j >= 0; j--) {
- _checkIndividualOverridesFromType(
- current, mixins[j], errorLocation, seen);
- }
- _checkIndividualOverridesFromType(current, parent, errorLocation, seen);
- }
- }
-
- /// Check overrides between a class and its superclasses and mixins. For
- /// example, in:
- ///
- /// A extends B with E, F
- ///
- /// we check A against B, B super classes, E, and F.
- ///
- /// Internally we avoid reporting errors twice and we visit classes bottom up
- /// to ensure we report the most immediate invalid override first. For
- /// example, in the following code we'll report that `Test` has an invalid
- /// override with respect to `Parent` (as opposed to an invalid override with
- /// respect to `Grandparent`):
- ///
- /// class Grandparent {
- /// m(A a) {}
- /// }
- /// class Parent extends Grandparent {
- /// m(A a) {}
- /// }
- /// class Test extends Parent {
- /// m(B a) {} // invalid override
- /// }
- void _checkSuperOverrides(ClassDeclaration node) {
- var seen = new Set<String>();
- var current = node.element.type;
- var visited = new Set<InterfaceType>();
- do {
- visited.add(current);
- current.mixins.reversed
- .forEach((m) => _checkIndividualOverridesFromClass(node, m, seen));
- _checkIndividualOverridesFromClass(node, current.superclass, seen);
- current = current.superclass;
- } while (!current.isObject && !visited.contains(current));
- }
-
- /// Checks that implementations correctly override all reachable interfaces.
- /// In particular, we need to check these overrides for the definitions in
- /// the class itself and each its superclasses. If a superclass is not
- /// abstract, then we can skip its transitive interfaces. For example, in:
- ///
- /// B extends C implements G
- /// A extends B with E, F implements H, I
- ///
- /// we check:
- ///
- /// C against G, H, and I
- /// B against G, H, and I
- /// E against H and I // no check against G because B is a concrete class
- /// F against H and I
- /// A against H and I
- void _checkAllInterfaceOverrides(ClassDeclaration node) {
- var seen = new Set<String>();
- // Helper function to collect all reachable interfaces.
- find(InterfaceType interfaceType, Set result) {
- if (interfaceType == null || interfaceType.isObject) return;
- if (result.contains(interfaceType)) return;
- result.add(interfaceType);
- find(interfaceType.superclass, result);
- interfaceType.mixins.forEach((i) => find(i, result));
- interfaceType.interfaces.forEach((i) => find(i, result));
- }
-
- // Check all interfaces reachable from the `implements` clause in the
- // current class against definitions here and in superclasses.
- var localInterfaces = new Set<InterfaceType>();
- var type = node.element.type;
- type.interfaces.forEach((i) => find(i, localInterfaces));
- _checkInterfacesOverrides(node, localInterfaces, seen,
- includeParents: true);
-
- // Check also how we override locally the interfaces from parent classes if
- // the parent class is abstract. Otherwise, these will be checked as
- // overrides on the concrete superclass.
- var superInterfaces = new Set<InterfaceType>();
- var parent = type.superclass;
- // TODO(sigmund): we don't seem to be reporting the analyzer error that a
- // non-abstract class is not implementing an interface. See
- // https://github.com/dart-lang/dart-dev-compiler/issues/25
- while (parent != null && parent.element.isAbstract) {
- parent.interfaces.forEach((i) => find(i, superInterfaces));
- parent = parent.superclass;
- }
- _checkInterfacesOverrides(node, superInterfaces, seen,
- includeParents: false);
- }
-
- /// Checks that [cls] and its super classes (including mixins) correctly
- /// overrides each interface in [interfaces]. If [includeParents] is false,
- /// then mixins are still checked, but the base type and it's transitive
- /// supertypes are not.
- ///
- /// [cls] can be either a [ClassDeclaration] or a [InterfaceType]. For
- /// [ClassDeclaration]s errors are reported on the member that contains the
- /// invalid override, for [InterfaceType]s we use [errorLocation] instead.
- void _checkInterfacesOverrides(
- cls, Iterable<InterfaceType> interfaces, Set<String> seen,
- {Set<InterfaceType> visited,
- bool includeParents: true,
- AstNode errorLocation}) {
- var node = cls is ClassDeclaration ? cls : null;
- var type = cls is InterfaceType ? cls : node.element.type;
-
- if (visited == null) {
- visited = new Set<InterfaceType>();
- } else if (visited.contains(type)) {
- // Malformed type.
- return;
- } else {
- visited.add(type);
- }
-
- // Check direct overrides on [type]
- for (var interfaceType in interfaces) {
- if (node != null) {
- _checkIndividualOverridesFromClass(node, interfaceType, seen);
- } else {
- _checkIndividualOverridesFromType(
- type, interfaceType, errorLocation, seen);
- }
- }
-
- // Check overrides from its mixins
- for (int i = 0; i < type.mixins.length; i++) {
- var loc =
- errorLocation != null ? errorLocation : node.withClause.mixinTypes[i];
- for (var interfaceType in interfaces) {
- // We copy [seen] so we can report separately if more than one mixin or
- // the base class have an invalid override.
- _checkIndividualOverridesFromType(
- type.mixins[i], interfaceType, loc, new Set.from(seen));
- }
- }
-
- // Check overrides from its superclasses
- if (includeParents) {
- var parent = type.superclass;
- if (parent.isObject) return;
- var loc = errorLocation != null ? errorLocation : node.extendsClause;
- // No need to copy [seen] here because we made copies above when reporting
- // errors on mixins.
- _checkInterfacesOverrides(parent, interfaces, seen,
- visited: visited, includeParents: true, errorLocation: loc);
- }
- }
-
- /// Check that individual methods and fields in [subType] correctly override
- /// the declarations in [baseType].
- ///
- /// The [errorLocation] node indicates where errors are reported, see
- /// [_checkSingleOverride] for more details.
- ///
- /// The set [seen] is used to avoid reporting overrides more than once. It
- /// is used when invoking this function multiple times when checking several
- /// types in a class hierarchy. Errors are reported only the first time an
- /// invalid override involving a specific member is encountered.
- _checkIndividualOverridesFromType(InterfaceType subType,
- InterfaceType baseType, AstNode errorLocation, Set<String> seen) {
- void checkHelper(ExecutableElement e) {
- if (e.isStatic) return;
- if (seen.contains(e.name)) return;
- if (_checkSingleOverride(e, baseType, null, errorLocation)) {
- seen.add(e.name);
- }
- }
- subType.methods.forEach(checkHelper);
- subType.accessors.forEach(checkHelper);
- }
-
- /// Check that individual methods and fields in [subType] correctly override
- /// the declarations in [baseType].
- ///
- /// The [errorLocation] node indicates where errors are reported, see
- /// [_checkSingleOverride] for more details.
- _checkIndividualOverridesFromClass(
- ClassDeclaration node, InterfaceType baseType, Set<String> seen) {
- for (var member in node.members) {
- if (member is ConstructorDeclaration) continue;
- if (member is FieldDeclaration) {
- if (member.isStatic) continue;
- for (var variable in member.fields.variables) {
- var element = variable.element as PropertyInducingElement;
- var name = element.name;
- if (seen.contains(name)) continue;
- var getter = element.getter;
- var setter = element.setter;
- bool found = _checkSingleOverride(getter, baseType, variable, member);
- if (!variable.isFinal &&
- !variable.isConst &&
- _checkSingleOverride(setter, baseType, variable, member)) {
- found = true;
- }
- if (found) seen.add(name);
- }
- } else {
- if ((member as MethodDeclaration).isStatic) continue;
- var method = (member as MethodDeclaration).element;
- if (seen.contains(method.name)) continue;
- if (_checkSingleOverride(method, baseType, member, member)) {
- seen.add(method.name);
- }
- }
- }
- }
-
- /// Checks that [element] correctly overrides its corresponding member in
- /// [type]. Returns `true` if an override was found, that is, if [element] has
- /// a corresponding member in [type] that it overrides.
- ///
- /// The [errorLocation] is a node where the error is reported. For example, a
- /// bad override of a method in a class with respect to its superclass is
- /// reported directly at the method declaration. However, invalid overrides
- /// from base classes to interfaces, mixins to the base they are applied to,
- /// or mixins to interfaces are reported at the class declaration, since the
- /// base class or members on their own were not incorrect, only combining them
- /// with the interface was problematic. For example, these are example error
- /// locations in these cases:
- ///
- /// error: base class introduces an invalid override. The type of B.foo is
- /// not a subtype of E.foo:
- /// class A extends B implements E { ... }
- /// ^^^^^^^^^
- ///
- /// error: mixin introduces an invalid override. The type of C.foo is not
- /// a subtype of E.foo:
- /// class A extends B with C implements E { ... }
- /// ^
- ///
- /// When checking for overrides from a type and it's super types, [node] is
- /// the AST node that defines [element]. This is used to determine whether the
- /// type of the element could be inferred from the types in the super classes.
- bool _checkSingleOverride(ExecutableElement element, InterfaceType type,
- AstNode node, AstNode errorLocation) {
- assert(!element.isStatic);
-
- FunctionType subType = _rules.elementType(element);
- // TODO(vsm): Test for generic
- FunctionType baseType = getMemberType(type, element);
-
- if (baseType == null) return false;
- if (!_rules.isAssignable(subType, baseType)) {
- // See whether non-assignable cases fit one of our common patterns:
- //
- // Common pattern 1: Inferable return type (on getters and methods)
- // class A {
- // int get foo => ...;
- // String toString() { ... }
- // }
- // class B extends A {
- // get foo => e; // no type specified.
- // toString() { ... } // no return type specified.
- // }
- _recordMessage(new InvalidMethodOverride(
- errorLocation, element, type, subType, baseType));
- }
- return true;
- }
-
- void _recordMessage(StaticInfo info) {
- if (info == null) return;
- var error = info.toAnalysisError();
- if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
- _reporter.onError(error);
- }
-}
-
-/// Checks the body of functions and properties.
-class CodeChecker extends RecursiveAstVisitor {
- final TypeRules rules;
- final AnalysisErrorListener reporter;
- final _OverrideChecker _overrideChecker;
- bool _failure = false;
- bool get failure => _failure || _overrideChecker._failure;
-
- void reset() {
- _failure = false;
- _overrideChecker._failure = false;
- }
-
- CodeChecker(TypeRules rules, AnalysisErrorListener reporter)
- : rules = rules,
- reporter = reporter,
- _overrideChecker = new _OverrideChecker(rules, reporter);
-
- @override
- void visitComment(Comment node) {
- // skip, no need to do typechecking inside comments (they may contain
- // comment references which would require resolution).
- }
-
- @override
- void visitClassDeclaration(ClassDeclaration node) {
- _overrideChecker.check(node);
- super.visitClassDeclaration(node);
- }
-
- @override
- void visitAssignmentExpression(AssignmentExpression node) {
- var token = node.operator;
- if (token.type != TokenType.EQ) {
- _checkCompoundAssignment(node);
- } else {
- DartType staticType = _getStaticType(node.leftHandSide);
- checkAssignment(node.rightHandSide, staticType);
- }
- node.visitChildren(this);
- }
-
- /// Check constructor declaration to ensure correct super call placement.
- @override
- void visitConstructorDeclaration(ConstructorDeclaration node) {
- node.visitChildren(this);
-
- final init = node.initializers;
- for (int i = 0, last = init.length - 1; i < last; i++) {
- final node = init[i];
- if (node is SuperConstructorInvocation) {
- _recordMessage(new InvalidSuperInvocation(node));
- }
- }
- }
-
- @override
- void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
- var field = node.fieldName;
- var element = field.staticElement;
- DartType staticType = rules.elementType(element);
- checkAssignment(node.expression, staticType);
- node.visitChildren(this);
- }
-
- @override
- void visitForEachStatement(ForEachStatement node) {
- // Check that the expression is an Iterable.
- var expr = node.iterable;
- var iterableType = node.awaitKeyword != null
- ? rules.provider.streamType
- : rules.provider.iterableType;
- var loopVariable = node.identifier != null
- ? node.identifier
- : node.loopVariable?.identifier;
- if (loopVariable != null) {
- var iteratorType = loopVariable.staticType;
- var checkedType = iterableType.substitute4([iteratorType]);
- checkAssignment(expr, checkedType);
- }
- node.visitChildren(this);
- }
-
- @override
- void visitForStatement(ForStatement node) {
- if (node.condition != null) {
- checkBoolean(node.condition);
- }
- node.visitChildren(this);
- }
-
- @override
- void visitIfStatement(IfStatement node) {
- checkBoolean(node.condition);
- node.visitChildren(this);
- }
-
- @override
- void visitDoStatement(DoStatement node) {
- checkBoolean(node.condition);
- node.visitChildren(this);
- }
-
- @override
- void visitWhileStatement(WhileStatement node) {
- checkBoolean(node.condition);
- node.visitChildren(this);
- }
-
- @override
- void visitSwitchStatement(SwitchStatement node) {
- // SwitchStatement defines a boolean conversion to check the result of the
- // case value == the switch value, but in dev_compiler we require a boolean
- // return type from an overridden == operator (because Object.==), so
- // checking in SwitchStatement shouldn't be necessary.
- node.visitChildren(this);
- }
-
- @override
- void visitListLiteral(ListLiteral node) {
- var type = rules.provider.dynamicType;
- if (node.typeArguments != null) {
- var targs = node.typeArguments.arguments;
- if (targs.length > 0) type = targs[0].type;
- }
- var elements = node.elements;
- for (int i = 0; i < elements.length; i++) {
- checkArgument(elements[i], type);
- }
- super.visitListLiteral(node);
- }
-
- @override
- void visitMapLiteral(MapLiteral node) {
- var ktype = rules.provider.dynamicType;
- var vtype = rules.provider.dynamicType;
- if (node.typeArguments != null) {
- var targs = node.typeArguments.arguments;
- if (targs.length > 0) ktype = targs[0].type;
- if (targs.length > 1) vtype = targs[1].type;
- }
- var entries = node.entries;
- for (int i = 0; i < entries.length; i++) {
- var entry = entries[i];
- checkArgument(entry.key, ktype);
- checkArgument(entry.value, vtype);
- }
- super.visitMapLiteral(node);
- }
-
- // Check invocations
- void checkArgumentList(ArgumentList node, FunctionType type) {
- NodeList<Expression> list = node.arguments;
- int len = list.length;
- for (int i = 0; i < len; ++i) {
- Expression arg = list[i];
- ParameterElement element = arg.staticParameterElement;
- if (element == null) {
- if (type.parameters.length < len) {
- // We found an argument mismatch, the analyzer will report this too,
- // so no need to insert an error for this here.
- continue;
- }
- element = type.parameters[i];
- // TODO(vsm): When can this happen?
- assert(element != null);
- }
- DartType expectedType = rules.elementType(element);
- if (expectedType == null) expectedType = rules.provider.dynamicType;
- checkArgument(arg, expectedType);
- }
- }
-
- void checkArgument(Expression arg, DartType expectedType) {
- // Preserve named argument structure, so their immediate parent is the
- // method invocation.
- if (arg is NamedExpression) {
- arg = (arg as NamedExpression).expression;
- }
- checkAssignment(arg, expectedType);
- }
-
- void checkFunctionApplication(
- Expression node, Expression f, ArgumentList list) {
- if (rules.isDynamicCall(f)) {
- // If f is Function and this is a method invocation, we should have
- // gotten an analyzer error, so no need to issue another error.
- _recordDynamicInvoke(node, f);
- } else {
- checkArgumentList(list, rules.getTypeAsCaller(f));
- }
- }
-
- @override
- visitMethodInvocation(MethodInvocation node) {
- var target = node.realTarget;
- if (rules.isDynamicTarget(target) &&
- !_isObjectMethod(node, node.methodName)) {
- _recordDynamicInvoke(node, target);
-
- // Mark the tear-off as being dynamic, too. This lets us distinguish
- // cases like:
- //
- // dynamic d;
- // d.someMethod(...); // the whole method call must be a dynamic send.
- //
- // ... from case like:
- //
- // SomeType s;
- // s.someDynamicField(...); // static get, followed by dynamic call.
- //
- // The first case is handled here, the second case is handled below when
- // we call [checkFunctionApplication].
- DynamicInvoke.set(node.methodName, true);
- } else {
- checkFunctionApplication(node, node.methodName, node.argumentList);
- }
- node.visitChildren(this);
- }
-
- @override
- void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
- checkFunctionApplication(node, node.function, node.argumentList);
- node.visitChildren(this);
- }
-
- @override
- void visitRedirectingConstructorInvocation(
- RedirectingConstructorInvocation node) {
- var type = node.staticElement.type;
- checkArgumentList(node.argumentList, type);
- node.visitChildren(this);
- }
-
- @override
- void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
- var element = node.staticElement;
- if (element == null) {
- _recordMessage(new MissingTypeError(node));
- } else {
- var type = node.staticElement.type;
- checkArgumentList(node.argumentList, type);
- }
- node.visitChildren(this);
- }
-
- void _checkReturnOrYield(Expression expression, AstNode node,
- {bool yieldStar: false}) {
- var body = node.getAncestor((n) => n is FunctionBody);
- var type = rules.getExpectedReturnType(body, yieldStar: yieldStar);
- if (type == null) {
- // We have a type mismatch: the async/async*/sync* modifier does
- // not match the return or yield type. We should have already gotten an
- // analyzer error in this case.
- return;
- }
- // TODO(vsm): Enforce void or dynamic (to void?) when expression is null.
- if (expression != null) checkAssignment(expression, type);
- }
-
- @override
- void visitExpressionFunctionBody(ExpressionFunctionBody node) {
- _checkReturnOrYield(node.expression, node);
- node.visitChildren(this);
- }
-
- @override
- void visitReturnStatement(ReturnStatement node) {
- _checkReturnOrYield(node.expression, node);
- node.visitChildren(this);
- }
-
- @override
- void visitYieldStatement(YieldStatement node) {
- _checkReturnOrYield(node.expression, node, yieldStar: node.star != null);
- node.visitChildren(this);
- }
-
- @override
- void visitPropertyAccess(PropertyAccess node) {
- var target = node.realTarget;
- if (rules.isDynamicTarget(target) &&
- !_isObjectProperty(target, node.propertyName)) {
- _recordDynamicInvoke(node, target);
- }
- node.visitChildren(this);
- }
-
- @override
- void visitPrefixedIdentifier(PrefixedIdentifier node) {
- final target = node.prefix;
- if (rules.isDynamicTarget(target) &&
- !_isObjectProperty(target, node.identifier)) {
- _recordDynamicInvoke(node, target);
- }
- node.visitChildren(this);
- }
-
- @override
- void visitDefaultFormalParameter(DefaultFormalParameter node) {
- // Check that defaults have the proper subtype.
- var parameter = node.parameter;
- var parameterType = rules.elementType(parameter.element);
- assert(parameterType != null);
- var defaultValue = node.defaultValue;
- if (defaultValue != null) {
- checkAssignment(defaultValue, parameterType);
- }
-
- node.visitChildren(this);
- }
-
- @override
- void visitFieldFormalParameter(FieldFormalParameter node) {
- var element = node.element;
- var typeName = node.type;
- if (typeName != null) {
- var type = rules.elementType(element);
- var fieldElement =
- node.identifier.staticElement as FieldFormalParameterElement;
- var fieldType = rules.elementType(fieldElement.field);
- if (!rules.isSubTypeOf(type, fieldType)) {
- var staticInfo =
- new InvalidParameterDeclaration(rules, node, fieldType);
- _recordMessage(staticInfo);
- }
- }
- node.visitChildren(this);
- }
-
- @override
- void visitInstanceCreationExpression(InstanceCreationExpression node) {
- var arguments = node.argumentList;
- var element = node.staticElement;
- if (element != null) {
- var type = rules.elementType(node.staticElement);
- checkArgumentList(arguments, type);
- } else {
- _recordMessage(new MissingTypeError(node));
- }
- node.visitChildren(this);
- }
-
- @override
- void visitVariableDeclarationList(VariableDeclarationList node) {
- TypeName type = node.type;
- if (type == null) {
- // No checks are needed when the type is var. Although internally the
- // typing rules may have inferred a more precise type for the variable
- // based on the initializer.
- } else {
- var dartType = getType(type);
- for (VariableDeclaration variable in node.variables) {
- var initializer = variable.initializer;
- if (initializer != null) {
- checkAssignment(initializer, dartType);
- }
- }
- }
- node.visitChildren(this);
- }
-
- void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) {
- var type = getType(typeName);
- if (!rules.isGroundType(type)) {
- _recordMessage(new NonGroundTypeCheckInfo(node, type));
- }
- }
-
- @override
- void visitAsExpression(AsExpression node) {
- // We could do the same check as the IsExpression below, but that is
- // potentially too conservative. Instead, at runtime, we must fail hard
- // if the Dart as and the DDC as would return different values.
- node.visitChildren(this);
- }
-
- @override
- void visitIsExpression(IsExpression node) {
- _checkRuntimeTypeCheck(node, node.type);
- node.visitChildren(this);
- }
-
- @override
- void visitPrefixExpression(PrefixExpression node) {
- if (node.operator.type == TokenType.BANG) {
- checkBoolean(node.operand);
- } else {
- _checkUnary(node);
- }
- node.visitChildren(this);
- }
-
- @override
- void visitPostfixExpression(PostfixExpression node) {
- _checkUnary(node);
- node.visitChildren(this);
- }
-
- void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) {
- var op = node.operator;
- if (op.isUserDefinableOperator ||
- op.type == TokenType.PLUS_PLUS ||
- op.type == TokenType.MINUS_MINUS) {
- if (rules.isDynamicTarget(node.operand)) {
- _recordDynamicInvoke(node, node.operand);
- }
- // For ++ and --, even if it is not dynamic, we still need to check
- // that the user defined method accepts an `int` as the RHS.
- // We assume Analyzer has done this already.
- }
- }
-
- @override
- void visitBinaryExpression(BinaryExpression node) {
- var op = node.operator;
- if (op.isUserDefinableOperator) {
- if (rules.isDynamicTarget(node.leftOperand)) {
- // Dynamic invocation
- // TODO(vsm): Move this logic to the resolver?
- if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) {
- _recordDynamicInvoke(node, node.leftOperand);
- }
- } else {
- var element = node.staticElement;
- // Method invocation.
- if (element is MethodElement) {
- var type = element.type;
- // Analyzer should enforce number of parameter types, but check in
- // case we have erroneous input.
- if (type.normalParameterTypes.isNotEmpty) {
- checkArgument(node.rightOperand, type.normalParameterTypes[0]);
- }
- } else {
- // TODO(vsm): Assert that the analyzer found an error here?
- }
- }
- } else {
- // Non-method operator.
- switch (op.type) {
- case TokenType.AMPERSAND_AMPERSAND:
- case TokenType.BAR_BAR:
- checkBoolean(node.leftOperand);
- checkBoolean(node.rightOperand);
- break;
- case TokenType.BANG_EQ:
- break;
- case TokenType.QUESTION_QUESTION:
- break;
- default:
- assert(false);
- }
- }
- node.visitChildren(this);
- }
-
- @override
- void visitConditionalExpression(ConditionalExpression node) {
- checkBoolean(node.condition);
- node.visitChildren(this);
- }
-
- @override
- void visitIndexExpression(IndexExpression node) {
- var target = node.realTarget;
- if (rules.isDynamicTarget(target)) {
- _recordDynamicInvoke(node, target);
- } else {
- var element = node.staticElement;
- if (element is MethodElement) {
- var type = element.type;
- // Analyzer should enforce number of parameter types, but check in
- // case we have erroneous input.
- if (type.normalParameterTypes.isNotEmpty) {
- checkArgument(node.index, type.normalParameterTypes[0]);
- }
- } else {
- // TODO(vsm): Assert that the analyzer found an error here?
- }
- }
- node.visitChildren(this);
- }
-
- DartType getType(TypeName name) {
- return (name == null) ? rules.provider.dynamicType : name.type;
- }
-
- /// Analyzer checks boolean conversions, but we need to check too, because
- /// it uses the default assignability rules that allow `dynamic` and `Object`
- /// to be assigned to bool with no message.
- void checkBoolean(Expression expr) =>
- checkAssignment(expr, rules.provider.boolType);
-
- void checkAssignment(Expression expr, DartType type) {
- if (expr is ParenthesizedExpression) {
- checkAssignment(expr.expression, type);
- } else {
- _recordMessage(rules.checkAssignment(expr, type));
- }
- }
-
- DartType _specializedBinaryReturnType(
- TokenType op, DartType t1, DartType t2, DartType normalReturnType) {
- // This special cases binary return types as per 16.26 and 16.27 of the
- // Dart language spec.
- switch (op) {
- case TokenType.PLUS:
- case TokenType.MINUS:
- case TokenType.STAR:
- case TokenType.TILDE_SLASH:
- case TokenType.PERCENT:
- case TokenType.PLUS_EQ:
- case TokenType.MINUS_EQ:
- case TokenType.STAR_EQ:
- case TokenType.TILDE_SLASH_EQ:
- case TokenType.PERCENT_EQ:
- if (t1 == rules.provider.intType &&
- t2 == rules.provider.intType) return t1;
- if (t1 == rules.provider.doubleType &&
- t2 == rules.provider.doubleType) return t1;
- // This particular combo is not spelled out in the spec, but all
- // implementations and analyzer seem to follow this.
- if (t1 == rules.provider.doubleType &&
- t2 == rules.provider.intType) return t1;
- }
- return normalReturnType;
- }
-
- void _checkCompoundAssignment(AssignmentExpression expr) {
- var op = expr.operator.type;
- assert(op.isAssignmentOperator && op != TokenType.EQ);
- var methodElement = expr.staticElement;
- if (methodElement == null) {
- // Dynamic invocation
- _recordDynamicInvoke(expr, expr.leftHandSide);
- } else {
- // Sanity check the operator
- assert(methodElement.isOperator);
- var functionType = methodElement.type;
- var paramTypes = functionType.normalParameterTypes;
- assert(paramTypes.length == 1);
- assert(functionType.namedParameterTypes.isEmpty);
- assert(functionType.optionalParameterTypes.isEmpty);
-
- // Check the lhs type
- var staticInfo;
- var rhsType = _getStaticType(expr.rightHandSide);
- var lhsType = _getStaticType(expr.leftHandSide);
- var returnType = _specializedBinaryReturnType(
- op, lhsType, rhsType, functionType.returnType);
-
- if (!rules.isSubTypeOf(returnType, lhsType)) {
- final numType = rules.provider.numType;
- // Try to fix up the numerical case if possible.
- if (rules.isSubTypeOf(lhsType, numType) &&
- rules.isSubTypeOf(lhsType, rhsType)) {
- // This is also slightly different from spec, but allows us to keep
- // compound operators in the int += num and num += dynamic cases.
- staticInfo = DownCast.create(
- rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType));
- rhsType = lhsType;
- } else {
- // Static type error
- staticInfo = new StaticTypeError(rules, expr, lhsType);
- }
- _recordMessage(staticInfo);
- }
-
- // Check the rhs type
- if (staticInfo is! CoercionInfo) {
- var paramType = paramTypes.first;
- staticInfo = rules.checkAssignment(expr.rightHandSide, paramType);
- _recordMessage(staticInfo);
- }
- }
- }
-
- bool _isObjectGetter(Expression target, SimpleIdentifier id) {
- PropertyAccessorElement element =
- rules.provider.objectType.element.getGetter(id.name);
- return (element != null && !element.isStatic);
- }
-
- bool _isObjectMethod(Expression target, SimpleIdentifier id) {
- MethodElement element =
- rules.provider.objectType.element.getMethod(id.name);
- return (element != null && !element.isStatic);
- }
-
- bool _isObjectProperty(Expression target, SimpleIdentifier id) {
- return _isObjectGetter(target, id) || _isObjectMethod(target, id);
- }
-
- DartType _getStaticType(Expression expr) {
- var type = expr.staticType;
- if (type == null) {
- reporter.onError(new MissingTypeError(expr).toAnalysisError());
- }
- return type ?? rules.provider.dynamicType;
- }
-
- void _recordDynamicInvoke(AstNode node, AstNode target) {
- reporter.onError(new DynamicInvoke(rules, node).toAnalysisError());
- // TODO(jmesserly): we may eventually want to record if the whole operation
- // (node) was dynamic, rather than the target, but this is an easier fit
- // with what we used to do.
- DynamicInvoke.set(target, true);
- }
-
- void _recordMessage(StaticInfo info) {
- if (info == null) return;
- var error = info.toAnalysisError();
- if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
- reporter.onError(error);
-
- if (info is CoercionInfo) {
- // TODO(jmesserly): if we're run again on the same AST, we'll produce the
- // same annotations. This should be harmless. This might go away once
- // CodeChecker is integrated better with analyzer, as it will know that
- // checking has already been performed.
- // assert(CoercionInfo.get(info.node) == null);
- CoercionInfo.set(info.node, info);
- }
- }
-}
+export 'package:analyzer/src/task/strong/checker.dart';
diff --git a/lib/src/checker/resolver.dart b/lib/src/checker/resolver.dart
index f05998e..1e141cf 100644
--- a/lib/src/checker/resolver.dart
+++ b/lib/src/checker/resolver.dart
@@ -19,6 +19,7 @@
import '../../strong_mode.dart' show StrongModeOptions;
import '../utils.dart';
+import 'rules.dart';
final _log = new logger.Logger('dev_compiler.src.resolver');
diff --git a/lib/src/checker/rules.dart b/lib/src/checker/rules.dart
index f4d35bc..eebfcac 100644
--- a/lib/src/checker/rules.dart
+++ b/lib/src/checker/rules.dart
@@ -4,731 +4,4 @@
library dev_compiler.src.checker.rules;
-import 'package:analyzer/src/generated/ast.dart';
-import 'package:analyzer/src/generated/element.dart';
-import 'package:analyzer/src/generated/resolver.dart';
-
-import '../../strong_mode.dart' show StrongModeOptions;
-import '../info.dart';
-import '../utils.dart' as utils;
-
-class TypeRules {
- final TypeProvider provider;
-
- /// Map of fields / properties / methods on Object.
- final Map<String, DartType> objectMembers;
-
- final StrongModeOptions options;
- DownwardsInference inferrer;
-
- TypeRules(TypeProvider provider, {this.options})
- : provider = provider,
- objectMembers = utils.getObjectMemberMap(provider) {
- inferrer = new DownwardsInference(this);
- }
-
- /// Given a type t, if t is an interface type with a call method
- /// defined, return the function type for the call method, otherwise
- /// return null.
- FunctionType getCallMethodType(DartType t) {
- if (t is InterfaceType) {
- ClassElement element = t.element;
- InheritanceManager manager = new InheritanceManager(element.library);
- FunctionType callType = manager.lookupMemberType(t, "call");
- return callType;
- }
- return null;
- }
-
- /// Given an expression, return its type assuming it is
- /// in the caller position of a call (that is, accounting
- /// for the possibility of a call method). Returns null
- /// if expression is not statically callable.
- FunctionType getTypeAsCaller(Expression applicand) {
- var t = getStaticType(applicand);
- if (t is InterfaceType) {
- return getCallMethodType(t);
- }
- if (t is FunctionType) return t;
- return null;
- }
-
- /// Gets the expected return type of the given function [body], either from
- /// a normal return/yield, or from a yield*.
- DartType getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
- FunctionType functionType;
- var parent = body.parent;
- if (parent is Declaration) {
- functionType = elementType(parent.element);
- } else {
- assert(parent is FunctionExpression);
- functionType = getStaticType(parent);
- }
-
- var type = functionType.returnType;
-
- InterfaceType expectedType = null;
- if (body.isAsynchronous) {
- if (body.isGenerator) {
- // Stream<T> -> T
- expectedType = provider.streamType;
- } else {
- // Future<T> -> T
- // TODO(vsm): Revisit with issue #228.
- expectedType = provider.futureType;
- }
- } else {
- if (body.isGenerator) {
- // Iterable<T> -> T
- expectedType = provider.iterableType;
- } else {
- // T -> T
- return type;
- }
- }
- if (yieldStar) {
- if (type.isDynamic) {
- // Ensure it's at least a Stream / Iterable.
- return expectedType.substitute4([provider.dynamicType]);
- } else {
- // Analyzer will provide a separate error if expected type
- // is not compatible with type.
- return type;
- }
- }
- if (type.isDynamic) {
- return type;
- } else if (type is InterfaceType && type.element == expectedType.element) {
- return type.typeArguments[0];
- } else {
- // Malformed type - fallback on analyzer error.
- return null;
- }
- }
-
- DartType getStaticType(Expression expr) {
- return expr.staticType ?? provider.dynamicType;
- }
-
- bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
- if (t.isDynamic && dynamicIsBottom) return true;
- // TODO(vsm): We need direct support for non-nullability in DartType.
- // This should check on "true/nonnullable" Bottom
- if (t.isBottom) return true;
- return false;
- }
-
- bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
- if (t.isDynamic && !dynamicIsBottom) return true;
- if (t.isObject) return true;
- return false;
- }
-
- bool _anyParameterType(FunctionType ft, bool predicate(DartType t)) {
- return ft.normalParameterTypes.any(predicate) ||
- ft.optionalParameterTypes.any(predicate) ||
- ft.namedParameterTypes.values.any(predicate);
- }
-
- // TODO(leafp): Revisit this.
- bool isGroundType(DartType t) {
- if (t is TypeParameterType) return false;
- if (_isTop(t)) return true;
-
- if (t is FunctionType) {
- if (!_isTop(t.returnType) ||
- _anyParameterType(t, (pt) => !_isBottom(pt, dynamicIsBottom: true))) {
- return false;
- } else {
- return true;
- }
- }
-
- if (t is InterfaceType) {
- var typeArguments = t.typeArguments;
- for (var typeArgument in typeArguments) {
- if (!_isTop(typeArgument)) return false;
- }
- return true;
- }
-
- // We should not see any other type aside from malformed code.
- return false;
- }
-
- /// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC
- /// checker to determine whether f1 would be a subtype of f2 if the return
- /// type of f1 is set to match f2's return type.
- // [fuzzyArrows] indicates whether or not the f1 and f2 should be
- // treated as fuzzy arrow types (and hence dynamic parameters to f2 treated as
- // bottom).
- bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2,
- {bool fuzzyArrows: true, bool ignoreReturn: false}) {
- final r1s = f1.normalParameterTypes;
- final o1s = f1.optionalParameterTypes;
- final n1s = f1.namedParameterTypes;
- final r2s = f2.normalParameterTypes;
- final o2s = f2.optionalParameterTypes;
- final n2s = f2.namedParameterTypes;
- final ret1 = ignoreReturn ? f2.returnType : f1.returnType;
- final ret2 = f2.returnType;
-
- // A -> B <: C -> D if C <: A and
- // either D is void or B <: D
- if (!ret2.isVoid && !isSubTypeOf(ret1, ret2)) return false;
-
- // Reject if one has named and the other has optional
- if (n1s.length > 0 && o2s.length > 0) return false;
- if (n2s.length > 0 && o1s.length > 0) return false;
-
- // f2 has named parameters
- if (n2s.length > 0) {
- // Check that every named parameter in f2 has a match in f1
- for (String k2 in n2s.keys) {
- if (!n1s.containsKey(k2)) return false;
- if (!isSubTypeOf(n2s[k2], n1s[k2],
- dynamicIsBottom: fuzzyArrows)) return false;
- }
- }
- // If we get here, we either have no named parameters,
- // or else the named parameters match and we have no optional
- // parameters
-
- // If f1 has more required parameters, reject
- if (r1s.length > r2s.length) return false;
-
- // If f2 has more required + optional parameters, reject
- if (r2s.length + o2s.length > r1s.length + o1s.length) return false;
-
- // The parameter lists must look like the following at this point
- // where rrr is a region of required, and ooo is a region of optionals.
- // f1: rrr ooo ooo ooo
- // f2: rrr rrr ooo
- int rr = r1s.length; // required in both
- int or = r2s.length - r1s.length; // optional in f1, required in f2
- int oo = o2s.length; // optional in both
-
- for (int i = 0; i < rr; ++i) {
- if (!isSubTypeOf(r2s[i], r1s[i],
- dynamicIsBottom: fuzzyArrows)) return false;
- }
- for (int i = 0, j = rr; i < or; ++i, ++j) {
- if (!isSubTypeOf(r2s[j], o1s[i],
- dynamicIsBottom: fuzzyArrows)) return false;
- }
- for (int i = or, j = 0; i < oo; ++i, ++j) {
- if (!isSubTypeOf(o2s[j], o1s[i],
- dynamicIsBottom: fuzzyArrows)) return false;
- }
- return true;
- }
-
- bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) {
- if (i1 == i2) return true;
-
- if (i1.element == i2.element) {
- List<DartType> tArgs1 = i1.typeArguments;
- List<DartType> tArgs2 = i2.typeArguments;
-
- // TODO(leafp): Verify that this is always true
- // Do raw types get filled in?
- assert(tArgs1.length == tArgs2.length);
-
- for (int i = 0; i < tArgs1.length; i++) {
- DartType t1 = tArgs1[i];
- DartType t2 = tArgs2[i];
- if (!isSubTypeOf(t1, t2)) return false;
- }
- return true;
- }
-
- if (i2.isDartCoreFunction) {
- if (i1.element.getMethod("call") != null) return true;
- }
-
- if (i1 == provider.objectType) return false;
-
- if (_isInterfaceSubTypeOf(i1.superclass, i2)) return true;
-
- for (final parent in i1.interfaces) {
- if (_isInterfaceSubTypeOf(parent, i2)) return true;
- }
-
- for (final parent in i1.mixins) {
- if (_isInterfaceSubTypeOf(parent, i2)) return true;
- }
-
- return false;
- }
-
- bool isSubTypeOf(DartType t1, DartType t2, {bool dynamicIsBottom: false}) {
- if (t1 == t2) return true;
-
- // Trivially true.
- if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
- _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
- return true;
- }
-
- // Trivially false.
- if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
- _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
- return false;
- }
-
- // The null type is a subtype of any nullable type, which is all Dart types.
- // TODO(vsm): Note, t1.isBottom still allows for null confusingly.
- // _isBottom(t1) does not necessarily imply t1.isBottom if there are
- // nonnullable types in the system.
- if (t1.isBottom) {
- return true;
- }
-
- // S <: T where S is a type variable
- // T is not dynamic or object (handled above)
- // S != T (handled above)
- // So only true if bound of S is S' and
- // S' <: T
- if (t1 is TypeParameterType) {
- DartType bound = t1.element.bound;
- if (bound == null) return false;
- return isSubTypeOf(bound, t2);
- }
-
- if (t2 is TypeParameterType) {
- return false;
- }
-
- if (t2.isDartCoreFunction) {
- if (t1 is FunctionType) return true;
- if (t1.element is ClassElement) {
- if ((t1.element as ClassElement).getMethod("call") != null) return true;
- }
- }
-
- // "Traditional" name-based subtype check.
- if (t1 is InterfaceType && t2 is InterfaceType) {
- return _isInterfaceSubTypeOf(t1, t2);
- }
-
- if (t1 is! FunctionType && t2 is! FunctionType) return false;
-
- if (t1 is InterfaceType && t2 is FunctionType) {
- var callType = getCallMethodType(t1);
- if (callType == null) return false;
- return isFunctionSubTypeOf(callType, t2);
- }
-
- if (t1 is FunctionType && t2 is InterfaceType) {
- return false;
- }
-
- // Functions
- // Note: it appears under the hood all Dart functions map to a class /
- // hidden type that:
- // (a) subtypes Object (an internal _FunctionImpl in the VM)
- // (b) implements Function
- // (c) provides standard Object members (hashCode, toString)
- // (d) contains private members (corresponding to _FunctionImpl?)
- // (e) provides a call method to handle the actual function invocation
- //
- // The standard Dart subtyping rules are structural in nature. I.e.,
- // bivariant on arguments and return type.
- //
- // The below tries for a more traditional subtyping rule:
- // - covariant on return type
- // - contravariant on parameters
- // - 'sensible' (?) rules on optional and/or named params
- // but doesn't properly mix with class subtyping. I suspect Java 8 lambdas
- // essentially map to dynamic (and rely on invokedynamic) due to similar
- // issues.
- return isFunctionSubTypeOf(t1 as FunctionType, t2 as FunctionType);
- }
-
- bool isAssignable(DartType t1, DartType t2) {
- return isSubTypeOf(t1, t2);
- }
-
- // Produce a coercion which coerces something of type fromT
- // to something of type toT.
- // If wrap is true and both are function types, a closure
- // wrapper coercion is produced using _wrapTo (see above)
- // Returns the error coercion if the types cannot be coerced
- // according to our current criteria.
- Coercion _coerceTo(DartType fromT, DartType toT) {
- // We can use anything as void
- if (toT.isVoid) return Coercion.identity(toT);
-
- // fromT <: toT, no coercion needed
- if (isSubTypeOf(fromT, toT)) return Coercion.identity(toT);
-
- // For now, reject conversions between function types and
- // call method objects. We could choose to allow casts here.
- // Wrapping a function type to assign it to a call method
- // object will never succeed. Wrapping the other way could
- // be allowed.
- if ((fromT is FunctionType && getCallMethodType(toT) != null) ||
- (toT is FunctionType && getCallMethodType(fromT) != null)) {
- return Coercion.error();
- }
-
- // Downcast if toT <: fromT
- if (isSubTypeOf(toT, fromT)) return Coercion.cast(fromT, toT);
-
- // Downcast if toT <===> fromT
- // The intention here is to allow casts that are sideways in the restricted
- // type system, but allowed in the regular dart type system, since these
- // are likely to succeed. The canonical example is List<dynamic> and
- // Iterable<T> for some concrete T (e.g. Object). These are unrelated
- // in the restricted system, but List<dynamic> <: Iterable<T> in dart.
- if (fromT.isAssignableTo(toT)) {
- return Coercion.cast(fromT, toT);
- }
- return Coercion.error();
- }
-
- StaticInfo checkAssignment(Expression expr, DartType toT) {
- final fromT = getStaticType(expr);
- final Coercion c = _coerceTo(fromT, toT);
- if (c is Identity) return null;
- if (c is CoercionError) return new StaticTypeError(this, expr, toT);
- var reason = null;
-
- var errors = <String>[];
- var ok = inferrer.inferExpression(expr, toT, errors);
- if (ok) return InferredType.create(this, expr, toT);
- reason = (errors.isNotEmpty) ? errors.first : null;
-
- if (c is Cast) return DownCast.create(this, expr, c, reason: reason);
- assert(false);
- return null;
- }
-
- DartType elementType(Element e) {
- if (e == null) {
- // Malformed code - just return dynamic.
- return provider.dynamicType;
- }
- return (e as dynamic).type;
- }
-
- /// Returns `true` if the target expression is dynamic.
- // TODO(jmesserly): remove this in favor of utils? Or a static method here?
- bool isDynamicTarget(Expression target) => utils.isDynamicTarget(target);
-
- /// Returns `true` if the expression is a dynamic function call or method
- /// invocation.
- bool isDynamicCall(Expression call) {
- var ft = getTypeAsCaller(call);
- // TODO(leafp): This will currently return true if t is Function
- // This is probably the most correct thing to do for now, since
- // this code is also used by the back end. Maybe revisit at some
- // point?
- if (ft == null) return true;
- // Dynamic as the parameter type is treated as bottom. A function with
- // a dynamic parameter type requires a dynamic call in general.
- // However, as an optimization, if we have an original definition, we know
- // dynamic is reified as Object - in this case a regular call is fine.
- if (call is SimpleIdentifier) {
- var element = call.staticElement;
- if (element is FunctionElement || element is MethodElement) {
- // An original declaration.
- return false;
- }
- }
-
- return _anyParameterType(ft, (pt) => pt.isDynamic);
- }
-}
-
-class DownwardsInference {
- final TypeRules rules;
-
- DownwardsInference(this.rules);
-
- /// Called for each list literal which gets inferred
- void annotateListLiteral(ListLiteral e, List<DartType> targs) {}
-
- /// Called for each map literal which gets inferred
- void annotateMapLiteral(MapLiteral e, List<DartType> targs) {}
-
- /// Called for each new/const which gets inferred
- void annotateInstanceCreationExpression(
- InstanceCreationExpression e, List<DartType> targs) {}
-
- /// Called for cast from dynamic required for inference to succeed
- void annotateCastFromDynamic(Expression e, DartType t) {}
-
- /// Called for each function expression return type inferred
- void annotateFunctionExpression(FunctionExpression e, DartType returnType) {}
-
- /// Downward inference
- bool inferExpression(Expression e, DartType t, List<String> errors) {
- // Don't cast top level expressions, only sub-expressions
- return _inferExpression(e, t, errors, cast: false);
- }
-
- /// Downward inference
- bool _inferExpression(Expression e, DartType t, List<String> errors,
- {cast: true}) {
- if (e is ConditionalExpression) {
- return _inferConditionalExpression(e, t, errors);
- }
- if (e is ParenthesizedExpression) {
- return _inferParenthesizedExpression(e, t, errors);
- }
- if (rules.isSubTypeOf(rules.getStaticType(e), t)) return true;
- if (cast && rules.getStaticType(e).isDynamic) {
- annotateCastFromDynamic(e, t);
- return true;
- }
- if (e is FunctionExpression) return _inferFunctionExpression(e, t, errors);
- if (e is ListLiteral) return _inferListLiteral(e, t, errors);
- if (e is MapLiteral) return _inferMapLiteral(e, t, errors);
- if (e is NamedExpression) return _inferNamedExpression(e, t, errors);
- if (e is InstanceCreationExpression) {
- return _inferInstanceCreationExpression(e, t, errors);
- }
- errors.add("$e cannot be typed as $t");
- return false;
- }
-
- /// If t1 = I<dynamic, ..., dynamic>, then look for a supertype
- /// of t1 of the form K<S0, ..., Sm> where t2 = K<S0', ..., Sm'>
- /// If the supertype exists, use the constraints S0 <: S0', ... Sm <: Sm'
- /// to derive a concrete instantation for I of the form <T0, ..., Tn>,
- /// such that I<T0, .., Tn> <: t2
- List<DartType> _matchTypes(InterfaceType t1, InterfaceType t2) {
- if (t1 == t2) return t2.typeArguments;
- var tArgs1 = t1.typeArguments;
- var tArgs2 = t2.typeArguments;
- // If t1 isn't a raw type, bail out
- if (tArgs1 != null && tArgs1.any((t) => !t.isDynamic)) return null;
-
- // This is our inferred type argument list. We start at all dynamic,
- // and fill in with inferred types when we reach a match.
- var actuals =
- new List<DartType>.filled(tArgs1.length, rules.provider.dynamicType);
-
- // When we find the supertype of t1 with the same
- // classname as t2 (see below), we have the following:
- // If t1 is an instantiation of a class T1<X0, ..., Xn>
- // and t2 is an instantiation of a class T2<Y0, ...., Ym>
- // of the form t2 = T2<S0, ..., Sm>
- // then we want to choose instantiations for the Xi
- // T0, ..., Tn such that T1<T0, ..., Tn> <: t2 .
- // To find this, we simply instantate T1 with
- // X0, ..., Xn, and then find its superclass
- // T2<T0', ..., Tn'>. We then solve the constraint
- // set T0' <: S0, ..., Tn' <: Sn for the Xi.
- // Currently, we only handle constraints where
- // the Ti' is one of the Xi'. If there are multiple
- // constraints on some Xi, we choose the lower of the
- // two (if it exists).
- bool permute(List<DartType> permutedArgs) {
- if (permutedArgs == null) return false;
- var ps = t1.typeParameters;
- var ts = ps.map((p) => p.type).toList();
- for (int i = 0; i < permutedArgs.length; i++) {
- var tVar = permutedArgs[i];
- var tActual = tArgs2[i];
- var index = ts.indexOf(tVar);
- if (index >= 0 && rules.isSubTypeOf(tActual, actuals[index])) {
- actuals[index] = tActual;
- }
- }
- return actuals.any((x) => !x.isDynamic);
- }
-
- // Look for the first supertype of t1 with the same class name as t2.
- bool match(InterfaceType t1) {
- if (t1.element == t2.element) {
- return permute(t1.typeArguments);
- }
-
- if (t1 == rules.provider.objectType) return false;
-
- if (match(t1.superclass)) return true;
-
- for (final parent in t1.interfaces) {
- if (match(parent)) return true;
- }
-
- for (final parent in t1.mixins) {
- if (match(parent)) return true;
- }
- return false;
- }
-
- // We have that t1 = T1<dynamic, ..., dynamic>.
- // To match t1 against t2, we use the uninstantiated version
- // of t1, essentially treating it as an instantiation with
- // fresh variables, and solve for the variables.
- // t1.element.type will be of the form T1<X0, ..., Xn>
- if (!match(t1.element.type)) return null;
- var newT1 = t1.element.type.substitute4(actuals);
- // If we found a solution, return it.
- if (rules.isSubTypeOf(newT1, t2)) return actuals;
- return null;
- }
-
- /// These assume that e is not already a subtype of t
-
- bool _inferConditionalExpression(
- ConditionalExpression e, DartType t, errors) {
- return _inferExpression(e.thenExpression, t, errors) &&
- _inferExpression(e.elseExpression, t, errors);
- }
-
- bool _inferParenthesizedExpression(
- ParenthesizedExpression e, DartType t, errors) {
- return _inferExpression(e.expression, t, errors);
- }
-
- bool _inferInstanceCreationExpression(
- InstanceCreationExpression e, DartType t, errors) {
- var arguments = e.argumentList.arguments;
- var rawType = rules.getStaticType(e);
- // rawType is the instantiated type of the instance
- if (rawType is! InterfaceType) return false;
- var type = (rawType as InterfaceType);
- if (type.typeParameters == null ||
- type.typeParameters.length == 0) return false;
- if (e.constructorName.type == null) return false;
- // classTypeName is the type name of the class being instantiated
- var classTypeName = e.constructorName.type;
- // Check that we were not passed any type arguments
- if (classTypeName.typeArguments != null) return false;
- // Infer type arguments
- if (t is! InterfaceType) return false;
- var targs = _matchTypes(type, t);
- if (targs == null) return false;
- if (e.staticElement == null) return false;
- var constructorElement = e.staticElement;
- // From the constructor element get:
- // the instantiated type of the constructor, then
- // the uninstantiated element for the constructor, then
- // the uninstantiated type for the constructor
- var rawConstructorElement =
- constructorElement.type.element as ConstructorElement;
- var baseType = rawConstructorElement.type;
- if (baseType == null) return false;
- // From the interface type (instantiated), get:
- // the uninstantiated element, then
- // the uninstantiated type, then
- // the type arguments (aka the type parameters)
- var tparams = type.element.type.typeArguments;
- // Take the uninstantiated constructor type, and replace the type
- // parameters with the inferred arguments.
- var fType = baseType.substitute2(targs, tparams);
- {
- var rTypes = fType.normalParameterTypes;
- var oTypes = fType.optionalParameterTypes;
- var pTypes = new List.from(rTypes)..addAll(oTypes);
- var pArgs = arguments.where((x) => x is! NamedExpression);
- var pi = 0;
- for (var arg in pArgs) {
- if (pi >= pTypes.length) return false;
- var argType = pTypes[pi];
- if (!_inferExpression(arg, argType, errors)) return false;
- pi++;
- }
- var nTypes = fType.namedParameterTypes;
- for (var arg0 in arguments) {
- if (arg0 is! NamedExpression) continue;
- var arg = arg0 as NamedExpression;
- SimpleIdentifier nameNode = arg.name.label;
- String name = nameNode.name;
- var argType = nTypes[name];
- if (argType == null) return false;
- if (!_inferExpression(arg, argType, errors)) return false;
- }
- }
- annotateInstanceCreationExpression(e, targs);
- return true;
- }
-
- bool _inferNamedExpression(NamedExpression e, DartType t, errors) {
- return _inferExpression(e.expression, t, errors);
- }
-
- bool _inferFunctionExpression(FunctionExpression e, DartType t, errors) {
- if (t is! FunctionType) return false;
- var fType = t as FunctionType;
- var eType = e.staticType as FunctionType;
- if (eType is! FunctionType) return false;
-
- // We have a function literal, so we can treat the arrow type
- // as non-fuzzy. Since we're not improving on parameter types
- // currently, if this check fails then we cannot succeed, so
- // bail out. Otherwise, we never need to check the parameter types
- // again.
- if (!rules.isFunctionSubTypeOf(eType, fType,
- fuzzyArrows: false, ignoreReturn: true)) return false;
-
- // This only entered inference because of fuzzy typing.
- // The function type is already specific enough, we can just
- // succeed and treat it as a successful inference
- if (rules.isSubTypeOf(eType.returnType, fType.returnType)) return true;
-
- // Fuzzy typing again, handle the void case (not caught by the previous)
- if (fType.returnType.isVoid) return true;
-
- if (e.body is! ExpressionFunctionBody) return false;
- var body = (e.body as ExpressionFunctionBody).expression;
- if (!_inferExpression(body, fType.returnType, errors)) return false;
-
- // TODO(leafp): Try narrowing the argument types if possible
- // to get better code in the function body. This requires checking
- // that the body is well-typed at the more specific type.
-
- // At this point, we know that the parameter types are in the appropriate subtype
- // relation, and we have checked that we can type the body at the appropriate return
- // type, so we can are done.
- annotateFunctionExpression(e, fType.returnType);
- return true;
- }
-
- bool _inferListLiteral(ListLiteral e, DartType t, errors) {
- var dyn = rules.provider.dynamicType;
- var listT = rules.provider.listType.substitute4([dyn]);
- // List <: t (using dart rules) must be true
- if (!listT.isSubtypeOf(t)) return false;
- // The list literal must have no type arguments
- if (e.typeArguments != null) return false;
- if (t is! InterfaceType) return false;
- var targs = _matchTypes(listT, t);
- if (targs == null) return false;
- assert(targs.length == 1);
- var etype = targs[0];
- assert(!etype.isDynamic);
- var elements = e.elements;
- var b = elements.every((e) => _inferExpression(e, etype, errors));
- if (b) annotateListLiteral(e, targs);
- return b;
- }
-
- bool _inferMapLiteral(MapLiteral e, DartType t, errors) {
- var dyn = rules.provider.dynamicType;
- var mapT = rules.provider.mapType.substitute4([dyn, dyn]);
- // Map <: t (using dart rules) must be true
- if (!mapT.isSubtypeOf(t)) return false;
- // The map literal must have no type arguments
- if (e.typeArguments != null) return false;
- if (t is! InterfaceType) return false;
- var targs = _matchTypes(mapT, t);
- if (targs == null) return false;
- assert(targs.length == 2);
- var kType = targs[0];
- var vType = targs[1];
- assert(!(kType.isDynamic && vType.isDynamic));
- var entries = e.entries;
- bool inferEntry(MapLiteralEntry entry) {
- return _inferExpression(entry.key, kType, errors) &&
- _inferExpression(entry.value, vType, errors);
- }
- var b = entries.every(inferEntry);
- if (b) annotateMapLiteral(e, targs);
- return b;
- }
-}
+export 'package:analyzer/src/task/strong/rules.dart';
diff --git a/lib/src/codegen/code_generator.dart b/lib/src/codegen/code_generator.dart
index eb4135d..12cc751 100644
--- a/lib/src/codegen/code_generator.dart
+++ b/lib/src/codegen/code_generator.dart
@@ -23,7 +23,7 @@
CodeGenerator(AbstractCompiler compiler)
: compiler = compiler,
- rules = compiler.rules,
+ rules = new TypeRules(compiler.context.typeProvider),
context = compiler.context,
options = compiler.options.codegenOptions;
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
index 99a2408..872a7a5 100644
--- a/lib/src/codegen/js_codegen.dart
+++ b/lib/src/codegen/js_codegen.dart
@@ -105,11 +105,10 @@
Map<String, DartType> _objectMembers;
- JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary,
+ JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary,
this._extensionTypes, this._fieldsNeedingStorage)
: compiler = compiler,
options = compiler.options.codegenOptions,
- rules = compiler.rules,
_types = compiler.context.typeProvider {
_loader = new ModuleItemLoadOrder(_emitModuleItem);
@@ -125,7 +124,7 @@
JS.Program emitLibrary(LibraryUnit library) {
// Modify the AST to make coercions explicit.
- new CoercionReifier(library, compiler).reify();
+ new CoercionReifier(library, rules).reify();
// Build the public namespace for this library. This allows us to do
// constant time lookups (contrast with `Element.getChild(name)`).
@@ -3280,7 +3279,7 @@
var library = unit.library.element.library;
var fields = findFieldsNeedingStorage(unit);
var codegen =
- new JSCodegenVisitor(compiler, library, _extensionTypes, fields);
+ new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields);
var module = codegen.emitLibrary(unit);
var out = compiler.getOutputPath(library.source.uri);
return writeJsLibrary(module, out,
diff --git a/lib/src/codegen/reify_coercions.dart b/lib/src/codegen/reify_coercions.dart
index 8a2c542..3bf1d91 100644
--- a/lib/src/codegen/reify_coercions.dart
+++ b/lib/src/codegen/reify_coercions.dart
@@ -9,7 +9,6 @@
import 'package:analyzer/src/generated/element.dart';
import 'package:logging/logging.dart' as logger;
-import '../compiler.dart' show AbstractCompiler;
import '../checker/rules.dart';
import '../info.dart';
@@ -100,11 +99,11 @@
CoercionReifier._(
this._cm, this._tm, this._vm, this._library, this._inferrer);
- factory CoercionReifier(LibraryUnit library, AbstractCompiler compiler) {
+ factory CoercionReifier(LibraryUnit library, TypeRules rules) {
var vm = new VariableManager();
var tm = new TypeManager(library.library.element.enclosingElement, vm);
var cm = new CoercionManager(vm, tm);
- var inferrer = new _Inference(compiler.rules, tm);
+ var inferrer = new _Inference(rules, tm);
return new CoercionReifier._(cm, tm, vm, library, inferrer);
}
diff --git a/lib/src/compiler.dart b/lib/src/compiler.dart
index 8194518..d4f61a4 100644
--- a/lib/src/compiler.dart
+++ b/lib/src/compiler.dart
@@ -25,8 +25,6 @@
import 'package:path/path.dart' as path;
import 'analysis_context.dart';
-import 'checker/checker.dart';
-import 'checker/rules.dart';
import 'codegen/html_codegen.dart' as html_codegen;
import 'codegen/js_codegen.dart';
import 'info.dart'
@@ -100,7 +98,7 @@
_dartCore = context.typeProvider.objectType.element.library;
}
- ErrorCollector get reporter => checker.reporter;
+ ErrorCollector get reporter => super.reporter;
/// Compiles every file in [options.inputs].
/// Returns true on successful compile.
@@ -211,10 +209,7 @@
for (var element in unitElements) {
var unit = context.resolveCompilationUnit(element.source, library);
units.add(unit);
- failureInLib = logErrors(element.source) || failureInLib;
- checker.reset();
- checker.visitCompilationUnit(unit);
- if (checker.failure) failureInLib = true;
+ failureInLib = computeErrors(element.source) || failureInLib;
}
if (failureInLib) _compilationRecord[library] = false;
@@ -345,19 +340,11 @@
abstract class AbstractCompiler {
final CompilerOptions options;
final AnalysisContext context;
- final CodeChecker checker;
+ final AnalysisErrorListener reporter;
- AbstractCompiler(AnalysisContext context, CompilerOptions options,
- [AnalysisErrorListener reporter])
- : context = context,
- options = options,
- checker = new CodeChecker(
- new TypeRules(context.typeProvider, options: options.strongOptions),
- reporter ?? AnalysisErrorListener.NULL_LISTENER);
+ AbstractCompiler(this.context, this.options, [this.reporter]);
String get outputDir => options.codegenOptions.outputDir;
- TypeRules get rules => checker.rules;
- AnalysisErrorListener get reporter => checker.reporter;
Uri stringToUri(String uriString) {
var uri = uriString.startsWith('dart:') || uriString.startsWith('package:')
@@ -440,23 +427,33 @@
/// Log any errors encountered when resolving [source] and return whether any
/// errors were found.
- bool logErrors(Source source) {
- List<AnalysisError> errors = context.computeErrors(source);
+ bool computeErrors(Source source) {
+ AnalysisContext errorContext = context;
+ // TODO(jmesserly): should this be a fix somewhere in analyzer?
+ // otherwise we fail to find the parts.
+ if (source.uri.scheme == 'dart') {
+ errorContext = context.sourceFactory.dartSdk.context;
+ }
+ List<AnalysisError> errors = errorContext.computeErrors(source);
bool failure = false;
- if (errors.isNotEmpty) {
- for (var error in errors) {
- // Always skip TODOs.
- if (error.errorCode.type == ErrorType.TODO) continue;
+ for (var error in errors) {
+ // Always skip TODOs.
+ if (error.errorCode.type == ErrorType.TODO) continue;
- // Skip hints for now. In the future these could be turned on via flags.
- if (error.errorCode.errorSeverity.ordinal <
- ErrorSeverity.WARNING.ordinal) {
- continue;
+ // TODO(jmesserly): for now, treat DDC errors as having a different
+ // error level from Analayzer ones.
+ if (error.errorCode.name.startsWith('dev_compiler')) {
+ reporter.onError(error);
+ if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) {
+ failure = true;
}
-
+ } else if (error.errorCode.errorSeverity.ordinal >=
+ ErrorSeverity.WARNING.ordinal) {
// All analyzer warnings or errors are errors for DDC.
failure = true;
reporter.onError(error);
+ } else {
+ // Skip hints for now.
}
}
return failure;
diff --git a/lib/src/info.dart b/lib/src/info.dart
index c48fb36..75e74a3 100644
--- a/lib/src/info.dart
+++ b/lib/src/info.dart
@@ -8,20 +8,19 @@
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
-import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/parser.dart';
-import 'checker/rules.dart';
import 'utils.dart' as utils;
+import 'package:analyzer/src/task/strong/info.dart';
+export 'package:analyzer/src/task/strong/info.dart';
/// Represents a summary of the results collected by running the program
/// checker.
class CheckerResults {
final List<LibraryInfo> libraries;
- final TypeRules rules;
final bool failure;
- CheckerResults(this.libraries, this.rules, this.failure);
+ CheckerResults(this.libraries, this.failure);
}
/// Computed information about each library.
@@ -102,474 +101,3 @@
return clone;
}
}
-
-// The abstract type of coercions mapping one type to another.
-// This class also exposes static builder functions which
-// check for errors and reduce redundant coercions to the identity.
-abstract class Coercion {
- final DartType fromType;
- final DartType toType;
- Coercion(this.fromType, this.toType);
- static Coercion cast(DartType fromT, DartType toT) => new Cast(fromT, toT);
- static Coercion identity(DartType type) => new Identity(type);
- static Coercion error() => new CoercionError();
-}
-
-// Coercion which casts one type to another
-class Cast extends Coercion {
- Cast(DartType fromType, DartType toType) : super(fromType, toType);
-}
-
-// The identity coercion
-class Identity extends Coercion {
- Identity(DartType fromType) : super(fromType, fromType);
-}
-
-// The error coercion. This coercion signals that a coercion
-// could not be generated. The code generator should not see
-// these.
-class CoercionError extends Coercion {
- CoercionError() : super(null, null);
-}
-
-// TODO(jmesserly): this could use some refactoring. These are essentially
-// like ErrorCodes in analyzer, but we're including some details in our message.
-// Analyzer instead has template strings, and replaces '{0}' with the first
-// argument.
-abstract class StaticInfo {
- /// AST Node this info is attached to.
- AstNode get node;
-
- // TODO(jmesserly): review the usage of error codes. We probably want our own,
- // as well as some DDC specific [ErrorType]s.
- ErrorCode toErrorCode();
-
- // TODO(jmesserly): what convention to use here?
- String get name => 'dev_compiler.$runtimeType';
-
- List<Object> get arguments => [node];
-
- AnalysisError toAnalysisError() {
- int begin = node is AnnotatedNode
- ? (node as AnnotatedNode).firstTokenAfterCommentAndMetadata.offset
- : node.offset;
- int length = node.end - begin;
- var source = (node.root as CompilationUnit).element.source;
- return new AnalysisError(source, begin, length, toErrorCode(), arguments);
- }
-}
-
-/// Implicitly injected expression conversion.
-abstract class CoercionInfo extends StaticInfo {
- final TypeRules rules;
-
- final Expression node;
-
- DartType get convertedType;
-
- CoercionInfo(this.rules, this.node);
-
- DartType get baseType => rules.getStaticType(node);
- DartType get staticType => convertedType;
-
- String get message;
- toErrorCode() => new HintCode(name, message);
-
- static const String _propertyName = 'dev_compiler.src.info.CoercionInfo';
-
- /// Gets the coercion info associated with this node.
- static CoercionInfo get(AstNode node) => node.getProperty(_propertyName);
-
- /// Sets the coercion info associated with this node.
- static CoercionInfo set(AstNode node, CoercionInfo info) {
- node.setProperty(_propertyName, info);
- return info;
- }
-}
-
-// Base class for all casts from base type to sub type.
-abstract class DownCast extends CoercionInfo {
- Cast _cast;
-
- DownCast._internal(TypeRules rules, Expression expression, this._cast)
- : super(rules, expression) {
- assert(_cast.toType != baseType &&
- _cast.fromType == baseType &&
- (baseType.isDynamic ||
- // Call methods make the following non-redundant
- _cast.toType.isSubtypeOf(baseType) ||
- baseType.isAssignableTo(_cast.toType)));
- }
-
- Cast get cast => _cast;
-
- DartType get convertedType => _cast.toType;
-
- @override List<Object> get arguments => [node, baseType, convertedType];
- @override String get message => '{0} ({1}) will need runtime check '
- 'to cast to type {2}';
-
- // Factory to create correct DownCast variant.
- static StaticInfo create(TypeRules rules, Expression expression, Cast cast,
- {String reason}) {
- final fromT = cast.fromType;
- final toT = cast.toType;
-
- // toT <:_R fromT => to <: fromT
- // NB: classes with call methods are subtypes of function
- // types, but the function type is not assignable to the class
- assert(toT.isSubtypeOf(fromT) || fromT.isAssignableTo(toT));
-
- // Handle null call specially.
- if (expression is NullLiteral) {
- // TODO(vsm): Create a NullCast for this once we revisit nonnullability.
- return new DownCastImplicit(rules, expression, cast);
- }
-
- // Inference "casts":
- if (expression is Literal) {
- // fromT should be an exact type - this will almost certainly fail at
- // runtime.
- return new StaticTypeError(rules, expression, toT, reason: reason);
- }
- if (expression is FunctionExpression) {
- // fromT should be an exact type - this will almost certainly fail at
- // runtime.
- return new UninferredClosure(rules, expression, cast);
- }
- if (expression is InstanceCreationExpression) {
- // fromT should be an exact type - this will almost certainly fail at
- // runtime.
- return new StaticTypeError(rules, expression, toT, reason: reason);
- }
-
- // Composite cast: these are more likely to fail.
- if (!rules.isGroundType(toT)) {
- // This cast is (probably) due to our different treatment of dynamic.
- // It may be more likely to fail at runtime.
- return new DownCastComposite(rules, expression, cast);
- }
-
- // Dynamic cast
- if (fromT.isDynamic) {
- return new DynamicCast(rules, expression, cast);
- }
-
- // Assignment cast
- var parent = expression.parent;
- if (parent is VariableDeclaration && (parent.initializer == expression)) {
- return new AssignmentCast(rules, expression, cast);
- }
-
- // Other casts
- return new DownCastImplicit(rules, expression, cast);
- }
-}
-
-//
-// Standard down casts. These casts are implicitly injected by the compiler.
-//
-
-// A down cast from dynamic to T.
-class DynamicCast extends DownCast {
- DynamicCast(TypeRules rules, Expression expression, Cast cast)
- : super._internal(rules, expression, cast);
-
- toErrorCode() => new HintCode(name, message);
-}
-
-// A down cast due to a variable declaration to a ground type. E.g.,
-// T x = expr;
-// where T is ground. We exclude non-ground types as these behave differently
-// compared to standard Dart.
-class AssignmentCast extends DownCast {
- AssignmentCast(TypeRules rules, Expression expression, Cast cast)
- : super._internal(rules, expression, cast);
-
- toErrorCode() => new HintCode(name, message);
-}
-
-//
-// Temporary "casts" of allocation sites - literals, constructor invocations,
-// and closures. These should be handled by contextual inference. In most
-// cases, inference will be sufficient, though in some it may unmask an actual
-// error: e.g.,
-// List<int> l = [1, 2, 3]; // Inference succeeds
-// List<String> l = [1, 2, 3]; // Inference reveals static type error
-// We're marking all as warnings for now.
-//
-// TODO(vsm,leafp): Remove this.
-class UninferredClosure extends DownCast {
- UninferredClosure(TypeRules rules, FunctionExpression expression, Cast cast)
- : super._internal(rules, expression, cast);
-
- toErrorCode() => new StaticTypeWarningCode(name, message);
-}
-
-//
-// Implicit down casts. These are only injected by the compiler by flag.
-//
-
-// A down cast to a non-ground type. These behave differently from standard
-// Dart and may be more likely to fail at runtime.
-class DownCastComposite extends DownCast {
- DownCastComposite(TypeRules rules, Expression expression, Cast cast)
- : super._internal(rules, expression, cast);
-
- toErrorCode() => new StaticTypeWarningCode(name, message);
-}
-
-// A down cast to a non-ground type. These behave differently from standard
-// Dart and may be more likely to fail at runtime.
-class DownCastImplicit extends DownCast {
- DownCastImplicit(TypeRules rules, Expression expression, Cast cast)
- : super._internal(rules, expression, cast);
-
- toErrorCode() => new HintCode(name, message);
-}
-
-// An inferred type for the wrapped expression, which may need to be
-// reified into the term
-abstract class InferredTypeBase extends CoercionInfo {
- final DartType _type;
-
- InferredTypeBase._internal(TypeRules rules, Expression expression, this._type)
- : super(rules, expression);
-
- DartType get type => _type;
- DartType get convertedType => type;
- @override String get message => '{0} has inferred type {1}';
- @override List get arguments => [node, type];
-
- toErrorCode() => new HintCode(name, message);
-}
-
-// Standard / unspecialized inferred type
-class InferredType extends InferredTypeBase {
- InferredType(TypeRules rules, Expression expression, DartType type)
- : super._internal(rules, expression, type);
-
- // Factory to create correct InferredType variant.
- static InferredTypeBase create(
- TypeRules rules, Expression expression, DartType type) {
- // Specialized inference:
- if (expression is Literal) {
- return new InferredTypeLiteral(rules, expression, type);
- }
- if (expression is InstanceCreationExpression) {
- return new InferredTypeAllocation(rules, expression, type);
- }
- if (expression is FunctionExpression) {
- return new InferredTypeClosure(rules, expression, type);
- }
- return new InferredType(rules, expression, type);
- }
-}
-
-// An infered type for a literal expression.
-class InferredTypeLiteral extends InferredTypeBase {
- InferredTypeLiteral(TypeRules rules, Expression expression, DartType type)
- : super._internal(rules, expression, type);
-}
-
-// An inferred type for a non-literal allocation site.
-class InferredTypeAllocation extends InferredTypeBase {
- InferredTypeAllocation(TypeRules rules, Expression expression, DartType type)
- : super._internal(rules, expression, type);
-}
-
-// An inferred type for a closure expression
-class InferredTypeClosure extends InferredTypeBase {
- InferredTypeClosure(TypeRules rules, Expression expression, DartType type)
- : super._internal(rules, expression, type);
-}
-
-class DynamicInvoke extends CoercionInfo {
- DynamicInvoke(TypeRules rules, Expression expression)
- : super(rules, expression);
-
- DartType get convertedType => rules.provider.dynamicType;
- String get message => '{0} requires dynamic invoke';
- toErrorCode() => new HintCode(name, message);
-
- static const String _propertyName = 'dev_compiler.src.info.DynamicInvoke';
-
- /// Whether this [node] is the target of a dynamic operation.
- static bool get(AstNode node) {
- var value = node.getProperty(_propertyName);
- return value != null ? value : false;
- }
-
- /// Sets whether this node is the target of a dynamic operation.
- static bool set(AstNode node, bool value) {
- // Free the storage for things that aren't dynamic.
- if (value == false) value = null;
- node.setProperty(_propertyName, value);
- return value;
- }
-}
-
-abstract class StaticError extends StaticInfo {
- final AstNode node;
-
- StaticError(this.node);
-
- String get message;
-
- toErrorCode() => new CompileTimeErrorCode(name, message);
-}
-
-class StaticTypeError extends StaticError {
- final DartType baseType;
- final DartType expectedType;
- String reason = null;
-
- StaticTypeError(TypeRules rules, Expression expression, this.expectedType,
- {this.reason})
- : baseType = rules.getStaticType(expression),
- super(expression);
-
- @override List<Object> get arguments => [node, baseType, expectedType];
- @override String get message =>
- 'Type check failed: {0} ({1}) is not of type {2}' +
- ((reason == null) ? '' : ' because $reason');
-}
-
-class InvalidVariableDeclaration extends StaticError {
- final DartType expectedType;
-
- InvalidVariableDeclaration(
- TypeRules rules, AstNode declaration, this.expectedType)
- : super(declaration);
-
- @override List<Object> get arguments => [expectedType];
- @override String get message => 'Type check failed: null is not of type {0}';
-}
-
-class InvalidParameterDeclaration extends StaticError {
- final DartType expectedType;
-
- InvalidParameterDeclaration(
- TypeRules rules, FormalParameter declaration, this.expectedType)
- : super(declaration);
-
- @override List<Object> get arguments => [node, expectedType];
- @override String get message => 'Type check failed: {0} is not of type {1}';
-}
-
-class NonGroundTypeCheckInfo extends StaticInfo {
- final DartType type;
- final AstNode node;
-
- NonGroundTypeCheckInfo(this.node, this.type) {
- assert(node is IsExpression || node is AsExpression);
- }
-
- @override List<Object> get arguments => [type];
- String get message =>
- "Runtime check on non-ground type {0} may throw StrongModeError";
-
- toErrorCode() => new HintCode(name, message);
-}
-
-// Invalid override of an instance member of a class.
-abstract class InvalidOverride extends StaticError {
- /// Member declaration with the invalid override.
- final ExecutableElement element;
-
- /// Type (class or interface) that provides the base declaration.
- final InterfaceType base;
-
- /// Actual type of the overridden member.
- final DartType subType;
-
- /// Actual type of the base member.
- final DartType baseType;
-
- /// Whether the error comes from combining a base class and an interface
- final bool fromBaseClass;
-
- /// Whether the error comes from a mixin (either overriding a base class or an
- /// interface declaration).
- final bool fromMixin;
-
- InvalidOverride(
- AstNode node, this.element, this.base, this.subType, this.baseType)
- : fromBaseClass = node is ExtendsClause,
- fromMixin = node.parent is WithClause,
- super(node);
-
- ClassElement get parent => element.enclosingElement;
-
- @override List<Object> get arguments =>
- [parent.name, element.name, subType, base, baseType];
-
- String _messageHelper(String errorName) {
- var lcErrorName = errorName.toLowerCase();
- var intro = fromBaseClass
- ? 'Base class introduces an $lcErrorName'
- : (fromMixin ? 'Mixin introduces an $lcErrorName' : errorName);
- return '$intro. The type of {0}.{1} ({2}) is not a '
- 'subtype of {3}.{1} ({4}).';
- }
-}
-
-// Invalid override due to incompatible type. I.e., the overridden signature
-// is not compatible with the original.
-class InvalidMethodOverride extends InvalidOverride {
- InvalidMethodOverride(AstNode node, ExecutableElement element,
- InterfaceType base, FunctionType subType, FunctionType baseType)
- : super(node, element, base, subType, baseType);
-
- String get message => _messageHelper('Invalid override');
-}
-
-/// Used to mark unexpected situations in our compiler were we couldn't compute
-/// the type of an expression.
-// TODO(sigmund): This is normally a result of another error that is caught by
-// the analyzer, so this should likely be removed in the future.
-class MissingTypeError extends StaticInfo {
- final AstNode node;
- toErrorCode() => new StaticTypeWarningCode(name, message);
-
- MissingTypeError(this.node);
-
- @override List<Object> get arguments => [node, node.runtimeType];
- String get message => "type analysis didn't compute the type of: {0} {1}";
-}
-
-/// Dart constructors have one weird quirk, illustrated with this example:
-///
-/// class Base {
-/// var x;
-/// Base() : x = print('Base.1') {
-/// print('Base.2');
-/// }
-/// }
-///
-/// class Derived extends Base {
-/// var y, z;
-/// Derived()
-/// : y = print('Derived.1'),
-/// super(),
-/// z = print('Derived.2') {
-/// print('Derived.3');
-/// }
-/// }
-///
-/// The order will be Derived.1, Base.1, Derived.2, Base.2, Derived.3; this
-/// ordering preserves the invariant that code can't observe uninitialized
-/// state, however it results in super constructor body not being run
-/// immediately after super initializers. Normally this isn't observable, but it
-/// could be if initializers have side effects.
-///
-/// Better to have `super` at the end, as required by the Dart style guide:
-/// <http://goo.gl/q1T4BB>
-///
-/// For now this is the only pattern we support.
-class InvalidSuperInvocation extends StaticError {
- InvalidSuperInvocation(SuperConstructorInvocation node) : super(node);
-
- @override String get message => "super call must be last in an initializer "
- "list (see http://goo.gl/q1T4BB): {0}";
-}
diff --git a/lib/src/server/server.dart b/lib/src/server/server.dart
index 9a6320f..079b231 100644
--- a/lib/src/server/server.dart
+++ b/lib/src/server/server.dart
@@ -84,7 +84,7 @@
var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
_log.fine('Compiled ${_libraries.length} libraries in ${time} s\n');
return new CheckerResults(
- _libraries, rules, _failure || options.codegenOptions.forceCompile);
+ _libraries, _failure || options.codegenOptions.forceCompile);
}
bool _buildSource(SourceNode node) {
@@ -154,9 +154,7 @@
for (var unit in libraryUnit.libraryThenParts) {
var unitSource = unit.element.source;
// TODO(sigmund): integrate analyzer errors with static-info (issue #6).
- failureInLib = logErrors(unitSource) || failureInLib;
- checker.visitCompilationUnit(unit);
- if (checker.failure) failureInLib = true;
+ failureInLib = computeErrors(unitSource) || failureInLib;
}
if (failureInLib) {
_failure = true;
@@ -310,4 +308,4 @@
}
final _log = new Logger('dev_compiler.src.server');
-final _earlyErrorResult = new CheckerResults(const [], null, true);
+final _earlyErrorResult = new CheckerResults(const [], true);
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index b561c59..b38f531 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -380,30 +380,6 @@
// TODO(vsm): Move this onto the appropriate class. Ideally, we'd attach
// it to TypeProvider.
-final _objectMap = new Expando('providerToObjectMap');
-Map<String, DartType> getObjectMemberMap(TypeProvider typeProvider) {
- var map = _objectMap[typeProvider] as Map<String, DartType>;
- if (map == null) {
- map = <String, DartType>{};
- _objectMap[typeProvider] = map;
- var objectType = typeProvider.objectType;
- var element = objectType.element;
- // Only record methods (including getters) with no parameters. As parameters are contravariant wrt
- // type, using Object's version may be too strict.
- // Add instance methods.
- element.methods.where((method) => !method.isStatic).forEach((method) {
- map[method.name] = method.type;
- });
- // Add getters.
- element.accessors
- .where((member) => !member.isStatic && member.isGetter)
- .forEach((member) {
- map[member.name] = member.type.returnType;
- });
- }
- return map;
-}
-
/// Searches all supertype, in order of most derived members, to see if any
/// [match] a condition. If so, returns the first match, otherwise returns null.
InterfaceType findSupertype(InterfaceType type, bool match(InterfaceType t)) {
diff --git a/lib/strong_mode.dart b/lib/strong_mode.dart
index 99cef5c..45d17ab 100644
--- a/lib/strong_mode.dart
+++ b/lib/strong_mode.dart
@@ -31,6 +31,7 @@
/// A type checker for Dart code that operates under stronger rules, and has
/// the ability to do local type inference in some situations.
+// TODO(jmesserly): remove this class.
class StrongChecker {
final AnalysisContext _context;
final CodeChecker _checker;
@@ -41,25 +42,28 @@
factory StrongChecker(AnalysisContext context, StrongModeOptions options) {
// TODO(vsm): Remove this once analyzer_cli is completely switched to the
// task model.
- if (!AnalysisEngine
- .instance.useTaskModel) enableDevCompilerInference(context, options);
- var rules = new TypeRules(context.typeProvider, options: options);
- var reporter = new _ErrorCollector(options.hints);
- var checker = new CodeChecker(rules, reporter);
- return new StrongChecker._(context, checker, reporter);
+ if (!AnalysisEngine.instance.useTaskModel) {
+ enableDevCompilerInference(context, options);
+ var rules = new TypeRules(context.typeProvider);
+ var reporter = new _ErrorCollector(options.hints);
+ var checker = new CodeChecker(rules, reporter);
+ return new StrongChecker._(context, checker, reporter);
+ }
+ return new StrongChecker._(context, null, null);
}
/// Computes and returns DDC errors for the [source].
AnalysisErrorInfo computeErrors(Source source) {
var errors = new List<AnalysisError>();
- _reporter.errors = errors;
+ if (_checker != null) {
+ _reporter.errors = errors;
- for (Source librarySource in _context.getLibrariesContaining(source)) {
- var resolved = _context.resolveCompilationUnit2(source, librarySource);
- _checker.visitCompilationUnit(resolved);
+ for (Source librarySource in _context.getLibrariesContaining(source)) {
+ var resolved = _context.resolveCompilationUnit2(source, librarySource);
+ _checker.visitCompilationUnit(resolved);
+ }
+ _reporter.errors = null;
}
- _reporter.errors = null;
-
return new AnalysisErrorInfoImpl(errors, _context.getLineInfo(source));
}
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 7c9dd37..0f56a4c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -41,3 +41,6 @@
# Similar to dartdevc, but runs the (single) entry point with iojs (requires
# a very recent iojs next-nightly version).
dartdevrun: devrun
+
+dependency_overrides:
+ analyzer: { path: ../dart/sdk/pkg/analyzer }
diff --git a/test/all_tests.dart b/test/all_tests.dart
index bd97444..b7c60ca 100644
--- a/test/all_tests.dart
+++ b/test/all_tests.dart
@@ -7,8 +7,6 @@
import 'package:test/test.dart';
-import 'checker/checker_test.dart' as checker_test;
-import 'checker/inferred_type_test.dart' as inferred_type_test;
import 'checker/self_host_test.dart' as self_host;
import 'closure/closure_annotation_test.dart' as closure_annotation_test;
import 'closure/closure_type_test.dart' as closure_type_test;
@@ -19,8 +17,6 @@
void main() {
group('end-to-end', e2e.main);
- group('inferred types', inferred_type_test.main);
- group('checker', checker_test.main);
group('report', report_test.main);
group('dependency_graph', dependency_graph_test.main);
group('codegen', () => codegen_test.main([]));
diff --git a/test/testing.dart b/test/testing.dart
index 6874ed3..faa90be 100644
--- a/test/testing.dart
+++ b/test/testing.dart
@@ -28,17 +28,21 @@
import 'package:dev_compiler/src/utils.dart';
/// Shared analysis context used for compilation.
-final realSdkContext = createAnalysisContextWithSources(
- new StrongModeOptions(),
- new SourceResolverOptions(
- dartSdkPath: getSdkDir().path,
- customUrlMappings: {
- 'package:expect/expect.dart': _testCodegenPath('expect.dart'),
- 'package:async_helper/async_helper.dart':
- _testCodegenPath('async_helper.dart'),
- 'package:unittest/unittest.dart': _testCodegenPath('unittest.dart'),
- 'package:dom/dom.dart': _testCodegenPath('sunflower', 'dom.dart')
- }))..analysisOptions.cacheSize = 512;
+final AnalysisContext realSdkContext = () {
+ var context = createAnalysisContextWithSources(
+ new StrongModeOptions(),
+ new SourceResolverOptions(
+ dartSdkPath: getSdkDir().path,
+ customUrlMappings: {
+ 'package:expect/expect.dart': _testCodegenPath('expect.dart'),
+ 'package:async_helper/async_helper.dart':
+ _testCodegenPath('async_helper.dart'),
+ 'package:unittest/unittest.dart': _testCodegenPath('unittest.dart'),
+ 'package:dom/dom.dart': _testCodegenPath('sunflower', 'dom.dart')
+ }));
+ (context.analysisOptions as AnalysisOptionsImpl).cacheSize = 512;
+ return context;
+}();
String _testCodegenPath(String p1, [String p2]) =>
path.join(testDirectory, 'codegen', p1, p2);