package com.intellij.javascript.flex.resolve; import com.intellij.lang.javascript.JavaScriptSupportLoader; import com.intellij.lang.javascript.psi.*; import com.intellij.lang.javascript.psi.e4x.JSE4XNamespaceReference; import com.intellij.lang.javascript.psi.ecmal4.JSClass; import com.intellij.lang.javascript.psi.ecmal4.JSQualifiedNamedElement; import com.intellij.lang.javascript.psi.ecmal4.impl.JSPackageWrapper; import com.intellij.lang.javascript.psi.impl.JSOffsetBasedImplicitElement; import com.intellij.lang.javascript.psi.resolve.*; import com.intellij.lang.javascript.psi.resolve.context.JSApplyCallElement; import com.intellij.lang.javascript.psi.resolve.context.JSApplyContextElement; import com.intellij.lang.javascript.psi.types.*; import com.intellij.lang.javascript.psi.types.primitives.JSPrimitiveArrayType; import com.intellij.lang.javascript.psi.util.JSUtils; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlToken; import com.intellij.psi.xml.XmlTokenType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.intellij.lang.javascript.psi.JSCommonTypeNames.ARRAY_CLASS_NAME; import static com.intellij.lang.javascript.psi.JSCommonTypeNames.VECTOR_CLASS_NAME; /** * @author Konstantin.Ulitin */ public class ActionScriptTypeEvaluator extends JSTypeEvaluator { private static final String REPEATER_CLASS_FQN = "mx.core.Repeater"; public ActionScriptTypeEvaluator(JSEvaluateContext context, JSTypeProcessor processor, boolean ecma) { super(context, processor, ecma); } @Override protected boolean addTypeFromDialectSpecificElements(PsiElement resolveResult) { if (resolveResult instanceof JSPackageWrapper) { JSReferenceExpression expression = myContext.getProcessedExpression(); if (myTypeProcessor instanceof PsiScopeProcessor && expression != null) { if (myTypeProcessor instanceof ResolveProcessor) ((ResolveProcessor)myTypeProcessor).prefixResolved(); resolveResult.processDeclarations((PsiScopeProcessor)myTypeProcessor, ResolveState.initial(), expression, expression); } else { String name = ((JSQualifiedNamedElement)resolveResult).getQualifiedName(); if (name != null) { addType(name, resolveResult); } } return true; } return false; } @Override protected void evaluateNewExpressionTypes(JSNewExpression newExpression, @NotNull JSEvaluateContext.JSEvaluationPlace place) { JSExpression methodExpr = newExpression.getMethodExpression(); if (methodExpr != null) { if (methodExpr instanceof JSArrayLiteralExpression) { JSTypeSource source = JSTypeSourceFactory.createTypeSource(methodExpr); JSType type = JSNamedType.createType(VECTOR_CLASS_NAME, source, JSContext.INSTANCE); PsiElement arrayInitializingType = newExpression.getArrayInitializingType(); if (arrayInitializingType != null) { JSType argType = JSTypeUtils.createType(JSImportHandlingUtil.resolveTypeName(arrayInitializingType.getText(), newExpression), source); type = new JSGenericTypeImpl(source, type, argType); } addType(type, methodExpr); } else { JSType type = JSAnyType.get(methodExpr, false); if (methodExpr instanceof JSReferenceExpression) { PsiElement resolve = ((JSReferenceExpression)methodExpr).resolve(); if (JSResolveUtil.isConstructorFunction(resolve) && resolve.getParent() instanceof JSClass) { resolve = resolve.getParent(); } if (resolve instanceof JSClass || resolve == null) { JSType typeFromText = JSTypeUtils.createType(methodExpr.getText(), JSTypeSourceFactory.createTypeSource(methodExpr, false)); if (typeFromText != null) type = typeFromText; } } addType(type, methodExpr); } } } @Override protected void addTypeFromClass(@NotNull PsiElement resolveResult, @Nullable PsiElement constructor) { if (resolveResult instanceof JSFunction) { resolveResult = resolveResult.getParent(); } final JSReferenceExpression expression = myContext.getProcessedExpression(); if (expression == null) return; PsiElement parent = expression.getParent(); if (parent instanceof JSExpression) parent = JSUtils.unparenthesize((JSExpression)parent); String psiElementType = parent instanceof JSReferenceExpression || JSResolveUtil.isExprInStrictTypeContext(expression) || PsiTreeUtil.getChildOfType(expression, JSE4XNamespaceReference.class) != null || // TODO avoid it parent instanceof JSCallExpression ? ((JSClass)resolveResult).getQualifiedName():"Class"; JSTypeSource source = JSTypeSourceFactory.createTypeSource(expression); JSType type = JSNamedType.createType(psiElementType, source, JSContext.UNKNOWN); if (JSTypeUtils.isActionScriptVectorType(type)) { type = JSTypeUtils.createType(JSImportHandlingUtil.resolveTypeName(expression.getText(), expression), source); } final JSApplyContextElement peek = myContext.peekJSElementToApply(); if (peek instanceof JSApplyCallElement) myContext.popJSElementToApply(); // MyClass(anyVar) is cast to MyClass addType(type, resolveResult); if (peek instanceof JSApplyCallElement) myContext.pushJSElementToApply(peek); } @Override protected boolean useVariableType(JSType type) { return myContext.isJSElementsToApplyEmpty() && super.useVariableType(type); } @Override protected boolean addTypeFromElementResolveResult(PsiElement resolveResult, boolean hasSomeType) { if (resolveResult instanceof JSOffsetBasedImplicitElement && JavaScriptSupportLoader.isFlexMxmFile(resolveResult.getContainingFile())) { resolveResult = ((JSOffsetBasedImplicitElement)resolveResult).getElementAtOffset(); } if (resolveResult instanceof XmlToken) { final XmlToken xmlToken = (XmlToken)resolveResult; final XmlAttribute xmlAttribute = PsiTreeUtil.getParentOfType(xmlToken, XmlAttribute.class); final XmlTag xmlTag = PsiTreeUtil.getParentOfType(xmlToken, XmlTag.class); if (xmlToken.getTokenType() == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN && xmlAttribute != null && "id".equals(xmlAttribute.getName()) && xmlTag != null && isInsideRepeaterTag(xmlTag)) { final PsiElement arrayClass = ActionScriptClassResolver.findClassByQNameStatic(ARRAY_CLASS_NAME, xmlToken); if (arrayClass != null) { final String arrayType = new JSTagContextBuilder(resolveResult, null).typeName; JSTypeSource source = JSTypeSourceFactory.createTypeSource(resolveResult); JSType type; if (arrayType != null) { JSType baseType = JSNamedType.createType(arrayType, source, JSContext.INSTANCE); type = new JSArrayTypeImpl(baseType, source); } else { type = new JSPrimitiveArrayType(source, JSTypeContext.INSTANCE); } addType(type, arrayClass); } } else { final XmlTag tag = PsiTreeUtil.getParentOfType(resolveResult, XmlTag.class, false); final JSClass clazz = JSResolveUtil.getClassFromTagNameInMxml(tag); if (clazz != null) { final String name = clazz.getQualifiedName(); if (name != null) { addType(name, clazz); } } } return hasSomeType; } return super.addTypeFromElementResolveResult(resolveResult, hasSomeType); } private static boolean isInsideRepeaterTag(@NotNull final XmlTag xmlTag) { PsiElement parent = xmlTag; while ((parent = parent.getParent()) instanceof XmlTag) { if (REPEATER_CLASS_FQN.equals(new JSTagContextBuilder(parent, "").typeName)) { return true; } } return false; } @Override public void addType(@Nullable final JSType _type, @Nullable PsiElement source) { if (_type != null && myContext.isJSElementsToApplyEmpty() && (source == null || source == EXPLICIT_TYPE_MARKER_ELEMENT) ) { // TODO [ksafonov] enforced scope (and context) should internal part of JSType.resolve() JSClass jsClass = JSInheritanceUtil.withEnforcedScope(() -> _type.resolveClass(), JSResolveUtil.getResolveScope(myContext.targetFile)); if (jsClass != null) { source = jsClass; } } super.addType(_type, source); } }