| // 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 dart_backend; |
| |
| class LocalPlaceholder { |
| final String identifier; |
| final Set<Node> nodes; |
| LocalPlaceholder(this.identifier) : nodes = new Set<Node>(); |
| int get hashCode => identifier.hashCode; |
| String toString() => |
| 'local_placeholder[id($identifier), nodes($nodes)]'; |
| } |
| |
| class FunctionScope { |
| final Set<String> parameterIdentifiers; |
| final Set<LocalPlaceholder> localPlaceholders; |
| FunctionScope() |
| : parameterIdentifiers = new Set<String>(), |
| localPlaceholders = new Set<LocalPlaceholder>(); |
| void registerParameter(Identifier node) { |
| parameterIdentifiers.add(node.source); |
| } |
| } |
| |
| class ConstructorPlaceholder { |
| final Identifier node; |
| final ConstructorElement element; |
| |
| ConstructorPlaceholder(this.node, this.element); |
| } |
| |
| class DeclarationTypePlaceholder { |
| final TypeAnnotation typeNode; |
| final bool requiresVar; |
| DeclarationTypePlaceholder(this.typeNode, this.requiresVar); |
| } |
| |
| class SendVisitor extends Visitor { |
| final TreeElements elements; |
| final PlaceholderCollector collector; |
| |
| SendVisitor(this.collector, this.elements); |
| |
| visitSend(Send node) { |
| Element element = elements[node]; |
| if (elements.isTypeLiteral(node)) { |
| DartType type = elements.getTypeLiteralType(node); |
| if (!type.isDynamic) { |
| if (type is TypeVariableType) { |
| collector.makeTypeVariablePlaceholder(node.selector, type); |
| } else { |
| collector.makeTypePlaceholder(node.selector, type); |
| } |
| } |
| } else if (node.isSuperCall) { |
| if (element != null && element.isConstructor) { |
| collector.tryMakeConstructorPlaceholder(node, element); |
| } else { |
| collector.tryMakeMemberPlaceholder(node.selector); |
| } |
| } else if (node.isOperator) { |
| return; |
| } else if (node.isPropertyAccess) { |
| if (!Elements.isUnresolved(element) && element.impliesType) { |
| collector.makeElementPlaceholder(node, element); |
| } else { |
| visitGetterSend(node); |
| } |
| } else if (element != null && Initializers.isConstructorRedirect(node)) { |
| visitStaticSend(node); |
| } else if (Elements.isClosureSend(node, element)) { |
| if (element != null) { |
| collector.tryMakeLocalPlaceholder(element, node.selector); |
| } |
| } else { |
| if (Elements.isUnresolved(element)) { |
| if (element == null) { |
| // Example: f() with 'f' unbound. |
| // This can only happen inside an instance method. |
| visitDynamicSend(node); |
| } else { |
| visitStaticSend(node); |
| } |
| } else if (element.isInstanceMember) { |
| // Example: f() with 'f' bound to instance method. |
| visitDynamicSend(node); |
| } else if (!element.isInstanceMember) { |
| // Example: A.f() or f() with 'f' bound to a static function. |
| // Also includes new A() or new A.named() which is treated like a |
| // static call to a factory. |
| visitStaticSend(node); |
| } |
| } |
| } |
| |
| visitDynamicSend(Send node) { |
| final element = elements[node]; |
| if (element == null || !element.isMalformed) { |
| collector.tryMakeMemberPlaceholder(node.selector); |
| } |
| } |
| |
| visitGetterSend(Send node) { |
| final element = elements[node]; |
| // element == null means dynamic property access. |
| if (element == null) { |
| collector.tryMakeMemberPlaceholder(node.selector); |
| } else if (element.isMalformed) { |
| collector.makeUnresolvedPlaceholder(node); |
| return; |
| } else if (element.isPrefix) { |
| // Node is prefix part in case of source 'lib.somesetter = 5;' |
| collector.makeErasePrefixPlaceholder(node); |
| } else if (Elements.isStaticOrTopLevel(element)) { |
| // Unqualified or prefixed top level or static. |
| collector.makeElementPlaceholder(node.selector, element); |
| } else if (!element.isTopLevel) { |
| if (element.isInstanceMember) { |
| collector.tryMakeMemberPlaceholder(node.selector); |
| } else { |
| // May get FunctionExpression here in selector |
| // in case of A(int this.f()); |
| if (node.selector is Identifier) { |
| collector.tryMakeLocalPlaceholder(element, node.selector); |
| } else { |
| assert(node.selector is FunctionExpression); |
| } |
| } |
| } |
| } |
| |
| visitStaticSend(Send node) { |
| Element element = elements[node]; |
| collector.mirrorRenamer.registerStaticSend( |
| collector.currentElement, element, node); |
| |
| if (Elements.isUnresolved(element) |
| || element.isDeferredLoaderGetter) { |
| return; |
| } |
| if (element.isConstructor || element.isFactoryConstructor) { |
| // Rename named constructor in redirection position: |
| // class C { C.named(); C.redirecting() : this.named(); } |
| if (node.receiver is Identifier |
| && node.receiver.asIdentifier().isThis()) { |
| assert(node.selector is Identifier); |
| collector.tryMakeConstructorPlaceholder(node, element); |
| } |
| return; |
| } |
| collector.makeElementPlaceholder(node.selector, element); |
| // Another ugly case: <lib prefix>.<top level> is represented as |
| // receiver: lib prefix, selector: top level. |
| if (element.isTopLevel && node.receiver != null) { |
| assert(elements[node.receiver].isPrefix); |
| // Hack: putting null into map overrides receiver of original node. |
| collector.makeErasePrefixPlaceholder(node.receiver); |
| } |
| } |
| |
| internalError(Spannable node, String reason) { |
| collector.internalError(reason, node: node); |
| } |
| |
| visitNode(Node node) { |
| internalError(node, "Unhandled node"); |
| } |
| } |
| |
| class PlaceholderCollector extends Visitor { |
| final DiagnosticReporter reporter; |
| final MirrorRenamer mirrorRenamer; |
| final FunctionElement mainFunction; |
| final Set<String> fixedMemberNames; // member names which cannot be renamed. |
| final Map<Element, ElementAst> elementAsts; |
| final Set<Node> prefixNodesToErase = new Set<Node>(); |
| final Set<Node> unresolvedNodes = new Set<Node>(); |
| final Map<Element, Set<Node>> elementNodes = new Map<Element, Set<Node>>(); |
| final Map<FunctionElement, FunctionScope> functionScopes |
| = new Map<FunctionElement, FunctionScope>(); |
| final Map<LibraryElement, Set<Identifier>> privateNodes = |
| new Map<LibraryElement, Set<Identifier>>(); |
| final List<DeclarationTypePlaceholder> declarationTypePlaceholders |
| = new List<DeclarationTypePlaceholder>(); |
| final Map<String, Set<Identifier>> memberPlaceholders |
| = new Map<String, Set<Identifier>>(); |
| final List<ConstructorPlaceholder> constructorPlaceholders |
| = new List<ConstructorPlaceholder>(); |
| Map<String, LocalPlaceholder> currentLocalPlaceholders; |
| Element currentElement; |
| FunctionElement topmostEnclosingFunction; |
| TreeElements treeElements; |
| |
| get currentFunctionScope => functionScopes.putIfAbsent( |
| topmostEnclosingFunction, () => new FunctionScope()); |
| |
| PlaceholderCollector(this.reporter, this.mirrorRenamer, |
| this.fixedMemberNames, this.elementAsts, |
| this.mainFunction); |
| |
| void collectFunctionDeclarationPlaceholders( |
| FunctionElement element, FunctionExpression node) { |
| if (element.isConstructor) { |
| ConstructorElement constructor = element; |
| tryMakeConstructorPlaceholder(node.name, element); |
| RedirectingFactoryBody bodyAsRedirectingFactoryBody = |
| node.body.asRedirectingFactoryBody(); |
| if (bodyAsRedirectingFactoryBody != null) { |
| // Factory redirection. |
| FunctionElement redirectTarget = constructor.immediateRedirectionTarget; |
| assert(redirectTarget != null && redirectTarget != element); |
| tryMakeConstructorPlaceholder( |
| bodyAsRedirectingFactoryBody.constructorReference, |
| redirectTarget); |
| } |
| } else if (Elements.isStaticOrTopLevel(element)) { |
| // Note: this code should only rename private identifiers for class' |
| // fields/getters/setters/methods. Top-level identifiers are renamed |
| // just to escape conflicts and that should be enough as we shouldn't |
| // be able to resolve private identifiers for other libraries. |
| makeElementPlaceholder(node.name, element); |
| } else if (element.isClassMember) { |
| if (node.name is Identifier) { |
| tryMakeMemberPlaceholder(node.name); |
| } else { |
| assert(node.name.asSend().isOperator); |
| } |
| } |
| } |
| |
| void collectFieldDeclarationPlaceholders(Element element, Node node) { |
| Identifier name = node is Identifier ? node : node.asSend().selector; |
| if (Elements.isStaticOrTopLevel(element)) { |
| makeElementPlaceholder(name, element); |
| } else if (Elements.isInstanceField(element)) { |
| tryMakeMemberPlaceholder(name); |
| } |
| } |
| |
| void collect(Element element) { |
| this.currentElement = element; |
| this.topmostEnclosingFunction = null; |
| final ElementAst elementAst = elementAsts[element]; |
| this.treeElements = elementAst.treeElements; |
| Node elementNode = elementAst.ast; |
| if (element is FunctionElement) { |
| collectFunctionDeclarationPlaceholders(element, elementNode); |
| } else if (element is VariableElement) { |
| VariableDefinitions definitions = elementNode; |
| Node definition = definitions.definitions.nodes.head; |
| collectFieldDeclarationPlaceholders(element, definition); |
| makeVarDeclarationTypePlaceholder(definitions); |
| } else { |
| assert(element is ClassElement || element is TypedefElement); |
| } |
| currentLocalPlaceholders = new Map<String, LocalPlaceholder>(); |
| if (!(element is ConstructorElement && element.isRedirectingFactory)) { |
| // Do not visit the body of redirecting factories. |
| reporter.withCurrentElement(element, () { |
| elementNode.accept(this); |
| }); |
| } |
| } |
| |
| // TODO(karlklose): should we create placeholders for these? |
| bool isTypedefParameter(Element element) { |
| return element != null && |
| element.enclosingElement != null && |
| element.enclosingElement.isTypedef; |
| } |
| |
| void tryMakeLocalPlaceholder(Element element, Identifier node) { |
| bool isNamedOptionalParameter() { |
| FunctionTypedElement function = element.enclosingElement; |
| FunctionSignature signature = function.functionSignature; |
| if (!signature.optionalParametersAreNamed) return false; |
| for (Element parameter in signature.optionalParameters) { |
| if (identical(parameter, element)) return true; |
| } |
| return false; |
| } |
| if (element.isParameter && !isTypedefParameter(element) && |
| isNamedOptionalParameter()) { |
| currentFunctionScope.registerParameter(node); |
| } else if (Elements.isLocal(element) && !isTypedefParameter(element)) { |
| makeLocalPlaceholder(node); |
| } |
| } |
| |
| void tryMakeMemberPlaceholder(Identifier node) { |
| assert(node != null); |
| if (node is Operator) return; |
| String identifier = node.source; |
| if (fixedMemberNames.contains(identifier)) return; |
| memberPlaceholders.putIfAbsent( |
| identifier, () => new Set<Identifier>()).add(node); |
| } |
| |
| void makeTypePlaceholder(Node node, DartType type) { |
| Send send = node.asSend(); |
| if (send != null) { |
| // Prefix. |
| assert(send.receiver is Identifier); |
| assert(send.selector is Identifier); |
| makeErasePrefixPlaceholder(send.receiver); |
| node = send.selector; |
| } |
| makeElementPlaceholder(node, type.element); |
| } |
| |
| void makeTypeVariablePlaceholder(Node node, TypeVariableType type) { |
| Send send = node.asSend(); |
| if (send != null) { |
| // Prefix. |
| assert(send.receiver is Identifier); |
| assert(send.selector is Identifier); |
| makeErasePrefixPlaceholder(send.receiver); |
| node = send.selector; |
| } |
| tryMakeMemberPlaceholder(node); |
| } |
| |
| void makeOmitDeclarationTypePlaceholder(TypeAnnotation type) { |
| if (type == null) return; |
| declarationTypePlaceholders.add( |
| new DeclarationTypePlaceholder(type, false)); |
| } |
| |
| void makeVarDeclarationTypePlaceholder(VariableDefinitions node) { |
| // TODO(smok): Maybe instead of calling this method and |
| // makeDeclaratioTypePlaceholder have type declaration placeholder |
| // collector logic in visitVariableDefinitions when resolver becomes better |
| // and/or catch syntax changes. |
| if (node.type == null) return; |
| bool requiresVar = !node.modifiers.isFinalOrConst; |
| declarationTypePlaceholders.add( |
| new DeclarationTypePlaceholder(node.type, requiresVar)); |
| } |
| |
| /// Marks [node] to be erased in the output. |
| /// This is done for library prefixes because they are not used in the output |
| /// because all imports are flattened and conflicts are renamed away. |
| void makeErasePrefixPlaceholder(Node node) { |
| assert(node is Identifier || node is Send); |
| prefixNodesToErase.add(node); |
| } |
| |
| void makeElementPlaceholder(Node node, Element element) { |
| assert(node != null); |
| assert(element != null); |
| LibraryElement library = element.library; |
| if (identical(element, mainFunction)) return; |
| if (library.isDartCore) return; |
| |
| if (library.isPlatformLibrary && !element.isTopLevel) { |
| return; |
| } |
| |
| ClassElement cls = element.enclosingClass; |
| if (cls != null && cls.isEnumClass) { |
| // Enums and enum values cannot be changed, since the semantics of |
| // `toString` is defined by the names of the declarations. |
| return; |
| } |
| |
| if (element.isAccessor) { |
| element = (element as AccessorElement).abstractField; |
| } |
| elementNodes.putIfAbsent(element, () => new Set<Node>()).add(node); |
| } |
| |
| /// Marks [node] to be renamed per-library if it names an instance member |
| /// and has a private name. |
| void tryMakePrivateIdentifier(Node node, Element element) { |
| if (node is Identifier && |
| !Elements.isStaticOrTopLevel(element) && |
| !Elements.isLocal(element) && |
| Name.isPrivateName(node.source)) { |
| privateNodes.putIfAbsent( |
| currentElement.library, () => new Set<Identifier>()).add(node); |
| } |
| } |
| |
| void makeUnresolvedPlaceholder(Node node) { |
| unresolvedNodes.add(node); |
| } |
| |
| void makeLocalPlaceholder(Identifier identifier) { |
| LocalPlaceholder getLocalPlaceholder() { |
| String name = identifier.source; |
| return currentLocalPlaceholders.putIfAbsent(name, () { |
| LocalPlaceholder localPlaceholder = new LocalPlaceholder(name); |
| currentFunctionScope.localPlaceholders.add(localPlaceholder); |
| return localPlaceholder; |
| }); |
| } |
| getLocalPlaceholder().nodes.add(identifier); |
| } |
| |
| /// Finds the first constructor on the chain of definingConstructor from |
| /// [element] that is not in a synthetic class. |
| Element findDefiningConstructor(ConstructorElement element) { |
| while (element.definingConstructor != null) { |
| element = element.definingConstructor; |
| } |
| return element; |
| } |
| |
| void tryMakeConstructorPlaceholder(Node node, ConstructorElement element) { |
| if (Elements.isUnresolved(element)) { |
| makeUnresolvedPlaceholder(node); |
| return; |
| } |
| // A library prefix. |
| Node prefix; |
| // The name of the class with the constructor. |
| Node className; |
| // Will be null for unnamed constructors. |
| Identifier constructorName; |
| // First deconstruct the constructor, there are 4 possibilities: |
| // ClassName() |
| // prefix.ClassName() |
| // ClassName.constructorName() |
| // prefix.ClassName.constructorName() |
| if (node is Send) { |
| if (node.receiver is Send) { |
| Send receiver = node.receiver; |
| // prefix.ClassName.constructorName() |
| assert(treeElements[receiver.receiver] != null && |
| treeElements[receiver.receiver].isPrefix); |
| prefix = receiver.receiver; |
| className = receiver.selector; |
| constructorName = node.selector; |
| } else { |
| Element receiverElement = treeElements[node.receiver]; |
| if (receiverElement != null && receiverElement.isPrefix) { |
| // prefix.ClassName() |
| prefix = node.receiver; |
| className = node.selector; |
| } else { |
| // ClassName.constructorName() |
| className = node.receiver; |
| constructorName = node.selector; |
| } |
| } |
| } else { |
| // ClassName() |
| className = node; |
| } |
| |
| if (prefix != null) { |
| makeErasePrefixPlaceholder(prefix); |
| } |
| |
| if (className is TypeAnnotation) { |
| visitTypeAnnotation(className); |
| } else if (Elements.isUnresolved(element)) { |
| // We handle unresolved nodes elsewhere. |
| } else if (className.isThis() || className.isSuper()) { |
| // Do not rename super and this. |
| } else if (className is Identifier) { |
| makeElementPlaceholder(className, element.contextClass); |
| } else { |
| throw "Bad type of constructor name $className"; |
| } |
| |
| if (constructorName != null) { |
| Element definingConstructor = findDefiningConstructor(element); |
| constructorPlaceholders.add(new ConstructorPlaceholder(constructorName, |
| definingConstructor)); |
| tryMakePrivateIdentifier(constructorName, element); |
| } |
| } |
| |
| void internalError(String reason, {Node node}) { |
| reporter.internalError(node, reason); |
| } |
| |
| visit(Node node) => (node == null) ? null : node.accept(this); |
| |
| visitNode(Node node) { node.visitChildren(this); } // We must go deeper. |
| |
| visitNewExpression(NewExpression node) { |
| Send send = node.send; |
| DartType type = treeElements.getType(node); |
| assert(type != null); |
| Element constructor = treeElements[send]; |
| assert(constructor != null); |
| assert(send.receiver == null); |
| if (!Elements.isMalformed(constructor)) { |
| tryMakeConstructorPlaceholder(node.send.selector, constructor); |
| // TODO(smok): Should this be in visitNamedArgument? |
| // Field names can be exposed as names of optional arguments, e.g. |
| // class C { |
| // final field; |
| // C([this.field]); |
| // } |
| // Do not forget to rename them as well. |
| FunctionElement constructorFunction = constructor; |
| List<Element> optionalParameters = |
| constructorFunction.functionSignature.optionalParameters; |
| for (final argument in send.argumentsNode) { |
| NamedArgument named = argument.asNamedArgument(); |
| if (named == null) continue; |
| Identifier name = named.name; |
| String nameAsString = name.source; |
| for (final parameter in optionalParameters) { |
| if (parameter.isInitializingFormal) { |
| if (parameter.name == nameAsString) { |
| tryMakeMemberPlaceholder(name); |
| break; |
| } |
| } |
| } |
| } |
| } else { |
| makeUnresolvedPlaceholder(node.send.selector); |
| } |
| visit(node.send.argumentsNode); |
| } |
| |
| visitSend(Send send) { |
| Element element = treeElements[send]; |
| tryMakePrivateIdentifier(send.selector, element); |
| new SendVisitor(this, treeElements).visitSend(send); |
| send.visitChildren(this); |
| } |
| |
| visitSendSet(SendSet send) { |
| Element element = treeElements[send]; |
| if (Elements.isMalformed(element)) { |
| // Complicated case: constructs like receiver.selector++ can resolve |
| // to ErroneousElement. Fortunately, receiver.selector still |
| // can be resoved via treeElements[send.selector], that's all |
| // that is needed to rename the construct properly. |
| element = treeElements[send.selector]; |
| } |
| tryMakePrivateIdentifier(send.selector, element); |
| if (element == null) { |
| if (send.receiver != null) tryMakeMemberPlaceholder(send.selector); |
| } else if (!element.isMalformed) { |
| if (Elements.isStaticOrTopLevel(element)) { |
| // TODO(smok): Worth investigating why sometimes we get getter/setter |
| // here and sometimes abstract field. |
| assert(element.isClass || element is VariableElement || |
| element.isAccessor || element.isAbstractField || |
| element.isFunction || element.isTypedef || |
| element is TypeVariableElement); |
| makeElementPlaceholder(send.selector, element); |
| } else { |
| Identifier identifier = send.selector.asIdentifier(); |
| if (identifier == null) { |
| // Handle optional function expression parameters with default values. |
| identifier = send.selector.asFunctionExpression().name; |
| } |
| if (Elements.isInstanceField(element)) { |
| tryMakeMemberPlaceholder(identifier); |
| } else { |
| tryMakeLocalPlaceholder(element, identifier); |
| } |
| } |
| } |
| send.visitChildren(this); |
| } |
| |
| visitTypeAnnotation(TypeAnnotation node) { |
| final type = treeElements.getType(node); |
| assert(invariant(node, type != null, |
| message: "Missing type for type annotation: $treeElements")); |
| if (!type.isVoid) { |
| if (!type.treatAsDynamic) { |
| if (type is TypeVariableType) { |
| makeTypeVariablePlaceholder(node.typeName, type); |
| } else { |
| makeTypePlaceholder(node.typeName, type); |
| } |
| } else if (!type.isDynamic) { |
| makeUnresolvedPlaceholder(node.typeName); |
| } |
| } |
| // Visit only type arguments, otherwise in case of lib.Class type |
| // annotation typeName is Send and we go to visitGetterSend, as a result |
| // "Class" is added to member placeholders. |
| visit(node.typeArguments); |
| } |
| |
| visitVariableDefinitions(VariableDefinitions node) { |
| // Collect only local placeholders. |
| for (Node definition in node.definitions.nodes) { |
| Element definitionElement = treeElements[definition]; |
| // definitionElement may be null if we're inside variable definitions |
| // of a function that is a parameter of another function. |
| // TODO(smok): Fix this when resolver correctly deals with |
| // such cases. |
| if (definitionElement == null) continue; |
| |
| Send send = definition.asSend(); |
| Identifier identifier = definition is Identifier |
| ? definition |
| : definition is Send |
| ? (send.selector is Identifier |
| ? send.selector |
| : null) |
| : null; |
| |
| tryMakePrivateIdentifier(identifier, definitionElement); |
| |
| if (send != null) { |
| // May get FunctionExpression here in definition.selector |
| // in case of A(int this.f()); |
| if (send.selector is Identifier) { |
| if (definitionElement.isInitializingFormal) { |
| tryMakeMemberPlaceholder(send.selector); |
| } else { |
| tryMakeLocalPlaceholder(definitionElement, send.selector); |
| } |
| } else { |
| assert(send.selector is FunctionExpression); |
| if (definitionElement.isInitializingFormal) { |
| tryMakeMemberPlaceholder( |
| send.selector.asFunctionExpression().name); |
| } |
| } |
| } else if (definition is Identifier) { |
| tryMakeLocalPlaceholder(definitionElement, definition); |
| } else if (definition is FunctionExpression) { |
| // Skip, it will be processed in visitFunctionExpression. |
| } else { |
| internalError('Unexpected definition structure $definition'); |
| } |
| } |
| node.visitChildren(this); |
| } |
| |
| visitFunctionExpression(FunctionExpression node) { |
| bool isKeyword(Identifier id) => |
| id != null && Keyword.keywords[id.source] != null; |
| |
| Element element = treeElements[node]; |
| // May get null; |
| if (element != null) { |
| tryMakePrivateIdentifier(node.name, element); |
| |
| // Rename only local functions. |
| if (topmostEnclosingFunction == null && |
| element is! LocalParameterElement && |
| element is! InitializingFormalElement) { |
| topmostEnclosingFunction = element; |
| } |
| if (!identical(element, currentElement)) { |
| if (node.name != null) { |
| assert(node.name is Identifier); |
| tryMakeLocalPlaceholder(element, node.name); |
| } |
| } |
| } |
| |
| node.visitChildren(this); |
| |
| // Make sure we don't omit return type of methods which names are |
| // identifiers, because the following works fine: |
| // int interface() => 1; |
| // But omitting 'int' makes VM unhappy. |
| // TODO(smok): Remove it when http://dartbug.com/5278 is fixed. |
| if (node.name == null || !isKeyword(node.name.asIdentifier())) { |
| makeOmitDeclarationTypePlaceholder(node.returnType); |
| } |
| collectFunctionParameters(node.parameters); |
| } |
| |
| void collectFunctionParameters(NodeList parameters) { |
| if (parameters == null) return; |
| for (Node parameter in parameters.nodes) { |
| if (parameter is NodeList) { |
| // Optional parameter list. |
| collectFunctionParameters(parameter); |
| } else { |
| assert(parameter is VariableDefinitions); |
| makeOmitDeclarationTypePlaceholder( |
| parameter.asVariableDefinitions().type); |
| } |
| } |
| } |
| |
| visitClassNode(ClassNode node) { |
| ClassElement classElement = currentElement; |
| makeElementPlaceholder(node.name, classElement); |
| node.visitChildren(this); |
| } |
| |
| visitNamedMixinApplication(NamedMixinApplication node) { |
| ClassElement classElement = currentElement; |
| makeElementPlaceholder(node.name, classElement); |
| node.visitChildren(this); |
| } |
| |
| visitTypeVariable(TypeVariable node) { |
| DartType type = treeElements.getType(node); |
| assert(invariant(node, type != null, |
| message: "Missing type for type variable: $treeElements")); |
| makeTypeVariablePlaceholder(node.name, type); |
| node.visitChildren(this); |
| } |
| |
| visitTypedef(Typedef node) { |
| assert(currentElement is TypedefElement); |
| makeElementPlaceholder(node.name, currentElement); |
| node.visitChildren(this); |
| makeOmitDeclarationTypePlaceholder(node.returnType); |
| collectFunctionParameters(node.formals); |
| } |
| |
| visitBlock(Block node) { |
| for (Node statement in node.statements.nodes) { |
| if (statement is VariableDefinitions) { |
| makeVarDeclarationTypePlaceholder(statement); |
| } |
| } |
| node.visitChildren(this); |
| } |
| } |