/* * (non-Javadoc) * @see com.aptana.editor.js.parsing.ast.JSTreeWalker#visit(com.aptana.editor.js.parsing.ast.JSFunctionNode) */ @Override public void visit(JSFunctionNode node) { List<String> types = new ArrayList<String>(); JSScope scope = this.getActiveScope(node.getBody().getStartingOffset()); boolean foundReturnExpression = false; // infer return types for (JSReturnNode returnValue : node.getReturnNodes()) { IParseNode expression = returnValue.getExpression(); if (!expression.isEmpty()) { foundReturnExpression = true; types.addAll(this.getTypes(expression, scope)); } } // If we couldn't infer a return type and we had a return // expression, then have it return Object if (foundReturnExpression && types.isEmpty()) { types.add(JSTypeConstants.OBJECT_TYPE); } // build function type, including return values String type = JSTypeUtil.toFunctionType(types); this.addType(type); }
/** * addType * * @param type */ protected void addType(String type) { if (type != null && type.length() > 0) { if (this._types == null) { this._types = new ArrayList<String>(); } type = JSTypeUtil.validateTypeName(type); if (!this._types.contains(type)) { this._types.add(type); } } }
/* * (non-Javadoc) * @see com.aptana.editor.js.parsing.ast.JSTreeWalker#visit(com.aptana.editor.js.parsing.ast.JSArrayNode) */ @Override public void visit(JSArrayNode node) { if (!node.hasChildren()) { this.addType(JSTypeConstants.ARRAY_TYPE); } else { // TODO: Add all element types? // TODO: Create equivalent of "structure" type if element types vary? for (String type : this.getTypes(node.getFirstChild())) { this.addType(JSTypeUtil.createGenericArrayType(type)); } } }
/* * (non-Javadoc) * @see com.aptana.editor.js.parsing.ast.JSTreeWalker#visit(com.aptana.editor.js.parsing.ast.JSGetElementNode) */ @Override public void visit(JSGetElementNode node) { // TODO: Should check subscript to determine if the type is a Number or // a String. If it is a String, then this should behave like get-property // assuming we can retrieve a literal string. IParseNode lhs = node.getLeftHandSide(); if (lhs instanceof JSNode) { for (String typeName : this.getTypes(lhs)) { String typeString = JSTypeUtil.getArrayElementType(typeName); if (typeString != null) { this.addType(typeString); } else { this.addType(JSTypeConstants.OBJECT_TYPE); } } } }
/* * (non-Javadoc) * @see com.aptana.editor.js.parsing.ast.JSTreeWalker#visit(com.aptana.editor.js.parsing.ast.JSConstructNode) */ @Override public void visit(JSConstructNode node) { // TODO: Need to handle any property assignments off of "this" IParseNode child = node.getExpression(); if (child instanceof JSNode) { List<String> types = this.getTypes(child); List<String> returnTypes = new ArrayList<String>(); for (String typeName : types) { if (typeName.startsWith(JSTypeConstants.GENERIC_CLASS_OPEN)) { returnTypes.add(JSTypeUtil.getClassType(typeName)); } else { // FIXME If this is a function that returns a type, assume that function is a constructor // and we've // defined the type as Function<Type>. // This is where the properties for that type are going to be hung. // That may not be "right". We may want to unwrap the function's return type and use that // as the // type we're dealing with. // in that case, we need to change JSSymbolTypeInferrer#generateType to also unwrap // Function<Type> // properly. returnTypes.add(typeName); } } for (String typeName : returnTypes) { Collection<PropertyElement> properties = this._queryHelper.getTypeMembers( this._index, typeName, JSTypeConstants.PROTOTYPE_PROPERTY); if (properties != null) { for (PropertyElement property : properties) { for (String propertyType : property.getTypeNames()) { this.addType(propertyType); } } } } } }
/* * (non-Javadoc) * @see com.aptana.editor.js.parsing.ast.JSTreeWalker#visit(com.aptana.editor.js.parsing.ast.JSInvokeNode) */ @Override public void visit(JSInvokeNode node) { IParseNode child = node.getExpression(); if (child instanceof JSNode) { // TODO hang the "require" string as a constant somewhere! if (child instanceof JSIdentifierNode && "require".equals(child.getNameNode().getName())) // $NON-NLS-1$ { // it's a requires! JSArgumentsNode args = (JSArgumentsNode) node.getArguments(); IParseNode[] children = args.getChildren(); for (IParseNode arg : children) { if (arg instanceof JSStringNode) { JSStringNode string = (JSStringNode) arg; String text = string.getText(); // strip quotes TODO Use util method to strip quotes! if (text.startsWith("'") || text.startsWith("\"")) // $NON-NLS-1$ //$NON-NLS-2$ { text = text.substring(1, text.length() - 1); } // Handle resolving absolute versus relative module ids! URI resolved = _location.resolve(text); URI relative = _index.getRelativeDocumentPath(resolved); this.addType(relative.getPath() + ".exports"); // $NON-NLS-1$ } } } List<String> types = this.getTypes(child); // NOTE: This is a special case for functions used as a RHS of assignments or as part of a // property chain. // If the invocation returns undefined, we change that to Object. // TODO: As a refinement, we want to check that the function being called is not defined in // the current // scope if (types.isEmpty()) { IParseNode parent = node.getParent(); if (parent != null) { switch (parent.getNodeType()) { case IJSNodeTypes.ASSIGN: if (node.getIndex() == 1) { this.addType(JSTypeConstants.OBJECT_TYPE); } break; case IJSNodeTypes.GET_PROPERTY: this.addType(JSTypeConstants.OBJECT_TYPE); break; default: break; } } } for (String typeName : types) { if (JSTypeUtil.isFunctionPrefix(typeName)) { List<String> returnTypes = JSTypeUtil.getFunctionSignatureReturnTypeNames(typeName); for (String returnTypeName : returnTypes) { this.addType(returnTypeName); } } } } }