Index: sdk/lib/_internal/compiler/implementation/typechecker.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/typechecker.dart b/sdk/lib/_internal/compiler/implementation/typechecker.dart |
deleted file mode 100644 |
index b1fb8cd434fc7613472b666e0482a5b58f8e88e3..0000000000000000000000000000000000000000 |
--- a/sdk/lib/_internal/compiler/implementation/typechecker.dart |
+++ /dev/null |
@@ -1,1786 +0,0 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-part of dart2js; |
- |
-class TypeCheckerTask extends CompilerTask { |
- TypeCheckerTask(Compiler compiler) : super(compiler); |
- String get name => "Type checker"; |
- |
- void check(TreeElements elements) { |
- AstElement element = elements.analyzedElement; |
- compiler.withCurrentElement(element, () { |
- measure(() { |
- Node tree = element.node; |
- TypeCheckerVisitor visitor = |
- new TypeCheckerVisitor(compiler, elements, compiler.types); |
- if (element.isField) { |
- visitor.analyzingInitializer = true; |
- } |
- tree.accept(visitor); |
- }); |
- }); |
- } |
-} |
- |
-/** |
- * Class used to report different warnings for differrent kinds of members. |
- */ |
-class MemberKind { |
- static const MemberKind METHOD = const MemberKind("method"); |
- static const MemberKind OPERATOR = const MemberKind("operator"); |
- static const MemberKind GETTER = const MemberKind("getter"); |
- static const MemberKind SETTER = const MemberKind("setter"); |
- |
- final String name; |
- |
- const MemberKind(this.name); |
- |
- String toString() => name; |
-} |
- |
-/** |
- * [ElementAccess] represents the access of [element], either as a property |
- * access or invocation. |
- */ |
-abstract class ElementAccess { |
- Element get element; |
- |
- DartType computeType(Compiler compiler); |
- |
- /// Returns [: true :] if the element can be access as an invocation. |
- bool isCallable(Compiler compiler) { |
- if (element != null && element.isAbstractField) { |
- AbstractFieldElement abstractFieldElement = element; |
- if (abstractFieldElement.getter == null) { |
- // Setters cannot be invoked as function invocations. |
- return false; |
- } |
- } |
- return compiler.types.isAssignable( |
- computeType(compiler), compiler.functionClass.computeType(compiler)); |
- } |
-} |
- |
-/// An access of a instance member. |
-class MemberAccess extends ElementAccess { |
- final MemberSignature member; |
- |
- MemberAccess(MemberSignature this.member); |
- |
- Element get element => member.declarations.first.element; |
- |
- DartType computeType(Compiler compiler) => member.type; |
- |
- String toString() => 'MemberAccess($member)'; |
-} |
- |
-/// An access of an unresolved element. |
-class DynamicAccess implements ElementAccess { |
- const DynamicAccess(); |
- |
- Element get element => null; |
- |
- DartType computeType(Compiler compiler) => const DynamicType(); |
- |
- bool isCallable(Compiler compiler) => true; |
- |
- String toString() => 'DynamicAccess'; |
-} |
- |
-/// An access of the `assert` method. |
-class AssertAccess implements ElementAccess { |
- const AssertAccess(); |
- |
- Element get element => null; |
- |
- DartType computeType(Compiler compiler) { |
- return new FunctionType.synthesized( |
- const VoidType(), |
- <DartType>[const DynamicType()]); |
- } |
- |
- bool isCallable(Compiler compiler) => true; |
- |
- String toString() => 'AssertAccess'; |
-} |
- |
-/** |
- * An access of a resolved top-level or static property or function, or an |
- * access of a resolved element through [:this:]. |
- */ |
-class ResolvedAccess extends ElementAccess { |
- final Element element; |
- |
- ResolvedAccess(Element this.element) { |
- assert(element != null); |
- } |
- |
- DartType computeType(Compiler compiler) { |
- if (element.isGetter) { |
- FunctionType functionType = element.computeType(compiler); |
- return functionType.returnType; |
- } else if (element.isSetter) { |
- FunctionType functionType = element.computeType(compiler); |
- if (functionType.parameterTypes.length != 1) { |
- // TODO(johnniwinther,karlklose): this happens for malformed static |
- // setters. Treat them the same as instance members. |
- return const DynamicType(); |
- } |
- return functionType.parameterTypes.first; |
- } else { |
- return element.computeType(compiler); |
- } |
- } |
- |
- String toString() => 'ResolvedAccess($element)'; |
-} |
- |
-/// An access to a promoted variable. |
-class PromotedAccess extends ElementAccess { |
- final VariableElement element; |
- final DartType type; |
- |
- PromotedAccess(VariableElement this.element, DartType this.type) { |
- assert(element != null); |
- assert(type != null); |
- } |
- |
- DartType computeType(Compiler compiler) => type; |
- |
- String toString() => 'PromotedAccess($element,$type)'; |
-} |
- |
-/** |
- * An access of a resolved top-level or static property or function, or an |
- * access of a resolved element through [:this:]. |
- */ |
-class TypeAccess extends ElementAccess { |
- final DartType type; |
- TypeAccess(DartType this.type) { |
- assert(type != null); |
- } |
- |
- Element get element => type.element; |
- |
- DartType computeType(Compiler compiler) => type; |
- |
- String toString() => 'TypeAccess($type)'; |
-} |
- |
-/** |
- * An access of a type literal. |
- */ |
-class TypeLiteralAccess extends ElementAccess { |
- final DartType type; |
- |
- TypeLiteralAccess(this.type) { |
- assert(type != null); |
- } |
- |
- Element get element => type.element; |
- |
- DartType computeType(Compiler compiler) => compiler.typeClass.rawType; |
- |
- String toString() => 'TypeLiteralAccess($type)'; |
-} |
- |
- |
-/// An access to the 'call' method of a function type. |
-class FunctionCallAccess implements ElementAccess { |
- final Element element; |
- final DartType type; |
- |
- const FunctionCallAccess(this.element, this.type); |
- |
- DartType computeType(Compiler compiler) => type; |
- |
- bool isCallable(Compiler compiler) => true; |
- |
- String toString() => 'FunctionAccess($element, $type)'; |
-} |
- |
- |
-/// An is-expression that potentially promotes a variable. |
-class TypePromotion { |
- final Send node; |
- final VariableElement variable; |
- final DartType type; |
- final List<TypePromotionMessage> messages = <TypePromotionMessage>[]; |
- |
- TypePromotion(this.node, this.variable, this.type); |
- |
- bool get isValid => messages.isEmpty; |
- |
- TypePromotion copy() { |
- return new TypePromotion(node, variable, type)..messages.addAll(messages); |
- } |
- |
- void addHint(Spannable spannable, MessageKind kind, [Map arguments]) { |
- messages.add(new TypePromotionMessage(api.Diagnostic.HINT, |
- spannable, kind, arguments)); |
- } |
- |
- void addInfo(Spannable spannable, MessageKind kind, [Map arguments]) { |
- messages.add(new TypePromotionMessage(api.Diagnostic.INFO, |
- spannable, kind, arguments)); |
- } |
- |
- String toString() { |
- return 'Promote ${variable} to ${type}${isValid ? '' : ' (invalid)'}'; |
- } |
-} |
- |
-/// A hint or info message attached to a type promotion. |
-class TypePromotionMessage { |
- api.Diagnostic diagnostic; |
- Spannable spannable; |
- MessageKind messageKind; |
- Map messageArguments; |
- |
- TypePromotionMessage(this.diagnostic, this.spannable, this.messageKind, |
- [this.messageArguments]); |
-} |
- |
-class TypeCheckerVisitor extends Visitor<DartType> { |
- final Compiler compiler; |
- final TreeElements elements; |
- final Types types; |
- |
- Node lastSeenNode; |
- DartType expectedReturnType; |
- AsyncMarker currentAsyncMarker = AsyncMarker.SYNC; |
- |
- final ClassElement currentClass; |
- |
- InterfaceType thisType; |
- InterfaceType superType; |
- |
- Link<DartType> cascadeTypes = const Link<DartType>(); |
- |
- bool analyzingInitializer = false; |
- |
- DartType intType; |
- DartType doubleType; |
- DartType boolType; |
- DartType stringType; |
- DartType objectType; |
- DartType listType; |
- |
- Map<Node, List<TypePromotion>> shownTypePromotionsMap = |
- new Map<Node, List<TypePromotion>>(); |
- |
- Map<VariableElement, Link<TypePromotion>> typePromotionsMap = |
- new Map<VariableElement, Link<TypePromotion>>(); |
- |
- Set<TypePromotion> reportedTypePromotions = new Set<TypePromotion>(); |
- |
- void showTypePromotion(Node node, TypePromotion typePromotion) { |
- List<TypePromotion> shownTypePromotions = |
- shownTypePromotionsMap.putIfAbsent(node, () => <TypePromotion>[]); |
- shownTypePromotions.add(typePromotion); |
- } |
- |
- void registerKnownTypePromotion(TypePromotion typePromotion) { |
- VariableElement variable = typePromotion.variable; |
- Link<TypePromotion> knownTypes = |
- typePromotionsMap.putIfAbsent(variable, |
- () => const Link<TypePromotion>()); |
- typePromotionsMap[variable] = knownTypes.prepend(typePromotion); |
- } |
- |
- void unregisterKnownTypePromotion(TypePromotion typePromotion) { |
- VariableElement variable = typePromotion.variable; |
- Link<TypePromotion> knownTypes = typePromotionsMap[variable].tail; |
- if (knownTypes.isEmpty) { |
- typePromotionsMap.remove(variable); |
- } else { |
- typePromotionsMap[variable] = knownTypes; |
- } |
- } |
- |
- List<TypePromotion> getShownTypePromotionsFor(Node node) { |
- List<TypePromotion> shownTypePromotions = shownTypePromotionsMap[node]; |
- return shownTypePromotions != null ? shownTypePromotions : const []; |
- } |
- |
- TypePromotion getKnownTypePromotion(VariableElement element) { |
- Link<TypePromotion> promotions = typePromotionsMap[element]; |
- if (promotions != null) { |
- while (!promotions.isEmpty) { |
- TypePromotion typePromotion = promotions.head; |
- if (typePromotion.isValid) { |
- return typePromotion; |
- } |
- promotions = promotions.tail; |
- } |
- } |
- return null; |
- } |
- |
- DartType getKnownType(VariableElement element) { |
- TypePromotion typePromotion = getKnownTypePromotion(element); |
- if (typePromotion != null) return typePromotion.type; |
- return element.type; |
- } |
- |
- TypeCheckerVisitor(this.compiler, TreeElements elements, this.types) |
- : this.elements = elements, |
- currentClass = elements.analyzedElement != null |
- ? elements.analyzedElement.enclosingClass : null { |
- intType = compiler.intClass.computeType(compiler); |
- doubleType = compiler.doubleClass.computeType(compiler); |
- boolType = compiler.boolClass.computeType(compiler); |
- stringType = compiler.stringClass.computeType(compiler); |
- objectType = compiler.objectClass.computeType(compiler); |
- listType = compiler.listClass.computeType(compiler); |
- |
- if (currentClass != null) { |
- thisType = currentClass.thisType; |
- superType = currentClass.supertype; |
- } |
- } |
- |
- LibraryElement get currentLibrary => elements.analyzedElement.library; |
- |
- reportTypeWarning(Spannable spannable, MessageKind kind, |
- [Map arguments = const {}]) { |
- compiler.reportWarning(spannable, kind, arguments); |
- } |
- |
- reportTypeInfo(Spannable spannable, MessageKind kind, |
- [Map arguments = const {}]) { |
- compiler.reportInfo(spannable, kind, arguments); |
- } |
- |
- reportTypePromotionHint(TypePromotion typePromotion) { |
- if (!reportedTypePromotions.contains(typePromotion)) { |
- reportedTypePromotions.add(typePromotion); |
- for (TypePromotionMessage message in typePromotion.messages) { |
- switch (message.diagnostic) { |
- case api.Diagnostic.HINT: |
- compiler.reportHint(message.spannable, |
- message.messageKind, |
- message.messageArguments); |
- break; |
- case api.Diagnostic.INFO: |
- compiler.reportInfo(message.spannable, |
- message.messageKind, |
- message.messageArguments); |
- break; |
- } |
- } |
- } |
- } |
- |
- // TODO(karlklose): remove these functions. |
- DartType unhandledExpression() => const DynamicType(); |
- |
- DartType analyzeNonVoid(Node node) { |
- DartType type = analyze(node); |
- if (type.isVoid) { |
- reportTypeWarning(node, MessageKind.VOID_EXPRESSION); |
- } |
- return type; |
- } |
- |
- DartType analyzeWithDefault(Node node, DartType defaultValue) { |
- return node != null ? analyze(node) : defaultValue; |
- } |
- |
- /// If [inInitializer] is true, assignment should be interpreted as write to |
- /// a field and not to a setter. |
- DartType analyze(Node node, {bool inInitializer: false}) { |
- if (node == null) { |
- final String error = 'Unexpected node: null'; |
- if (lastSeenNode != null) { |
- compiler.internalError(lastSeenNode, error); |
- } else { |
- compiler.internalError(elements.analyzedElement, error); |
- } |
- } else { |
- lastSeenNode = node; |
- } |
- bool previouslyInitializer = analyzingInitializer; |
- analyzingInitializer = inInitializer; |
- DartType result = node.accept(this); |
- analyzingInitializer = previouslyInitializer; |
- if (result == null) { |
- compiler.internalError(node, 'Type is null.'); |
- } |
- return result; |
- } |
- |
- void checkTypePromotion(Node node, TypePromotion typePromotion, |
- {bool checkAccesses: false}) { |
- VariableElement variable = typePromotion.variable; |
- String variableName = variable.name; |
- List<Node> potentialMutationsIn = |
- elements.getPotentialMutationsIn(node, variable); |
- if (!potentialMutationsIn.isEmpty) { |
- typePromotion.addHint(typePromotion.node, |
- MessageKind.POTENTIAL_MUTATION, |
- {'variableName': variableName, 'shownType': typePromotion.type}); |
- for (Node mutation in potentialMutationsIn) { |
- typePromotion.addInfo(mutation, |
- MessageKind.POTENTIAL_MUTATION_HERE, |
- {'variableName': variableName}); |
- } |
- } |
- List<Node> potentialMutationsInClosures = |
- elements.getPotentialMutationsInClosure(variable); |
- if (!potentialMutationsInClosures.isEmpty) { |
- typePromotion.addHint(typePromotion.node, |
- MessageKind.POTENTIAL_MUTATION_IN_CLOSURE, |
- {'variableName': variableName, 'shownType': typePromotion.type}); |
- for (Node mutation in potentialMutationsInClosures) { |
- typePromotion.addInfo(mutation, |
- MessageKind.POTENTIAL_MUTATION_IN_CLOSURE_HERE, |
- {'variableName': variableName}); |
- } |
- } |
- if (checkAccesses) { |
- List<Node> accesses = elements.getAccessesByClosureIn(node, variable); |
- List<Node> mutations = elements.getPotentialMutations(variable); |
- if (!accesses.isEmpty && !mutations.isEmpty) { |
- typePromotion.addHint(typePromotion.node, |
- MessageKind.ACCESSED_IN_CLOSURE, |
- {'variableName': variableName, 'shownType': typePromotion.type}); |
- for (Node access in accesses) { |
- typePromotion.addInfo(access, |
- MessageKind.ACCESSED_IN_CLOSURE_HERE, |
- {'variableName': variableName}); |
- } |
- for (Node mutation in mutations) { |
- typePromotion.addInfo(mutation, |
- MessageKind.POTENTIAL_MUTATION_HERE, |
- {'variableName': variableName}); |
- } |
- } |
- } |
- } |
- |
- /// Show type promotions from [left] and [right] in [node] given that the |
- /// promoted variables are not potentially mutated in [right]. |
- void reshowTypePromotions(Node node, Node left, Node right) { |
- for (TypePromotion typePromotion in getShownTypePromotionsFor(left)) { |
- typePromotion = typePromotion.copy(); |
- checkTypePromotion(right, typePromotion); |
- showTypePromotion(node, typePromotion); |
- } |
- |
- for (TypePromotion typePromotion in getShownTypePromotionsFor(right)) { |
- typePromotion = typePromotion.copy(); |
- checkTypePromotion(right, typePromotion); |
- showTypePromotion(node, typePromotion); |
- } |
- } |
- |
- /// Analyze [node] in the context of the known types shown in [context]. |
- DartType analyzeInPromotedContext(Node context, Node node) { |
- Link<TypePromotion> knownForNode = const Link<TypePromotion>(); |
- for (TypePromotion typePromotion in getShownTypePromotionsFor(context)) { |
- typePromotion = typePromotion.copy(); |
- checkTypePromotion(node, typePromotion, checkAccesses: true); |
- knownForNode = knownForNode.prepend(typePromotion); |
- registerKnownTypePromotion(typePromotion); |
- } |
- |
- final DartType type = analyze(node); |
- |
- while (!knownForNode.isEmpty) { |
- unregisterKnownTypePromotion(knownForNode.head); |
- knownForNode = knownForNode.tail; |
- } |
- |
- return type; |
- } |
- |
- /** |
- * Check if a value of type [from] can be assigned to a variable, parameter or |
- * return value of type [to]. If `isConst == true`, an error is emitted in |
- * checked mode, otherwise a warning is issued. |
- */ |
- bool checkAssignable(Spannable spannable, DartType from, DartType to, |
- {bool isConst: false}) { |
- if (!types.isAssignable(from, to)) { |
- if (compiler.enableTypeAssertions && isConst) { |
- compiler.reportError(spannable, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': from, 'toType': to}); |
- } else { |
- reportTypeWarning(spannable, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': from, 'toType': to}); |
- } |
- return false; |
- } |
- return true; |
- } |
- |
- checkCondition(Expression condition) { |
- checkAssignable(condition, analyze(condition), boolType); |
- } |
- |
- void pushCascadeType(DartType type) { |
- cascadeTypes = cascadeTypes.prepend(type); |
- } |
- |
- DartType popCascadeType() { |
- DartType type = cascadeTypes.head; |
- cascadeTypes = cascadeTypes.tail; |
- return type; |
- } |
- |
- DartType visitBlock(Block node) { |
- return analyze(node.statements); |
- } |
- |
- DartType visitCascade(Cascade node) { |
- analyze(node.expression); |
- return popCascadeType(); |
- } |
- |
- DartType visitCascadeReceiver(CascadeReceiver node) { |
- DartType type = analyze(node.expression); |
- pushCascadeType(type); |
- return type; |
- } |
- |
- DartType visitDoWhile(DoWhile node) { |
- analyze(node.body); |
- checkCondition(node.condition); |
- return const StatementType(); |
- } |
- |
- DartType visitExpressionStatement(ExpressionStatement node) { |
- Expression expression = node.expression; |
- analyze(expression); |
- return const StatementType(); |
- } |
- |
- /** Dart Programming Language Specification: 11.5.1 For Loop */ |
- DartType visitFor(For node) { |
- if (node.initializer != null) { |
- analyze(node.initializer); |
- } |
- if (node.condition != null) { |
- checkCondition(node.condition); |
- } |
- if (node.update != null) { |
- analyze(node.update); |
- } |
- return analyze(node.body); |
- } |
- |
- DartType visitFunctionDeclaration(FunctionDeclaration node) { |
- analyze(node.function); |
- return const StatementType(); |
- } |
- |
- DartType visitFunctionExpression(FunctionExpression node) { |
- DartType type; |
- DartType returnType; |
- DartType previousType; |
- final FunctionElement element = elements.getFunctionDefinition(node); |
- assert(invariant(node, element != null, |
- message: 'FunctionExpression with no element')); |
- if (Elements.isUnresolved(element)) return const DynamicType(); |
- if (identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR) || |
- identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR_BODY)) { |
- type = const DynamicType(); |
- returnType = const VoidType(); |
- |
- element.functionSignature.forEachParameter((ParameterElement parameter) { |
- if (parameter.isInitializingFormal) { |
- InitializingFormalElement fieldParameter = parameter; |
- checkAssignable(parameter, parameter.type, |
- fieldParameter.fieldElement.computeType(compiler)); |
- } |
- }); |
- if (node.initializers != null) { |
- analyze(node.initializers, inInitializer: true); |
- } |
- } else { |
- FunctionType functionType = element.computeType(compiler); |
- returnType = functionType.returnType; |
- type = functionType; |
- } |
- DartType previousReturnType = expectedReturnType; |
- expectedReturnType = returnType; |
- AsyncMarker previousAsyncMarker = currentAsyncMarker; |
- currentAsyncMarker = element.asyncMarker; |
- analyze(node.body); |
- expectedReturnType = previousReturnType; |
- currentAsyncMarker = previousAsyncMarker; |
- return type; |
- } |
- |
- DartType visitIdentifier(Identifier node) { |
- if (node.isThis()) { |
- return thisType; |
- } else if (node.isSuper()) { |
- return superType; |
- } else { |
- Element element = elements[node]; |
- assert(invariant(node, element != null, |
- message: 'Missing element for identifier')); |
- assert(invariant(node, element.isVariable || |
- element.isParameter || |
- element.isField, |
- message: 'Unexpected context element ${element}')); |
- return element.computeType(compiler); |
- } |
- } |
- |
- DartType visitIf(If node) { |
- Expression condition = node.condition.expression; |
- Statement thenPart = node.thenPart; |
- |
- checkCondition(node.condition); |
- analyzeInPromotedContext(condition, thenPart); |
- if (node.elsePart != null) { |
- analyze(node.elsePart); |
- } |
- return const StatementType(); |
- } |
- |
- void checkPrivateAccess(Node node, Element element, String name) { |
- if (name != null && |
- isPrivateName(name) && |
- element.library != currentLibrary) { |
- reportTypeWarning( |
- node, |
- MessageKind.PRIVATE_ACCESS, |
- {'name': name, |
- 'libraryName': element.library.getLibraryOrScriptName()}); |
- } |
- |
- } |
- |
- ElementAccess lookupMember(Node node, DartType receiverType, String name, |
- MemberKind memberKind, Element receiverElement, |
- {bool lookupClassMember: false}) { |
- if (receiverType.treatAsDynamic) { |
- return const DynamicAccess(); |
- } |
- |
- Name memberName = new Name(name, currentLibrary, |
- isSetter: memberKind == MemberKind.SETTER); |
- |
- // Compute the unaliased type of the first non type variable bound of |
- // [type]. |
- DartType computeUnaliasedBound(DartType type) { |
- DartType originalType = type; |
- while (identical(type.kind, TypeKind.TYPE_VARIABLE)) { |
- TypeVariableType variable = type; |
- type = variable.element.bound; |
- if (type == originalType) { |
- type = compiler.objectClass.rawType; |
- } |
- } |
- if (type.isMalformed) { |
- return const DynamicType(); |
- } |
- return type.unalias(compiler); |
- } |
- |
- // Compute the interface type of [type]. For type variable it is the |
- // interface type of the bound, for function types and typedefs it is the |
- // `Function` type. |
- InterfaceType computeInterfaceType(DartType type) { |
- if (type.isFunctionType) { |
- type = compiler.functionClass.rawType; |
- } |
- assert(invariant(node, type.isInterfaceType, |
- message: "unexpected type kind ${type.kind}.")); |
- return type; |
- } |
- |
- // Lookup the class or interface member [name] in [interface]. |
- MemberSignature lookupMemberSignature(Name name, InterfaceType interface) { |
- MembersCreator.computeClassMembersByName( |
- compiler, interface.element, name.text); |
- return lookupClassMember || analyzingInitializer |
- ? interface.lookupClassMember(name) |
- : interface.lookupInterfaceMember(name); |
- } |
- |
- // Compute the access of [name] on [type]. This function takes the special |
- // 'call' method into account. |
- ElementAccess getAccess(Name name, |
- DartType unaliasedBound, InterfaceType interface) { |
- MemberSignature member = lookupMemberSignature(memberName, interface); |
- if (member != null) { |
- return new MemberAccess(member); |
- } |
- if (name == const PublicName('call')) { |
- if (unaliasedBound.isFunctionType) { |
- // This is an access the implicit 'call' method of a function type. |
- return new FunctionCallAccess(receiverElement, unaliasedBound); |
- } |
- if (types.isSubtype(interface, compiler.functionClass.rawType)) { |
- // This is an access of the special 'call' method implicitly defined |
- // on 'Function'. This method can be called with any arguments, which |
- // we ensure by giving it the type 'dynamic'. |
- return new FunctionCallAccess(null, const DynamicType()); |
- } |
- } |
- return null; |
- } |
- |
- DartType unaliasedBound = computeUnaliasedBound(receiverType); |
- if (unaliasedBound.treatAsDynamic) { |
- return new DynamicAccess(); |
- } |
- InterfaceType interface = computeInterfaceType(unaliasedBound); |
- ElementAccess access = getAccess(memberName, unaliasedBound, interface); |
- if (access != null) { |
- return access; |
- } |
- if (receiverElement != null && |
- (receiverElement.isVariable || receiverElement.isParameter)) { |
- Link<TypePromotion> typePromotions = typePromotionsMap[receiverElement]; |
- if (typePromotions != null) { |
- while (!typePromotions.isEmpty) { |
- TypePromotion typePromotion = typePromotions.head; |
- if (!typePromotion.isValid) { |
- DartType unaliasedBound = computeUnaliasedBound(typePromotion.type); |
- if (!unaliasedBound.treatAsDynamic) { |
- InterfaceType interface = computeInterfaceType(unaliasedBound); |
- if (getAccess(memberName, unaliasedBound, interface) != null) { |
- reportTypePromotionHint(typePromotion); |
- } |
- } |
- } |
- typePromotions = typePromotions.tail; |
- } |
- } |
- } |
- // We didn't find a member with the correct name. If this lookup is for a |
- // super or redirecting initializer, the resolver has already emitted an |
- // error message. If the target is a proxy, no warning needs to be emitted. |
- // Otherwise, try to emit the most precise warning. |
- if (!interface.element.isProxy && !analyzingInitializer) { |
- bool foundPrivateMember = false; |
- if (memberName.isPrivate) { |
- void findPrivateMember(MemberSignature member) { |
- if (memberName.isSimilarTo(member.name)) { |
- PrivateName privateName = member.name; |
- reportTypeWarning( |
- node, |
- MessageKind.PRIVATE_ACCESS, |
- {'name': name, |
- 'libraryName': privateName.library.getLibraryOrScriptName()}); |
- foundPrivateMember = true; |
- } |
- } |
- // TODO(johnniwinther): Avoid computation of all class members. |
- MembersCreator.computeAllClassMembers(compiler, interface.element); |
- if (lookupClassMember) { |
- interface.element.forEachClassMember(findPrivateMember); |
- } else { |
- interface.element.forEachInterfaceMember(findPrivateMember); |
- } |
- |
- } |
- if (!foundPrivateMember) { |
- switch (memberKind) { |
- case MemberKind.METHOD: |
- reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND, |
- {'className': receiverType.name, 'memberName': name}); |
- break; |
- case MemberKind.OPERATOR: |
- reportTypeWarning(node, MessageKind.OPERATOR_NOT_FOUND, |
- {'className': receiverType.name, 'memberName': name}); |
- break; |
- case MemberKind.GETTER: |
- if (lookupMemberSignature(memberName.setter, interface) != null) { |
- // A setter is present so warn explicitly about the missing |
- // getter. |
- reportTypeWarning(node, MessageKind.GETTER_NOT_FOUND, |
- {'className': receiverType.name, 'memberName': name}); |
- } else { |
- reportTypeWarning(node, MessageKind.MEMBER_NOT_FOUND, |
- {'className': receiverType.name, 'memberName': name}); |
- } |
- break; |
- case MemberKind.SETTER: |
- reportTypeWarning(node, MessageKind.SETTER_NOT_FOUND, |
- {'className': receiverType.name, 'memberName': name}); |
- break; |
- } |
- } |
- } |
- return const DynamicAccess(); |
- } |
- |
- DartType lookupMemberType(Node node, DartType type, String name, |
- MemberKind memberKind) { |
- return lookupMember(node, type, name, memberKind, null) |
- .computeType(compiler); |
- } |
- |
- void analyzeArguments(Send send, Element element, DartType type, |
- [LinkBuilder<DartType> argumentTypes]) { |
- Link<Node> arguments = send.arguments; |
- DartType unaliasedType = type.unalias(compiler); |
- if (identical(unaliasedType.kind, TypeKind.FUNCTION)) { |
- bool error = false; |
- FunctionType funType = unaliasedType; |
- Iterator<DartType> parameterTypes = funType.parameterTypes.iterator; |
- Iterator<DartType> optionalParameterTypes = |
- funType.optionalParameterTypes.iterator; |
- while (!arguments.isEmpty) { |
- Node argument = arguments.head; |
- NamedArgument namedArgument = argument.asNamedArgument(); |
- if (namedArgument != null) { |
- argument = namedArgument.expression; |
- String argumentName = namedArgument.name.source; |
- DartType namedParameterType = |
- funType.getNamedParameterType(argumentName); |
- if (namedParameterType == null) { |
- error = true; |
- // TODO(johnniwinther): Provide better information on the called |
- // function. |
- reportTypeWarning(argument, MessageKind.NAMED_ARGUMENT_NOT_FOUND, |
- {'argumentName': argumentName}); |
- |
- DartType argumentType = analyze(argument); |
- if (argumentTypes != null) argumentTypes.addLast(argumentType); |
- } else { |
- DartType argumentType = analyze(argument); |
- if (argumentTypes != null) argumentTypes.addLast(argumentType); |
- if (!checkAssignable(argument, argumentType, namedParameterType)) { |
- error = true; |
- } |
- } |
- } else { |
- if (!parameterTypes.moveNext()) { |
- if (!optionalParameterTypes.moveNext()) { |
- error = true; |
- // TODO(johnniwinther): Provide better information on the |
- // called function. |
- reportTypeWarning(argument, MessageKind.ADDITIONAL_ARGUMENT); |
- |
- DartType argumentType = analyze(argument); |
- if (argumentTypes != null) argumentTypes.addLast(argumentType); |
- } else { |
- DartType argumentType = analyze(argument); |
- if (argumentTypes != null) argumentTypes.addLast(argumentType); |
- if (!checkAssignable(argument, |
- argumentType, |
- optionalParameterTypes.current)) { |
- error = true; |
- } |
- } |
- } else { |
- DartType argumentType = analyze(argument); |
- if (argumentTypes != null) argumentTypes.addLast(argumentType); |
- if (!checkAssignable(argument, argumentType, |
- parameterTypes.current)) { |
- error = true; |
- } |
- } |
- } |
- arguments = arguments.tail; |
- } |
- if (parameterTypes.moveNext()) { |
- error = true; |
- // TODO(johnniwinther): Provide better information on the called |
- // function. |
- reportTypeWarning(send, MessageKind.MISSING_ARGUMENT, |
- {'argumentType': parameterTypes.current}); |
- } |
- if (error) { |
- // TODO(johnniwinther): Improve access to declaring element and handle |
- // synthesized member signatures. Currently function typed instance |
- // members provide no access to there own name. |
- if (element == null) { |
- element = type.element; |
- } else if (type.element.isTypedef) { |
- if (element != null) { |
- reportTypeInfo(element, |
- MessageKind.THIS_IS_THE_DECLARATION, |
- {'name': element.name}); |
- } |
- element = type.element; |
- } |
- reportTypeInfo(element, MessageKind.THIS_IS_THE_METHOD); |
- } |
- } else { |
- while(!arguments.isEmpty) { |
- DartType argumentType = analyze(arguments.head); |
- if (argumentTypes != null) argumentTypes.addLast(argumentType); |
- arguments = arguments.tail; |
- } |
- } |
- } |
- |
- // Analyze the invocation [node] of [elementAccess]. |
- // |
- // If provided [argumentTypes] is filled with the argument types during |
- // analysis. |
- DartType analyzeInvocation(Send node, ElementAccess elementAccess, |
- [LinkBuilder<DartType> argumentTypes]) { |
- DartType type = elementAccess.computeType(compiler); |
- if (elementAccess.isCallable(compiler)) { |
- analyzeArguments(node, elementAccess.element, type, argumentTypes); |
- } else { |
- reportTypeWarning(node, MessageKind.NOT_CALLABLE, |
- {'elementName': elementAccess.element.name}); |
- analyzeArguments(node, elementAccess.element, const DynamicType(), |
- argumentTypes); |
- } |
- type = type.unalias(compiler); |
- if (identical(type.kind, TypeKind.FUNCTION)) { |
- FunctionType funType = type; |
- return funType.returnType; |
- } else { |
- return const DynamicType(); |
- } |
- } |
- |
- /** |
- * Computes the [ElementAccess] for [name] on the [node] possibly using the |
- * [element] provided for [node] by the resolver. |
- */ |
- ElementAccess computeAccess(Send node, String name, Element element, |
- MemberKind memberKind, |
- {bool lookupClassMember: false}) { |
- if (element != null && element.isErroneous) { |
- // An error has already been reported for this node. |
- return const DynamicAccess(); |
- } |
- if (node.receiver != null) { |
- Element receiverElement = elements[node.receiver]; |
- if (receiverElement != null) { |
- if (receiverElement.isPrefix) { |
- assert(invariant(node, element != null, |
- message: 'Prefixed node has no element.')); |
- return computeResolvedAccess(node, name, element, memberKind); |
- } |
- } |
- // e.foo() for some expression e. |
- DartType receiverType = analyze(node.receiver); |
- if (receiverType.treatAsDynamic || receiverType.isVoid) { |
- return const DynamicAccess(); |
- } |
- TypeKind receiverKind = receiverType.kind; |
- return lookupMember(node, receiverType, name, memberKind, |
- elements[node.receiver], |
- lookupClassMember: lookupClassMember || |
- element != null && element.isStatic); |
- } else { |
- return computeResolvedAccess(node, name, element, memberKind); |
- } |
- } |
- |
- /** |
- * Computes the [ElementAccess] for [name] on the [node] using the [element] |
- * provided for [node] by the resolver. |
- */ |
- ElementAccess computeResolvedAccess(Send node, String name, |
- Element element, MemberKind memberKind) { |
- if (element == null) { |
- // foo() where foo is unresolved. |
- return lookupMember(node, thisType, name, memberKind, null); |
- } else if (element.isErroneous) { |
- // foo() where foo is erroneous. |
- return const DynamicAccess(); |
- } else if (element.impliesType) { |
- // The literal `Foo` where Foo is a class, a typedef, or a type variable. |
- if (elements.isTypeLiteral(node)) { |
- return new TypeLiteralAccess(elements.getTypeLiteralType(node)); |
- } |
- return createResolvedAccess(node, name, element); |
- } else if (element.isClassMember) { |
- // foo() where foo is a member. |
- return lookupMember(node, thisType, name, memberKind, null, |
- lookupClassMember: element.isStatic); |
- } else if (element.isFunction) { |
- // foo() where foo is a method in the same class. |
- return createResolvedAccess(node, name, element); |
- } else if (element.isVariable || |
- element.isParameter || |
- element.isField) { |
- // foo() where foo is a field in the same class. |
- return createResolvedAccess(node, name, element); |
- } else if (element.isGetter || element.isSetter) { |
- return createResolvedAccess(node, name, element); |
- } else { |
- compiler.internalError(element, |
- 'Unexpected element kind ${element.kind}.'); |
- return null; |
- } |
- } |
- |
- ElementAccess createResolvedAccess(Send node, String name, |
- Element element) { |
- checkPrivateAccess(node, element, name); |
- return createPromotedAccess(element); |
- } |
- |
- ElementAccess createPromotedAccess(Element element) { |
- if (element.isVariable || element.isParameter) { |
- TypePromotion typePromotion = getKnownTypePromotion(element); |
- if (typePromotion != null) { |
- return new PromotedAccess(element, typePromotion.type); |
- } |
- } |
- return new ResolvedAccess(element); |
- } |
- |
- /** |
- * Computes the type of the access of [name] on the [node] possibly using the |
- * [element] provided for [node] by the resolver. |
- */ |
- DartType computeAccessType(Send node, String name, Element element, |
- MemberKind memberKind, |
- {bool lookupClassMember: false}) { |
- DartType type = |
- computeAccess(node, name, element, memberKind, |
- lookupClassMember: lookupClassMember).computeType(compiler); |
- if (type == null) { |
- compiler.internalError(node, 'Type is null on access of $name on $node.'); |
- } |
- return type; |
- } |
- |
- /// Compute a version of [shownType] that is more specific that [knownType]. |
- /// This is used to provided better hints when trying to promote a supertype |
- /// to a raw subtype. For instance trying to promote `Iterable<int>` to `List` |
- /// we suggest the use of `List<int>`, which would make promotion valid. |
- DartType computeMoreSpecificType(DartType shownType, |
- DartType knownType) { |
- if (knownType.isInterfaceType && |
- shownType.isInterfaceType && |
- types.isSubtype(shownType.asRaw(), knownType)) { |
- // For the comments in the block, assume the hierarchy: |
- // class A<T, V> {} |
- // class B<S, U> extends A<S, int> {} |
- // and a promotion from a [knownType] of `A<double, int>` to a |
- // [shownType] of `B`. |
- InterfaceType knownInterfaceType = knownType; |
- ClassElement shownClass = shownType.element; |
- |
- // Compute `B<double, dynamic>` as the subtype of `A<double, int>` using |
- // the relation between `A<S, int>` and `A<double, int>`. |
- MoreSpecificSubtypeVisitor visitor = |
- new MoreSpecificSubtypeVisitor(compiler); |
- InterfaceType shownTypeGeneric = visitor.computeMoreSpecific( |
- shownClass, knownInterfaceType); |
- |
- if (shownTypeGeneric != null && |
- types.isMoreSpecific(shownTypeGeneric, knownType)) { |
- // This should be the case but we double-check. |
- // TODO(johnniwinther): Ensure that we don't suggest malbounded types. |
- return shownTypeGeneric; |
- } |
- } |
- return null; |
- |
- } |
- |
- DartType visitSend(Send node) { |
- if (elements.isAssert(node)) { |
- return analyzeInvocation(node, const AssertAccess()); |
- } |
- |
- Element element = elements[node]; |
- |
- if (element != null && element.isConstructor) { |
- DartType receiverType; |
- if (node.receiver != null) { |
- receiverType = analyze(node.receiver); |
- } else if (node.selector.isSuper()) { |
- // TODO(johnniwinther): Lookup super-member in class members. |
- receiverType = superType; |
- } else { |
- assert(node.selector.isThis()); |
- receiverType = thisType; |
- } |
- DartType constructorType = computeConstructorType(element, receiverType); |
- analyzeArguments(node, element, constructorType); |
- return const DynamicType(); |
- } |
- |
- if (Elements.isClosureSend(node, element)) { |
- if (element != null) { |
- // foo() where foo is a local or a parameter. |
- return analyzeInvocation(node, createPromotedAccess(element)); |
- } else { |
- // exp() where exp is some complex expression like (o) or foo(). |
- DartType type = analyze(node.selector); |
- return analyzeInvocation(node, new TypeAccess(type)); |
- } |
- } |
- |
- Identifier selector = node.selector.asIdentifier(); |
- String name = selector.source; |
- |
- if (node.isOperator && identical(name, 'is')) { |
- analyze(node.receiver); |
- if (!node.isIsNotCheck) { |
- Element variable = elements[node.receiver]; |
- if (variable == null) { |
- // Look for the variable element within parenthesized expressions. |
- ParenthesizedExpression parentheses = |
- node.receiver.asParenthesizedExpression(); |
- while (parentheses != null) { |
- variable = elements[parentheses.expression]; |
- if (variable != null) break; |
- parentheses = parentheses.expression.asParenthesizedExpression(); |
- } |
- } |
- |
- if (variable != null && |
- (variable.isVariable || variable.isParameter)) { |
- DartType knownType = getKnownType(variable); |
- if (!knownType.isDynamic) { |
- DartType shownType = elements.getType(node.arguments.head); |
- TypePromotion typePromotion = |
- new TypePromotion(node, variable, shownType); |
- if (!types.isMoreSpecific(shownType, knownType)) { |
- String variableName = variable.name; |
- if (!types.isSubtype(shownType, knownType)) { |
- typePromotion.addHint(node, |
- MessageKind.NOT_MORE_SPECIFIC_SUBTYPE, |
- {'variableName': variableName, |
- 'shownType': shownType, |
- 'knownType': knownType}); |
- } else { |
- DartType shownTypeSuggestion = |
- computeMoreSpecificType(shownType, knownType); |
- if (shownTypeSuggestion != null) { |
- typePromotion.addHint(node, |
- MessageKind.NOT_MORE_SPECIFIC_SUGGESTION, |
- {'variableName': variableName, |
- 'shownType': shownType, |
- 'shownTypeSuggestion': shownTypeSuggestion, |
- 'knownType': knownType}); |
- } else { |
- typePromotion.addHint(node, |
- MessageKind.NOT_MORE_SPECIFIC, |
- {'variableName': variableName, |
- 'shownType': shownType, |
- 'knownType': knownType}); |
- } |
- } |
- } |
- showTypePromotion(node, typePromotion); |
- } |
- } |
- } |
- return boolType; |
- } if (node.isOperator && identical(name, 'as')) { |
- analyze(node.receiver); |
- return elements.getType(node.arguments.head); |
- } else if (node.isOperator) { |
- final Node receiver = node.receiver; |
- final DartType receiverType = analyze(receiver); |
- if (identical(name, '==') || identical(name, '!=') |
- // TODO(johnniwinther): Remove these. |
- || identical(name, '===') || identical(name, '!==')) { |
- // Analyze argument. |
- analyze(node.arguments.head); |
- return boolType; |
- } else if (identical(name, '||')) { |
- checkAssignable(receiver, receiverType, boolType); |
- final Node argument = node.arguments.head; |
- final DartType argumentType = analyze(argument); |
- checkAssignable(argument, argumentType, boolType); |
- return boolType; |
- } else if (identical(name, '&&')) { |
- checkAssignable(receiver, receiverType, boolType); |
- final Node argument = node.arguments.head; |
- |
- final DartType argumentType = |
- analyzeInPromotedContext(receiver, argument); |
- |
- reshowTypePromotions(node, receiver, argument); |
- |
- checkAssignable(argument, argumentType, boolType); |
- return boolType; |
- } else if (identical(name, '!')) { |
- checkAssignable(receiver, receiverType, boolType); |
- return boolType; |
- } else if (identical(name, '?')) { |
- return boolType; |
- } |
- String operatorName = selector.source; |
- if (identical(name, '-') && node.arguments.isEmpty) { |
- operatorName = 'unary-'; |
- } |
- assert(invariant(node, |
- identical(name, '+') || identical(name, '=') || |
- identical(name, '-') || identical(name, '*') || |
- identical(name, '/') || identical(name, '%') || |
- identical(name, '~/') || identical(name, '|') || |
- identical(name, '&') || identical(name, '^') || |
- identical(name, '~')|| identical(name, '<<') || |
- identical(name, '>>') || |
- identical(name, '<') || identical(name, '>') || |
- identical(name, '<=') || identical(name, '>=') || |
- identical(name, '[]'), |
- message: 'Unexpected operator $name')); |
- |
- // TODO(karlklose): handle `void` in expression context by calling |
- // [analyzeNonVoid] instead of [analyze]. |
- ElementAccess access = receiverType.isVoid ? const DynamicAccess() |
- : lookupMember(node, receiverType, operatorName, |
- MemberKind.OPERATOR, null); |
- LinkBuilder<DartType> argumentTypesBuilder = new LinkBuilder<DartType>(); |
- DartType resultType = |
- analyzeInvocation(node, access, argumentTypesBuilder); |
- if (identical(receiverType.element, compiler.intClass)) { |
- if (identical(name, '+') || |
- identical(operatorName, '-') || |
- identical(name, '*') || |
- identical(name, '%')) { |
- DartType argumentType = argumentTypesBuilder.toLink().head; |
- if (identical(argumentType.element, compiler.intClass)) { |
- return intType; |
- } else if (identical(argumentType.element, compiler.doubleClass)) { |
- return doubleType; |
- } |
- } |
- } |
- return resultType; |
- } else if (node.isPropertyAccess) { |
- ElementAccess access = |
- computeAccess(node, selector.source, element, MemberKind.GETTER); |
- return access.computeType(compiler); |
- } else if (node.isFunctionObjectInvocation) { |
- return unhandledExpression(); |
- } else { |
- ElementAccess access = |
- computeAccess(node, selector.source, element, MemberKind.METHOD); |
- return analyzeInvocation(node, access); |
- } |
- } |
- |
- /// Returns the first type in the list or [:dynamic:] if the list is empty. |
- DartType firstType(List<DartType> list) { |
- return list.isEmpty ? const DynamicType() : list.first; |
- } |
- |
- /** |
- * Returns the second type in the list or [:dynamic:] if the list is too |
- * short. |
- */ |
- DartType secondType(List<DartType> list) { |
- return list.length < 2 ? const DynamicType() : list[1]; |
- } |
- |
- /** |
- * Checks [: target o= value :] for some operator o, and returns the type |
- * of the result. This method also handles increment/decrement expressions |
- * like [: target++ :]. |
- */ |
- DartType checkAssignmentOperator(SendSet node, |
- String operatorName, |
- Node valueNode, |
- DartType value) { |
- assert(invariant(node, !node.isIndex)); |
- Element setterElement = elements[node]; |
- Element getterElement = elements[node.selector]; |
- Identifier selector = node.selector; |
- DartType getter = computeAccessType( |
- node, selector.source, getterElement, MemberKind.GETTER); |
- DartType setter = computeAccessType( |
- node, selector.source, setterElement, MemberKind.SETTER); |
- // [operator] is the type of operator+ or operator- on [target]. |
- DartType operator = |
- lookupMemberType(node, getter, operatorName, MemberKind.OPERATOR); |
- if (operator is FunctionType) { |
- FunctionType operatorType = operator; |
- // [result] is the type of target o value. |
- DartType result = operatorType.returnType; |
- DartType operatorArgument = firstType(operatorType.parameterTypes); |
- // Check target o value. |
- bool validValue = checkAssignable(valueNode, value, operatorArgument); |
- if (validValue || !(node.isPrefix || node.isPostfix)) { |
- // Check target = result. |
- checkAssignable(node.assignmentOperator, result, setter); |
- } |
- return node.isPostfix ? getter : result; |
- } |
- return const DynamicType(); |
- } |
- |
- /** |
- * Checks [: base[key] o= value :] for some operator o, and returns the type |
- * of the result. This method also handles increment/decrement expressions |
- * like [: base[key]++ :]. |
- */ |
- DartType checkIndexAssignmentOperator(SendSet node, |
- String operatorName, |
- Node valueNode, |
- DartType value) { |
- assert(invariant(node, node.isIndex)); |
- final DartType base = analyze(node.receiver); |
- final Node keyNode = node.arguments.head; |
- final DartType key = analyze(keyNode); |
- |
- // [indexGet] is the type of operator[] on [base]. |
- DartType indexGet = lookupMemberType( |
- node, base, '[]', MemberKind.OPERATOR); |
- if (indexGet is FunctionType) { |
- FunctionType indexGetType = indexGet; |
- DartType indexGetKey = firstType(indexGetType.parameterTypes); |
- // Check base[key]. |
- bool validKey = checkAssignable(keyNode, key, indexGetKey); |
- |
- // [element] is the type of base[key]. |
- DartType element = indexGetType.returnType; |
- // [operator] is the type of operator o on [element]. |
- DartType operator = lookupMemberType( |
- node, element, operatorName, MemberKind.OPERATOR); |
- if (operator is FunctionType) { |
- FunctionType operatorType = operator; |
- |
- // Check base[key] o value. |
- DartType operatorArgument = firstType(operatorType.parameterTypes); |
- bool validValue = checkAssignable(valueNode, value, operatorArgument); |
- |
- // [result] is the type of base[key] o value. |
- DartType result = operatorType.returnType; |
- |
- // [indexSet] is the type of operator[]= on [base]. |
- DartType indexSet = lookupMemberType( |
- node, base, '[]=', MemberKind.OPERATOR); |
- if (indexSet is FunctionType) { |
- FunctionType indexSetType = indexSet; |
- DartType indexSetKey = firstType(indexSetType.parameterTypes); |
- DartType indexSetValue = secondType(indexSetType.parameterTypes); |
- |
- if (validKey || indexGetKey != indexSetKey) { |
- // Only check base[key] on []= if base[key] was valid for [] or |
- // if the key types differ. |
- checkAssignable(keyNode, key, indexSetKey); |
- } |
- // Check base[key] = result |
- if (validValue || !(node.isPrefix || node.isPostfix)) { |
- checkAssignable(node.assignmentOperator, result, indexSetValue); |
- } |
- } |
- return node.isPostfix ? element : result; |
- } |
- } |
- return const DynamicType(); |
- } |
- |
- visitSendSet(SendSet node) { |
- Element element = elements[node]; |
- Identifier selector = node.selector; |
- final name = node.assignmentOperator.source; |
- if (identical(name, '=')) { |
- // e1 = value |
- if (node.isIndex) { |
- // base[key] = value |
- final DartType base = analyze(node.receiver); |
- final Node keyNode = node.arguments.head; |
- final DartType key = analyze(keyNode); |
- final Node valueNode = node.arguments.tail.head; |
- final DartType value = analyze(valueNode); |
- DartType indexSet = lookupMemberType( |
- node, base, '[]=', MemberKind.OPERATOR); |
- if (indexSet is FunctionType) { |
- FunctionType indexSetType = indexSet; |
- DartType indexSetKey = firstType(indexSetType.parameterTypes); |
- checkAssignable(keyNode, key, indexSetKey); |
- DartType indexSetValue = secondType(indexSetType.parameterTypes); |
- checkAssignable(node.assignmentOperator, value, indexSetValue); |
- } |
- return value; |
- } else { |
- // target = value |
- DartType target; |
- if (analyzingInitializer) { |
- // Field declaration `Foo target = value;` or initializer |
- // `this.target = value`. Lookup the getter `target` in the class |
- // members. |
- target = computeAccessType(node, selector.source, element, |
- MemberKind.GETTER, lookupClassMember: true); |
- } else { |
- // Normal assignment `target = value`. |
- target = computeAccessType( |
- node, selector.source, element, MemberKind.SETTER); |
- } |
- final Node valueNode = node.arguments.head; |
- final DartType value = analyze(valueNode); |
- checkAssignable(node.assignmentOperator, value, target); |
- return value; |
- } |
- } else if (identical(name, '++') || identical(name, '--')) { |
- // e++ or e-- |
- String operatorName = identical(name, '++') ? '+' : '-'; |
- if (node.isIndex) { |
- // base[key]++, base[key]--, ++base[key], or --base[key] |
- return checkIndexAssignmentOperator( |
- node, operatorName, node.assignmentOperator, intType); |
- } else { |
- // target++, target--, ++target, or --target |
- return checkAssignmentOperator( |
- node, operatorName, node.assignmentOperator, intType); |
- } |
- } else { |
- // e1 o= e2 for some operator o. |
- String operatorName; |
- switch (name) { |
- case '+=': operatorName = '+'; break; |
- case '-=': operatorName = '-'; break; |
- case '*=': operatorName = '*'; break; |
- case '/=': operatorName = '/'; break; |
- case '%=': operatorName = '%'; break; |
- case '~/=': operatorName = '~/'; break; |
- case '&=': operatorName = '&'; break; |
- case '|=': operatorName = '|'; break; |
- case '^=': operatorName = '^'; break; |
- case '<<=': operatorName = '<<'; break; |
- case '>>=': operatorName = '>>'; break; |
- default: |
- compiler.internalError(node, 'Unexpected assignment operator $name.'); |
- } |
- if (node.isIndex) { |
- // base[key] o= value for some operator o. |
- final Node valueNode = node.arguments.tail.head; |
- final DartType value = analyze(valueNode); |
- return checkIndexAssignmentOperator( |
- node, operatorName, valueNode, value); |
- } else { |
- // target o= value for some operator o. |
- final Node valueNode = node.arguments.head; |
- final DartType value = analyze(valueNode); |
- return checkAssignmentOperator(node, operatorName, valueNode, value); |
- } |
- } |
- } |
- |
- DartType visitLiteralInt(LiteralInt node) { |
- return intType; |
- } |
- |
- DartType visitLiteralDouble(LiteralDouble node) { |
- return doubleType; |
- } |
- |
- DartType visitLiteralBool(LiteralBool node) { |
- return boolType; |
- } |
- |
- DartType visitLiteralString(LiteralString node) { |
- return stringType; |
- } |
- |
- DartType visitStringJuxtaposition(StringJuxtaposition node) { |
- analyze(node.first); |
- analyze(node.second); |
- return stringType; |
- } |
- |
- DartType visitLiteralNull(LiteralNull node) { |
- return const DynamicType(); |
- } |
- |
- DartType visitLiteralSymbol(LiteralSymbol node) { |
- return compiler.symbolClass.rawType; |
- } |
- |
- DartType computeConstructorType(Element constructor, DartType type) { |
- if (Elements.isUnresolved(constructor)) return const DynamicType(); |
- DartType constructorType = constructor.computeType(compiler); |
- if (identical(type.kind, TypeKind.INTERFACE)) { |
- if (constructor.isSynthesized) { |
- // TODO(johnniwinther): Remove this when synthesized constructors handle |
- // type variables correctly. |
- InterfaceType interfaceType = type; |
- ClassElement receiverElement = interfaceType.element; |
- while (receiverElement.isMixinApplication) { |
- receiverElement = receiverElement.supertype.element; |
- } |
- constructorType = constructorType.substByContext( |
- interfaceType.asInstanceOf(receiverElement)); |
- } else { |
- constructorType = constructorType.substByContext(type); |
- } |
- } |
- return constructorType; |
- } |
- |
- DartType visitNewExpression(NewExpression node) { |
- Element element = elements[node.send]; |
- if (Elements.isUnresolved(element)) return const DynamicType(); |
- |
- checkPrivateAccess(node, element, element.name); |
- |
- DartType newType = elements.getType(node); |
- DartType constructorType = computeConstructorType(element, newType); |
- analyzeArguments(node.send, element, constructorType); |
- return newType; |
- } |
- |
- DartType visitLiteralList(LiteralList node) { |
- InterfaceType listType = elements.getType(node); |
- DartType listElementType = firstType(listType.typeArguments); |
- for (Link<Node> link = node.elements.nodes; |
- !link.isEmpty; |
- link = link.tail) { |
- Node element = link.head; |
- DartType elementType = analyze(element); |
- checkAssignable(element, elementType, listElementType, |
- isConst: node.isConst); |
- } |
- return listType; |
- } |
- |
- DartType visitNodeList(NodeList node) { |
- for (Link<Node> link = node.nodes; !link.isEmpty; link = link.tail) { |
- analyze(link.head, inInitializer: analyzingInitializer); |
- } |
- return const StatementType(); |
- } |
- |
- DartType visitRedirectingFactoryBody(RedirectingFactoryBody node) { |
- // TODO(lrn): Typecheck the body. It must refer to the constructor |
- // of a subtype. |
- return const StatementType(); |
- } |
- |
- DartType visitRethrow(Rethrow node) { |
- return const StatementType(); |
- } |
- |
- /** Dart Programming Language Specification: 11.10 Return */ |
- DartType visitReturn(Return node) { |
- if (identical(node.beginToken.stringValue, 'native')) { |
- return const StatementType(); |
- } |
- |
- final expression = node.expression; |
- final isVoidFunction = expectedReturnType.isVoid; |
- |
- // Executing a return statement return e; [...] It is a static type warning |
- // if the type of e may not be assigned to the declared return type of the |
- // immediately enclosing function. |
- if (expression != null) { |
- final expressionType = analyze(expression); |
- Element element = elements.analyzedElement; |
- if (element != null && element.isGenerativeConstructor) { |
- // The resolver already emitted an error for this expression. |
- } else if (isVoidFunction |
- && !types.isAssignable(expressionType, const VoidType())) { |
- reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID); |
- } else { |
- checkAssignable(expression, expressionType, expectedReturnType); |
- } |
- |
- // Let f be the function immediately enclosing a return statement of the |
- // form 'return;' It is a static warning if both of the following conditions |
- // hold: |
- // - f is not a generative constructor. |
- // - The return type of f may not be assigned to void. |
- } else if (!types.isAssignable(expectedReturnType, const VoidType())) { |
- reportTypeWarning(node, MessageKind.RETURN_NOTHING, |
- {'returnType': expectedReturnType}); |
- } |
- return const StatementType(); |
- } |
- |
- DartType visitThrow(Throw node) { |
- // TODO(johnniwinther): Handle reachability. |
- analyze(node.expression); |
- return const DynamicType(); |
- } |
- |
- DartType visitAwait(Await node) { |
- DartType expressionType = analyze(node.expression); |
- DartType resultType = expressionType; |
- if (expressionType is InterfaceType) { |
- InterfaceType futureType = |
- expressionType.asInstanceOf(compiler.futureClass); |
- if (futureType != null) { |
- resultType = futureType.typeArguments.first; |
- } |
- } |
- return resultType; |
- } |
- |
- DartType visitYield(Yield node) { |
- DartType resultType = analyze(node.expression); |
- if (!node.hasStar) { |
- if (currentAsyncMarker.isAsync) { |
- resultType = |
- compiler.streamClass.thisType.createInstantiation( |
- <DartType>[resultType]); |
- } else { |
- resultType = |
- compiler.iterableClass.thisType.createInstantiation( |
- <DartType>[resultType]); |
- } |
- } |
- checkAssignable(node, resultType, expectedReturnType); |
- return const StatementType(); |
- } |
- |
- DartType visitTypeAnnotation(TypeAnnotation node) { |
- return elements.getType(node); |
- } |
- |
- DartType visitVariableDefinitions(VariableDefinitions node) { |
- DartType type = analyzeWithDefault(node.type, const DynamicType()); |
- if (type.isVoid) { |
- reportTypeWarning(node.type, MessageKind.VOID_VARIABLE); |
- type = const DynamicType(); |
- } |
- for (Link<Node> link = node.definitions.nodes; !link.isEmpty; |
- link = link.tail) { |
- Node definition = link.head; |
- invariant(definition, definition is Identifier || definition is SendSet, |
- message: 'expected identifier or initialization'); |
- if (definition is SendSet) { |
- SendSet initialization = definition; |
- DartType initializer = analyzeNonVoid(initialization.arguments.head); |
- checkAssignable(initialization.assignmentOperator, initializer, type); |
- } |
- } |
- return const StatementType(); |
- } |
- |
- DartType visitWhile(While node) { |
- checkCondition(node.condition); |
- analyze(node.body); |
- Expression cond = node.condition.asParenthesizedExpression().expression; |
- return const StatementType(); |
- } |
- |
- DartType visitParenthesizedExpression(ParenthesizedExpression node) { |
- Expression expression = node.expression; |
- DartType type = analyze(expression); |
- for (TypePromotion typePromotion in getShownTypePromotionsFor(expression)) { |
- showTypePromotion(node, typePromotion); |
- } |
- return type; |
- } |
- |
- DartType visitConditional(Conditional node) { |
- Expression condition = node.condition; |
- Expression thenExpression = node.thenExpression; |
- |
- checkCondition(condition); |
- |
- DartType thenType = analyzeInPromotedContext(condition, thenExpression); |
- |
- DartType elseType = analyze(node.elseExpression); |
- return compiler.types.computeLeastUpperBound(thenType, elseType); |
- } |
- |
- visitStringInterpolation(StringInterpolation node) { |
- node.visitChildren(this); |
- return stringType; |
- } |
- |
- visitStringInterpolationPart(StringInterpolationPart node) { |
- node.visitChildren(this); |
- return stringType; |
- } |
- |
- visitEmptyStatement(EmptyStatement node) { |
- return const StatementType(); |
- } |
- |
- visitBreakStatement(BreakStatement node) { |
- return const StatementType(); |
- } |
- |
- visitContinueStatement(ContinueStatement node) { |
- return const StatementType(); |
- } |
- |
- visitForIn(ForIn node) { |
- analyze(node.expression); |
- analyze(node.body); |
- return const StatementType(); |
- } |
- |
- visitLabeledStatement(LabeledStatement node) { |
- return analyze(node.statement); |
- } |
- |
- visitLiteralMap(LiteralMap node) { |
- InterfaceType mapType = elements.getType(node); |
- DartType mapKeyType = firstType(mapType.typeArguments); |
- DartType mapValueType = secondType(mapType.typeArguments); |
- bool isConst = node.isConst; |
- for (Link<Node> link = node.entries.nodes; |
- !link.isEmpty; |
- link = link.tail) { |
- LiteralMapEntry entry = link.head; |
- DartType keyType = analyze(entry.key); |
- checkAssignable(entry.key, keyType, mapKeyType, isConst: isConst); |
- DartType valueType = analyze(entry.value); |
- checkAssignable(entry.value, valueType, mapValueType, isConst: isConst); |
- } |
- return mapType; |
- } |
- |
- visitNamedArgument(NamedArgument node) { |
- // Named arguments are visited as part of analyzing invocations of |
- // unresolved methods. For instance [: foo(a: 42); :] where 'foo' is neither |
- // found in the enclosing scope nor through lookup on 'this' or |
- // [: x.foo(b: 42); :] where 'foo' cannot be not found through lookup on |
- // the static type of 'x'. |
- return analyze(node.expression); |
- } |
- |
- visitSwitchStatement(SwitchStatement node) { |
- // TODO(johnniwinther): Handle reachability based on reachability of |
- // switch cases. |
- |
- DartType expressionType = analyze(node.expression); |
- |
- // Check that all the case expressions are assignable to the expression. |
- for (SwitchCase switchCase in node.cases) { |
- for (Node labelOrCase in switchCase.labelsAndCases) { |
- CaseMatch caseMatch = labelOrCase.asCaseMatch(); |
- if (caseMatch == null) continue; |
- |
- DartType caseType = analyze(caseMatch.expression); |
- checkAssignable(caseMatch, expressionType, caseType); |
- } |
- |
- analyze(switchCase); |
- } |
- |
- return const StatementType(); |
- } |
- |
- visitSwitchCase(SwitchCase node) { |
- return analyze(node.statements); |
- } |
- |
- visitTryStatement(TryStatement node) { |
- // TODO(johnniwinther): Use reachability information of try-block, |
- // catch-blocks and finally-block to compute the whether the try statement |
- // is returning. |
- analyze(node.tryBlock); |
- for (CatchBlock catchBlock in node.catchBlocks) { |
- analyze(catchBlock); |
- } |
- analyzeWithDefault(node.finallyBlock, null); |
- return const StatementType(); |
- } |
- |
- visitCatchBlock(CatchBlock node) { |
- return analyze(node.block); |
- } |
- |
- visitTypedef(Typedef node) { |
- // Do not typecheck [Typedef] nodes. |
- } |
- |
- visitNode(Node node) { |
- compiler.internalError(node, |
- 'Unexpected node ${node.getObjectDescription()} in the type checker.'); |
- } |
-} |