package com.jetbrains.lang.dart.util; import com.google.common.collect.Lists; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.LeafPsiElement; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlFile; import com.intellij.util.BooleanValueHolder; import com.intellij.util.Function; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.lang.dart.DartComponentType; import com.jetbrains.lang.dart.DartTokenTypesSets; import com.jetbrains.lang.dart.ide.index.*; import com.jetbrains.lang.dart.ide.info.DartFunctionDescription; import com.jetbrains.lang.dart.ide.info.DartParameterInfoHandler; import com.jetbrains.lang.dart.psi.*; import com.jetbrains.lang.dart.psi.impl.AbstractDartPsiClass; import com.jetbrains.lang.dart.psi.impl.DartPsiCompositeElementImpl; import com.jetbrains.lang.dart.resolve.DartPsiScopeProcessor; import com.jetbrains.lang.dart.resolve.DartResolveProcessor; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; import static com.jetbrains.lang.dart.ide.index.DartImportOrExportInfo.Kind; import static com.jetbrains.lang.dart.util.DartUrlResolver.DART_CORE_URI; public class DartResolveUtil { public static final String OBJECT = "Object"; public static List<PsiElement> findDartRoots(@Nullable final PsiFile psiFile) { if (psiFile instanceof XmlFile) { return findDartRootsInXml((XmlFile)psiFile); } return psiFile instanceof DartFile ? Collections.singletonList(psiFile) : Collections.emptyList(); } private static List<PsiElement> findDartRootsInXml(XmlFile xmlFile) { final List<PsiElement> result = new ArrayList<>(); xmlFile.acceptChildren(new XmlRecursiveElementWalkingVisitor() { @Override public void visitElement(PsiElement element) { if (element instanceof DartEmbeddedContent) { result.add(element); return; } super.visitElement(element); } }); return result; } public static boolean isLValue(PsiElement element) { if (element instanceof PsiFile) return false; PsiElement nextSibling = UsefulPsiTreeUtil.getNextSiblingSkippingWhiteSpacesAndComments(element); while (nextSibling == null && element != null) { element = element.getParent(); nextSibling = UsefulPsiTreeUtil.getNextSiblingSkippingWhiteSpacesAndComments(element); } if (nextSibling instanceof LeafPsiElement) { return DartTokenTypesSets.ASSIGNMENT_OPERATORS.contains(((LeafPsiElement)nextSibling).getElementType()); } return nextSibling instanceof DartAssignmentOperator; } public static boolean checkParametersType(DartFormalParameterList list, DartClass... classes) { final List<DartNormalFormalParameter> normalFormalParameterList = list.getNormalFormalParameterList(); int i = 0; for (int size = normalFormalParameterList.size(); i < size; i++) { if (i >= classes.length) return false; final DartNormalFormalParameter normalFormalParameter = normalFormalParameterList.get(i); final DartType dartType = findType(normalFormalParameter); if (dartType != null && !canAssign(resolveClassByType(dartType).getDartClass(), classes[i])) { return false; } } final DartOptionalFormalParameters optionalFormalParameters = list.getOptionalFormalParameters(); if (optionalFormalParameters == null) { return true; } for (int size = optionalFormalParameters.getDefaultFormalNamedParameterList().size(); i - normalFormalParameterList.size() < size; ++i) { final DartDefaultFormalNamedParameter defaultFormalParameter = optionalFormalParameters.getDefaultFormalNamedParameterList().get(i - normalFormalParameterList.size()); final DartType dartType = findType(defaultFormalParameter.getNormalFormalParameter()); if (dartType != null && !canAssign(resolveClassByType(dartType).getDartClass(), classes[i])) { return false; } } return true; } private static boolean canAssign(@Nullable final DartClass baseClass, @Nullable DartClass aClass) { if (baseClass == null || aClass == null) { return true; } final BooleanValueHolder result = new BooleanValueHolder(false); processSuperClasses(dartClass -> { if (dartClass == baseClass) { result.setValue(true); return false; } return true; }, aClass); return result.getValue(); } @Nullable public static DartType findType(@Nullable PsiElement element) { if (element instanceof DartDefaultFormalNamedParameter) { return findType(((DartDefaultFormalNamedParameter)element).getNormalFormalParameter()); } if (element instanceof DartNormalFormalParameter) { //final DartFunctionFormalParameter functionFormalParameter = ((DartNormalFormalParameter)element).getFunctionFormalParameter(); final DartFieldFormalParameter fieldFormalParameter = ((DartNormalFormalParameter)element).getFieldFormalParameter(); final DartSimpleFormalParameter simpleFormalParameter = ((DartNormalFormalParameter)element).getSimpleFormalParameter(); // todo return some FUNCTION type? //if (functionFormalParameter != null) {} if (fieldFormalParameter != null) return fieldFormalParameter.getType(); if (simpleFormalParameter != null) return simpleFormalParameter.getType(); } return null; } @NotNull public static DartClassResolveResult findCoreClass(PsiElement context, String className) { final VirtualFile dartCoreLib = DartLibraryIndex.getSdkLibByUri(context.getProject(), DART_CORE_URI); final List<DartComponentName> result = new ArrayList<>(); processTopLevelDeclarations(context, new DartResolveProcessor(result, className), dartCoreLib, className); final PsiElement parent = result.isEmpty() ? null : result.iterator().next().getParent(); return DartClassResolveResult.create(parent instanceof DartClass ? (DartClass)parent : null); } @NotNull public static String getLibraryName(@NotNull final PsiFile psiFile) { for (PsiElement root : findDartRoots(psiFile)) { final DartLibraryStatement libraryStatement = PsiTreeUtil.getChildOfType(root, DartLibraryStatement.class); if (libraryStatement != null) { return libraryStatement.getLibraryNameElement().getName(); } final DartPartOfStatement partOfStatement = PsiTreeUtil.getChildOfType(root, DartPartOfStatement.class); if (partOfStatement != null) { return partOfStatement.getLibraryName(); } } return psiFile.getName(); } @NotNull public static Collection<DartClass> getClassDeclarations(@NotNull final PsiElement root) { final List<DartClass> result = new SmartList<>(); for (PsiElement child = root.getFirstChild(); child != null; child = child.getNextSibling()) { if (child instanceof DartClass) { result.add((DartClass)child); } } return result; } public static void processTopLevelDeclarations(final @NotNull PsiElement context, final @NotNull DartPsiScopeProcessor processor, final @NotNull List<VirtualFile> files, final @Nullable String componentNameHint) { for (VirtualFile virtualFile : files) { if (!processTopLevelDeclarations(context, processor, virtualFile, componentNameHint)) { break; } } } public static boolean processTopLevelDeclarations(final @NotNull PsiElement context, final @NotNull DartPsiScopeProcessor processor, final @Nullable VirtualFile rootVirtualFile, final @Nullable String componentNameHint) { final Set<VirtualFile> filesOfInterest = componentNameHint == null ? null : (Set<VirtualFile>)DartComponentIndex.getAllFiles(componentNameHint, context.getResolveScope()); if (filesOfInterest != null && filesOfInterest.isEmpty()) return true; final boolean privateOnly = componentNameHint != null && componentNameHint.startsWith("_"); return processTopLevelDeclarationsImpl(context, processor, rootVirtualFile, filesOfInterest, new THashSet<>(), privateOnly); } private static boolean processTopLevelDeclarationsImpl(final @NotNull PsiElement context, final @NotNull DartPsiScopeProcessor processor, final @Nullable VirtualFile virtualFile, final @Nullable Set<VirtualFile> filesOfInterest, final @NotNull Set<VirtualFile> alreadyProcessed, final boolean privateOnly) { if (virtualFile == null) return true; if (alreadyProcessed.contains(virtualFile)) { processor.processFilteredOutElementsForImportedFile(virtualFile); return true; } alreadyProcessed.add(virtualFile); boolean contains = filesOfInterest == null || filesOfInterest.contains(virtualFile); if (contains) { final PsiFile psiFile = context.getManager().findFile(virtualFile); for (PsiElement root : findDartRoots(psiFile)) { if (!DartPsiCompositeElementImpl.processDeclarationsImpl(root, processor, ResolveState.initial(), null)) { return false; } } } for (String partUrl : DartPartUriIndex.getPartUris(context.getProject(), virtualFile)) { final VirtualFile partFile = getImportedFile(context.getProject(), virtualFile, partUrl); if (partFile == null || alreadyProcessed.contains(partFile) || (filesOfInterest != null && !filesOfInterest.contains(partFile))) { continue; } final PsiFile partPsiFile = context.getManager().findFile(partFile); if (partPsiFile != null) { if (!processTopLevelDeclarationsImpl(partPsiFile, processor, partFile, filesOfInterest, alreadyProcessed, privateOnly)) { return false; } } } if (privateOnly) { return true; } final List<VirtualFile> libraryFiles = findLibrary(context.getContainingFile()); final boolean processingLibraryWhereContextElementLocated = libraryFiles.contains(virtualFile); boolean coreImportedExplicitly = false; for (DartImportOrExportInfo importOrExportInfo : DartImportAndExportIndex.getImportAndExportInfos(context.getProject(), virtualFile)) { if (processingLibraryWhereContextElementLocated && importOrExportInfo.getKind() == Kind.Export) continue; if (!processingLibraryWhereContextElementLocated && importOrExportInfo.getKind() == Kind.Import) continue; if (importOrExportInfo.getKind() == Kind.Import && DART_CORE_URI.equals(importOrExportInfo.getUri())) { coreImportedExplicitly = true; } // if statement has prefix all components are prefix.Name if (importOrExportInfo.getKind() == Kind.Import && importOrExportInfo.getImportPrefix() != null) continue; final VirtualFile importedFile = getImportedFile(context.getProject(), virtualFile, importOrExportInfo.getUri()); if (importedFile != null) { processor.importedFileProcessingStarted(importedFile, importOrExportInfo); final boolean continueProcessing = processTopLevelDeclarationsImpl(context, processor, importedFile, filesOfInterest, alreadyProcessed, false); processor.importedFileProcessingFinished(importedFile); if (!continueProcessing) { return false; } } } if (!coreImportedExplicitly && processingLibraryWhereContextElementLocated) { final VirtualFile dartCoreLib = DartLibraryIndex.getSdkLibByUri(context.getProject(), DART_CORE_URI); if (dartCoreLib != null) { final DartImportOrExportInfo implicitImportInfo = new DartImportOrExportInfo(Kind.Import, DART_CORE_URI, null, Collections.emptySet(), Collections.emptySet()); processor.importedFileProcessingStarted(dartCoreLib, implicitImportInfo); final boolean continueProcessing = processTopLevelDeclarationsImpl(context, processor, dartCoreLib, filesOfInterest, alreadyProcessed, false); processor.importedFileProcessingFinished(dartCoreLib); if (!continueProcessing) { return false; } } } return true; } @Nullable public static VirtualFile getImportedFile(final @NotNull Project project, final @NotNull VirtualFile contextFile, final @NotNull String importText) { if (importText.startsWith(DartUrlResolver.DART_PREFIX) || importText.startsWith(DartUrlResolver.PACKAGE_PREFIX) || importText.startsWith(DartUrlResolver.FILE_PREFIX)) { return DartUrlResolver.getInstance(project, contextFile).findFileByDartUrl(importText); } final VirtualFile parent = contextFile.getParent(); return parent == null ? null : VfsUtilCore.findRelativeFile(importText, parent); } @Nullable public static VirtualFile getRealVirtualFile(PsiFile psiFile) { return psiFile != null ? psiFile.getOriginalFile().getVirtualFile() : null; } public static boolean sameLibrary(@NotNull PsiElement context1, @NotNull PsiElement context2) { final List<VirtualFile> librariesForContext1 = findLibrary(context1.getContainingFile()); if (librariesForContext1.isEmpty()) return false; final List<VirtualFile> librariesForContext2 = findLibrary(context2.getContainingFile()); if (librariesForContext2.isEmpty()) return false; final THashSet<VirtualFile> librariesSetForContext1 = new THashSet<>(librariesForContext1); return ContainerUtil.find(librariesForContext2, librariesSetForContext1::contains) != null; } @NotNull public static List<VirtualFile> findLibrary(@NotNull final PsiFile context) { final VirtualFile contextVirtualFile = getRealVirtualFile(context); if (contextVirtualFile == null) return Collections.emptyList(); return CachedValuesManager.getCachedValue(context, () -> { for (PsiElement root : findDartRoots(context)) { final DartPartOfStatement partOfStatement = PsiTreeUtil.getChildOfType(root, DartPartOfStatement.class); if (partOfStatement != null) { final String libraryName = partOfStatement.getLibraryName(); final List<VirtualFile> files = findLibraryByName(context, libraryName); if (!files.isEmpty()) { return new CachedValueProvider.Result<>(files, PsiModificationTracker.MODIFICATION_COUNT); } } } // no 'part of' statement in file -> this file itself is a library return new CachedValueProvider.Result<>(Collections.singletonList(contextVirtualFile), PsiModificationTracker.MODIFICATION_COUNT); }); } @NotNull public static List<VirtualFile> findLibraryByName(@NotNull final PsiElement context, @NotNull final String libraryName) { return ContainerUtil.filter(DartLibraryIndex.getFilesByLibName(context.getResolveScope(), libraryName), mainLibFile -> { for (String partUrl : DartPartUriIndex.getPartUris(context.getProject(), mainLibFile)) { final VirtualFile partFile = getImportedFile(context.getProject(), mainLibFile, partUrl); if (Comparing.equal(getRealVirtualFile(context.getContainingFile()), partFile)) return true; } return false; }); } public static boolean isLibraryRoot(PsiFile psiFile) { for (PsiElement root : findDartRoots(psiFile)) { if (PsiTreeUtil.getChildOfType(root, DartPartOfStatement.class) != null) return false; } return true; } // todo this method must look for main function in library parts as well @Nullable public static DartFunctionDeclarationWithBodyOrNative getMainFunction(final @Nullable PsiFile file) { if (!(file instanceof DartFile)) return null; final ArrayList<DartComponentName> result = new ArrayList<>(); DartPsiCompositeElementImpl.processDeclarationsImpl(file, new DartResolveProcessor(result, "main"), ResolveState.initial(), null); for (DartComponentName componentName : result) { final PsiElement parent = componentName.getParent(); if (parent instanceof DartFunctionDeclarationWithBodyOrNative) { return (DartFunctionDeclarationWithBodyOrNative)parent; } } return null; } @Nullable public static DartReference getLeftReference(@Nullable final PsiElement node) { if (node == null) return null; for (PsiElement sibling = UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpacesAndComments(node, true); sibling != null; sibling = UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpacesAndComments(sibling, true)) { String siblingText = sibling.getText(); // String.equals() is fast so use it instead of trying to optimize this. if (".".equals(siblingText)) continue; if ("..".equals(siblingText)) continue; if ("?.".equals(siblingText)) continue; PsiElement candidate = sibling; if (candidate instanceof DartType) { candidate = ((DartType)sibling).getReferenceExpression(); } return candidate instanceof DartReference && candidate != node ? (DartReference)candidate : null; } DartReference reference = PsiTreeUtil.getParentOfType(node, DartReference.class, false); while (reference != null) { PsiElement parent = reference.getParent(); if (parent instanceof DartCascadeReferenceExpression) { parent = parent.getParent(); if (parent instanceof DartValueExpression) { final List<DartExpression> expressionList = ((DartValueExpression)parent).getExpressionList(); final DartExpression firstExpression = expressionList.isEmpty() ? null : expressionList.get(0); if (firstExpression instanceof DartReference) { return (DartReference)firstExpression; } } // Invalid tree shape return null; } else if (parent instanceof DartReference && parent.getFirstChild() == reference) { reference = (DartReference)parent; } else { break; } } return null; } @NotNull public static List<DartComponent> findNamedSubComponents(@NotNull DartClass... rootDartClasses) { return findNamedSubComponents(true, rootDartClasses); } @NotNull public static List<DartComponent> findNamedSubComponents(boolean unique, @NotNull DartClass... rootDartClasses) { final List<DartComponent> unfilteredResult = findSubComponents(dartClass -> { final List<DartComponent> result = new ArrayList<>(); for (DartComponent namedComponent : getNamedSubComponents(dartClass)) { if (namedComponent.getName() != null) { result.add(namedComponent); } } return result; }, rootDartClasses); if (!unique) { return unfilteredResult; } return new ArrayList<>(namedComponentToMap(unfilteredResult).values()); } public static List<DartMethodDeclaration> findOperators(AbstractDartPsiClass dartPsiClass) { return findSubComponents(dartClass -> { List<DartMethodDeclaration> operators = Lists.newArrayList(); final DartMethodDeclaration[] methods = PsiTreeUtil.getChildrenOfType(getBody(dartClass), DartMethodDeclaration.class); if (methods != null) { for (DartMethodDeclaration method : methods) { if (method.isOperator()) { operators.add(method); } } } return operators; }, dartPsiClass); } @NotNull public static <T> List<T> findSubComponents(final Function<DartClass, List<T>> fun, @NotNull DartClass... rootDartClasses) { final List<T> unfilteredResult = new ArrayList<>(); processSuperClasses(dartClass -> { unfilteredResult.addAll(fun.fun(dartClass)); return true; }, rootDartClasses); return unfilteredResult; } public static boolean processSuperClasses(PsiElementProcessor<DartClass> processor, @NotNull DartClass... rootDartClasses) { final Set<DartClass> processedClasses = new THashSet<>(); final LinkedList<DartClass> classes = new LinkedList<>(); classes.addAll(Arrays.asList(rootDartClasses)); while (!classes.isEmpty()) { final DartClass dartClass = classes.pollFirst(); if (dartClass == null || processedClasses.contains(dartClass)) { continue; } if (!processor.execute(dartClass)) { return false; } ContainerUtil.addIfNotNull(classes, dartClass.getSuperClassResolvedOrObjectClass().getDartClass()); for (DartType type : getImplementsAndMixinsList(dartClass)) { ContainerUtil.addIfNotNull(classes, resolveClassByType(type).getDartClass()); } processedClasses.add(dartClass); } return true; } public static void collectSupers(@NotNull final List<DartClass> superClasses, @NotNull final List<DartClass> superInterfaces, @Nullable DartClass rootDartClass) { processSupers(dartClass -> { superClasses.add(dartClass); return true; }, dartClass -> { superInterfaces.add(dartClass); return true; }, rootDartClass); } public static void processSupers(@Nullable PsiElementProcessor<DartClass> superClassProcessor, @Nullable PsiElementProcessor<DartClass> superInterfaceProcessor, @Nullable DartClass rootDartClass) { final Set<DartClass> processedClasses = new THashSet<>(); DartClass currentClass = rootDartClass; while (currentClass != null) { processedClasses.add(currentClass); // implements for (DartType type : currentClass.getImplementsList()) { final DartClass result = resolveClassByType(type).getDartClass(); if (superInterfaceProcessor == null || result == null || processedClasses.contains(result)) { continue; } if (!superInterfaceProcessor.execute(result)) { return; } if (!processSuperClasses(superInterfaceProcessor, result)) { return; } } // mixins for (DartType type : currentClass.getMixinsList()) { final DartClass result = resolveClassByType(type).getDartClass(); if (superClassProcessor == null || result == null || processedClasses.contains(result)) { continue; } if (!superClassProcessor.execute(result)) { return; } } currentClass = currentClass.getSuperClassResolvedOrObjectClass().getDartClass(); if (currentClass == null || processedClasses.contains(currentClass)) { break; } if (superClassProcessor != null) { if (!superClassProcessor.execute(currentClass)) { return; } } } } public static Map<Pair<String, Boolean>, DartComponent> namedComponentToMap(List<DartComponent> unfilteredResult) { final Map<Pair<String, Boolean>, DartComponent> result = new HashMap<>(); for (DartComponent dartComponent : unfilteredResult) { // need order Pair<String, Boolean> key = Pair.create(dartComponent.getName(), dartComponent.isGetter()); if (result.containsKey(key)) continue; result.put(key, dartComponent); } return result; } public static List<DartComponentName> getComponentNames(List<? extends DartComponent> fields) { return ContainerUtil .filter(ContainerUtil.map(fields, (Function<DartComponent, DartComponentName>)DartComponent::getComponentName), Condition.NOT_NULL); } public static DartComponentName[] getComponentNameArray(List<? extends DartComponent> components) { final List<DartComponentName> names = getComponentNames(components); return names.toArray(new DartComponentName[names.size()]); } @NotNull public static List<DartComponent> getNamedSubComponents(DartClass dartClass) { if (dartClass.isEnum()) { final List<DartEnumConstantDeclaration> enumConstants = dartClass.getEnumConstantDeclarationList(); final List<DartComponent> result = new ArrayList<>(enumConstants.size()); result.addAll(enumConstants); return result; } PsiElement body = getBody(dartClass); final List<DartComponent> result = new ArrayList<>(); if (body == null) { return result; } final DartComponent[] namedComponents = PsiTreeUtil.getChildrenOfType(body, DartComponent.class); final DartVarDeclarationList[] variables = PsiTreeUtil.getChildrenOfType(body, DartVarDeclarationList.class); if (namedComponents != null) { ContainerUtil.addAll(result, namedComponents); } if (variables == null) { return result; } for (DartVarDeclarationList varDeclarationList : variables) { result.add(varDeclarationList.getVarAccessDeclaration()); result.addAll(varDeclarationList.getVarDeclarationListPartList()); } return result; } @Nullable public static DartClassMembers getBody(@Nullable final DartClass dartClass) { final DartClassBody body = dartClass instanceof DartClassDefinition ? ((DartClassDefinition)dartClass).getClassBody() : null; return body == null ? null : body.getClassMembers(); } public static List<DartComponent> filterComponentsByType(List<DartComponent> components, final DartComponentType type) { return ContainerUtil.filter(components, component -> type == DartComponentType.typeOf(component)); } public static List<DartType> getTypes(@Nullable DartTypeList typeList) { if (typeList == null) { return Collections.emptyList(); } return typeList.getTypeList(); } @NotNull public static DartClassResolveResult resolveClassByType(@Nullable DartType dartType) { return resolveClassByType(dartType, DartClassResolveResult.EMPTY); } private static DartClassResolveResult resolveClassByType(DartType dartType, DartClassResolveResult initializer) { if (dartType == null) { return DartClassResolveResult.EMPTY; } final PsiElement target = dartType.resolveReference(); if (target instanceof DartComponentName) { final PsiElement targetParent = target.getParent(); if (targetParent == initializer.getDartClass()) { return initializer; } if (targetParent instanceof DartClass) { return DartClassResolveResult.create((DartClass)targetParent); } // todo: fix // prefix.ClassName or ClassName.name ? if (DartComponentType.typeOf(targetParent) == DartComponentType.CONSTRUCTOR) { return DartClassResolveResult.create(PsiTreeUtil.getParentOfType(target, DartClass.class)); } } return DartClassResolveResult.EMPTY; } @NotNull public static DartClassResolveResult getDartClassResolveResult(@Nullable PsiElement element) { return getDartClassResolveResult(element, new DartGenericSpecialization()); } @NotNull public static DartClassResolveResult getDartClassResolveResult(@Nullable PsiElement element, @NotNull DartGenericSpecialization specialization) { if (element == null) { return DartClassResolveResult.create(null); } final PsiElement parentElement = element.getParent(); if (parentElement instanceof DartEnumConstantDeclaration) { return getDartClassResolveResult(parentElement.getParent()); } if (element instanceof DartComponentName) { return getDartClassResolveResult(parentElement, specialization); } if (element instanceof DartClass) { final DartClass dartClass = (DartClass)element; return DartClassResolveResult.create(dartClass, specialization); } DartClassResolveResult result = tryFindTypeAndResolveClass(element, specialization); if (result.getDartClass() != null) { return result; } PsiElement functionBody = PsiTreeUtil.getChildOfType(element, DartFunctionBody.class); if (functionBody == null) { functionBody = PsiTreeUtil.getChildOfType(element, DartFunctionExpressionBody.class); } DartReference functionBodyExpression = PsiTreeUtil.getChildOfType(functionBody, DartReference.class); if (functionBodyExpression != null) { return functionBodyExpression.resolveDartClass(); } if (specialization.containsKey(null, element.getText())) { return specialization.get(null, element.getText()); } if (element instanceof DartVarAccessDeclaration && parentElement instanceof DartForInPart) { final DartForInPart forInPart = (DartForInPart)parentElement; return resolveForInPartClass(forInPart); } if (element instanceof DartForInPart) { final DartForInPart forInPart = (DartForInPart)element; return resolveForInPartClass(forInPart); } if (element instanceof DartSimpleFormalParameter && parentElement instanceof DartNormalFormalParameter && parentElement.getParent() instanceof DartFormalParameterList && parentElement.getParent().getParent() instanceof DartFunctionExpression && parentElement.getParent().getParent().getParent() instanceof DartArgumentList) { final int parameterIndex = getParameterIndex(parentElement, ((DartFormalParameterList)parentElement.getParent())); final int argumentIndex = getArgumentIndex(parentElement.getParent().getParent()); final DartCallExpression callExpression = PsiTreeUtil.getParentOfType(element, DartCallExpression.class); final DartReference callReference = callExpression == null ? null : (DartReference)callExpression.getExpression(); final PsiElement target = callReference == null ? null : callReference.resolve(); final PsiElement argument = target == null ? null : findParameter(target.getParent(), argumentIndex); if (argument instanceof DartNormalFormalParameter) { final DartType dartType = findParameterType(((DartNormalFormalParameter)argument).getFunctionFormalParameter(), parameterIndex); final DartClassResolveResult callClassResolveResult = getLeftClassResolveResult(callReference); return getDartClassResolveResult(dartType, callClassResolveResult.getSpecialization()); } return DartClassResolveResult.EMPTY; } final DartVarInit varInit = PsiTreeUtil.getChildOfType(element instanceof DartVarDeclarationListPart ? element : parentElement, DartVarInit.class); final DartExpression initExpression = varInit == null ? null : varInit.getExpression(); if (initExpression instanceof DartReference) { result = ((DartReference)initExpression).resolveDartClass(); result.specialize(initExpression); return result; } return getDartClassResolveResult(initExpression); } private static DartClassResolveResult getLeftClassResolveResult(DartReference reference) { final DartReference[] references = PsiTreeUtil.getChildrenOfType(reference, DartReference.class); if (references != null && references.length == 2) { return references[0].resolveDartClass(); } return DartClassResolveResult.create(PsiTreeUtil.getChildOfType(reference, DartClass.class)); } /** * Returns the constructor invoked by the given {@link DartNewExpression}. * <p/> * TODO(scheglov) Add non-named constructor declarations (they're methods now). */ @Nullable public static DartComponent findConstructorDeclaration(DartNewExpression newExpression) { DartType type = newExpression.getType(); PsiElement psiElement = type != null ? type.getReferenceExpression() : null; PsiElement target = psiElement != null ? ((DartReference)psiElement).resolve() : null; return target != null ? (DartComponent)target.getParent() : null; } @Nullable private static PsiElement findParameter(@Nullable PsiElement element, int index) { final DartFormalParameterList parameterList = PsiTreeUtil.getChildOfType(element, DartFormalParameterList.class); if (parameterList == null) { return null; } final int normalParameterSize = parameterList.getNormalFormalParameterList().size(); if (index < normalParameterSize) { return parameterList.getNormalFormalParameterList().get(index); } final DartOptionalFormalParameters optionalFormalParameters = parameterList.getOptionalFormalParameters(); return optionalFormalParameters == null ? null : optionalFormalParameters.getDefaultFormalNamedParameterList().get(index - normalParameterSize); } @Nullable private static DartType findParameterType(@Nullable PsiElement element, int index) { final PsiElement target = findParameter(element, index); return findType(target); } private static int getParameterIndex(@NotNull PsiElement element, @NotNull DartFormalParameterList parameterList) { int normalIndex = parameterList.getNormalFormalParameterList().indexOf(element); final DartOptionalFormalParameters formalParameters = parameterList.getOptionalFormalParameters(); int namedIndex = formalParameters == null ? -1 : formalParameters.getDefaultFormalNamedParameterList().indexOf(element); return normalIndex >= 0 ? normalIndex : namedIndex >= 0 ? namedIndex + parameterList.getNormalFormalParameterList().size() : -1; } private static DartClassResolveResult resolveForInPartClass(DartForInPart forInPart) { final DartExpression expression = forInPart.getExpression(); final DartReference dartReference = expression instanceof DartReference ? (DartReference)expression : null; final DartClassResolveResult classResolveResult = dartReference == null ? DartClassResolveResult.EMPTY : dartReference.resolveDartClass(); final DartClass dartClass = classResolveResult.getDartClass(); final DartClassResolveResult iteratorResult = dartClass == null ? DartClassResolveResult.EMPTY : getDartClassResolveResult(dartClass.findMemberByName("iterator"), classResolveResult.getSpecialization()); final DartClassResolveResult finalResult = iteratorResult.getSpecialization().get(null, "E"); return finalResult == null ? DartClassResolveResult.EMPTY : finalResult; } public static int getArgumentIndex(PsiElement place) { int parameterIndex = -1; final DartArgumentList argumentList = PsiTreeUtil.getParentOfType(place, DartArgumentList.class, false); if (place == argumentList) { assert argumentList != null; final DartFunctionDescription functionDescription = DartFunctionDescription.tryGetDescription((DartCallExpression)argumentList.getParent()); // the last one parameterIndex = functionDescription == null ? -1 : functionDescription.getParameters().length - 1; } else if (argumentList != null) { for (DartExpression expression : argumentList.getExpressionList()) { ++parameterIndex; if (expression.getTextRange().getEndOffset() >= place.getTextRange().getStartOffset()) { break; } } } else if (UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpacesAndComments(place, true) instanceof DartArgumentList) { // seems foo(param1, param2<caret>) final DartArgumentList prevSibling = (DartArgumentList)UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpacesAndComments(place, true); assert prevSibling != null; // callExpression -> arguments -> argumentList parameterIndex = prevSibling.getExpressionList().size() + prevSibling.getNamedArgumentList().size(); } else if (DartParameterInfoHandler.findElementForParameterInfo(place) != null) { // foo(<caret>), new Foo(<caret>) or @Foo(<caret>) parameterIndex = 0; } return parameterIndex; } @NotNull private static DartClassResolveResult tryFindTypeAndResolveClass(@Nullable PsiElement element, DartGenericSpecialization specialization) { DartType type = PsiTreeUtil.getChildOfType(element, DartType.class); if (type == null && element instanceof DartType) { type = (DartType)element; } else if (type == null) { final DartReturnType returnType = PsiTreeUtil.getChildOfType(element, DartReturnType.class); type = returnType == null ? null : returnType.getType(); } if (type == null && element instanceof DartVarDeclarationListPart) { final PsiElement parent = element.getParent(); if (parent instanceof DartVarDeclarationList) { type = ((DartVarDeclarationList)parent).getVarAccessDeclaration().getType(); } } DartClass dartClass = type == null ? null : resolveClassByType(type).getDartClass(); if (dartClass == null && type != null && specialization.containsKey(element, type.getText())) { return specialization.get(element, type.getText()); } DartClassResolveResult result = getDartClassResolveResult(dartClass, specialization.getInnerSpecialization(element)); if (result.getDartClass() != null) { result.specializeByParameters(type == null ? null : type.getTypeArguments()); return result; } return DartClassResolveResult.EMPTY; } @NotNull public static String getOperatorString(@Nullable PsiElement element) { if (element == null) { return ""; } final StringBuilder result = new StringBuilder(); element.accept(new PsiRecursiveElementVisitor() { @Override public void visitElement(PsiElement element) { if (element instanceof LeafPsiElement && DartTokenTypesSets.OPERATORS.contains(((LeafPsiElement)element).getElementType())) { result.append(element.getText()); } super.visitElement(element); } }); return result.toString(); } @Nullable public static DartComponent findReferenceAndComponentTarget(@Nullable PsiElement element) { DartReference reference = PsiTreeUtil.getNonStrictParentOfType(element, DartReference.class); PsiElement target = reference == null ? null : reference.resolve(); PsiElement targetParent = target != null ? target.getParent() : null; if (targetParent instanceof DartComponent) { return (DartComponent)targetParent; } return null; } public static boolean aloneOrFirstInChain(DartReference reference) { return PsiTreeUtil.getChildrenOfType(reference, DartReference.class) == null && getLeftReference(reference) == null && getLeftReference(reference.getParent()) == null; } @NotNull public static ResolveResult[] toCandidateInfoArray(@Nullable List<? extends PsiElement> elements) { if (elements == null) { return ResolveResult.EMPTY_ARRAY; } elements = ContainerUtil.filter(elements, (Condition<PsiElement>)Objects::nonNull); final ResolveResult[] result = new ResolveResult[elements.size()]; for (int i = 0, size = elements.size(); i < size; i++) { result[i] = new PsiElementResolveResult(elements.get(i)); } return result; } public static List<DartType> getImplementsAndMixinsList(DartClass dartClass) { return ContainerUtil.concat(dartClass.getImplementsList(), dartClass.getMixinsList()); } }