/* * Copyright 2012-2014 Sergey Ignatov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.intellij.erlang.psi.impl; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.BaseScopeProcessor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import org.intellij.erlang.psi.*; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Map; import static org.intellij.erlang.psi.impl.ErlangPsiImplUtil.*; public class ErlangVarProcessor extends BaseScopeProcessor { public static final Key<Map<String, ErlangQVar>> ERLANG_VARIABLE_CONTEXT = Key.create("ERLANG_VARIABLE_CONTEXT"); private List<ErlangQVar> myVarList = ContainerUtil.newArrayListWithCapacity(0); private final String myRequestedName; private final PsiElement myOrigin; public ErlangVarProcessor(String requestedName, PsiElement origin) { myRequestedName = requestedName; myOrigin = origin; } @Override public boolean execute(@NotNull PsiElement psiElement, @NotNull ResolveState resolveState) { Map<String, ErlangQVar> variableContext = psiElement.getContainingFile().getOriginalFile().getUserData(ERLANG_VARIABLE_CONTEXT); if (variableContext != null) { ContainerUtil.addIfNotNull(variableContext.get(myRequestedName), myVarList); return true; } if (!(psiElement instanceof ErlangQVar)) return true; if (!psiElement.getText().equals(myRequestedName)) return true; if (psiElement.equals(myOrigin)) return true; if (inFunArgList(myOrigin)) return true; ErlangFunctionClause functionClause = PsiTreeUtil.getTopmostParentOfType(myOrigin, ErlangFunctionClause.class); ErlangSpecification spec = PsiTreeUtil.getTopmostParentOfType(myOrigin, ErlangSpecification.class); ErlangMacrosDefinition macroDefinition = PsiTreeUtil.getTopmostParentOfType(myOrigin, ErlangMacrosDefinition.class); boolean inSpecification = PsiTreeUtil.isAncestor(spec, psiElement, false); boolean inDefinition = inArgumentDefinition(psiElement); boolean inFunctionClause = PsiTreeUtil.isAncestor(functionClause, psiElement, false); boolean inAssignment = inLeftPartOfAssignment(psiElement); boolean inDefinitionOrAssignment = inDefinition || inAssignment; boolean inFunction = inFunctionClause && inDefinitionOrAssignment; boolean inMacroDefinition = PsiTreeUtil.isAncestor(macroDefinition, psiElement, false) && inDefinitionOrAssignment; if (inFunction || inModule(psiElement) || inSpecification || inMacroDefinition) { boolean inArgumentList = inArgumentList(psiElement); //noinspection unchecked boolean inArgumentListBeforeAssignment = PsiTreeUtil.getParentOfType(psiElement, ErlangArgumentList.class, ErlangAssignmentExpression.class) instanceof ErlangArgumentList; if (inArgumentList && inArgumentListBeforeAssignment && !inDefinitionBeforeArgumentList(psiElement)) return true; if (inDifferentCrClauses(psiElement)) return true; if (hasNarrowerParentScope(psiElement)) return true; // put all possible variables to list boolean inArgDefList = inFunArgList(psiElement); return !myVarList.add((ErlangQVar) psiElement) && !inArgDefList; } return true; } private static boolean inFunArgList(PsiElement psiElement) { ErlangArgumentDefinitionList list = PsiTreeUtil.getParentOfType(psiElement, ErlangArgumentDefinitionList.class); return list != null && list.getParent() instanceof ErlangFunClause; } private boolean hasNarrowerParentScope(PsiElement psiElement) { @SuppressWarnings("unchecked") ErlangCompositeElement narrowestParentScopeOwner = PsiTreeUtil.getParentOfType(psiElement, ErlangCrClause.class, ErlangFunClause.class, ErlangFunctionClause.class, ErlangListComprehension.class); if (narrowestParentScopeOwner instanceof ErlangFunClause) { ErlangArgumentDefinition funName = PsiTreeUtil.getParentOfType(psiElement, ErlangArgumentDefinition.class); if (((ErlangFunClause) narrowestParentScopeOwner).getArgumentDefinition() == funName) { return false; } } if (narrowestParentScopeOwner instanceof ErlangCrClause) { ErlangCaseExpression caseExpr = PsiTreeUtil.getParentOfType(psiElement, ErlangCaseExpression.class); if (Comparing.equal( PsiTreeUtil.getParentOfType(caseExpr, ErlangClauseBody.class), PsiTreeUtil.getParentOfType(myOrigin, ErlangClauseBody.class))) return false; } return narrowestParentScopeOwner != null && !PsiTreeUtil.isAncestor(narrowestParentScopeOwner, myOrigin, false); } private boolean inDifferentCrClauses(PsiElement psiElement) { ErlangCrClause crClause = PsiTreeUtil.getParentOfType(psiElement, ErlangCrClause.class); ErlangCrClause crClauseOrigin = PsiTreeUtil.getParentOfType(myOrigin, ErlangCrClause.class); if (crClause == null || crClauseOrigin == null) return false; if (crClause.getParent() != crClauseOrigin.getParent()) return false; ErlangCaseExpression caseExpression = PsiTreeUtil.getParentOfType(psiElement, ErlangCaseExpression.class); ErlangCaseExpression caseExpressionOrigin = PsiTreeUtil.getParentOfType(myOrigin, ErlangCaseExpression.class); if (caseExpressionOrigin != null && caseExpression == caseExpressionOrigin && crClause != crClauseOrigin) return true; return false; } @NotNull public List<ErlangQVar> getAllResults() { return myVarList; } }