blob: eafd20759490c64d0807c89b6c99517b6965dcb3 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* 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 com.jetbrains.python.refactoring;
import com.google.common.collect.Sets;
import com.intellij.codeInsight.controlflow.ControlFlow;
import com.intellij.codeInsight.controlflow.ControlFlowUtil;
import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.PsiElement;
import com.intellij.util.Function;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyAugAssignmentStatementNavigator;
import com.intellij.psi.util.QualifiedName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dennis.Ushakov
*/
public class PyDefUseUtil {
private PyDefUseUtil() {
}
@NotNull
public static List<ReadWriteInstruction> getLatestDefs(ScopeOwner block, String varName, PsiElement anchor, boolean acceptTypeAssertions) {
final ControlFlow controlFlow = ControlFlowCache.getControlFlow(block);
final Instruction[] instructions = controlFlow.getInstructions();
final PyAugAssignmentStatement augAssignment = PyAugAssignmentStatementNavigator.getStatementByTarget(anchor);
if (augAssignment != null) {
anchor = augAssignment;
}
int instr = ControlFlowUtil.findInstructionNumberByElement(instructions, anchor);
if (instr < 0) {
return Collections.emptyList();
}
if (anchor instanceof PyTargetExpression) {
Collection<Instruction> pred = instructions[instr].allPred();
if (!pred.isEmpty()) {
instr = pred.iterator().next().num();
}
}
final Collection<ReadWriteInstruction> result = getLatestDefs(varName, instructions, instr, acceptTypeAssertions);
return new ArrayList<ReadWriteInstruction>(result);
}
private static Collection<ReadWriteInstruction> getLatestDefs(final String varName, final Instruction[] instructions, final int instr,
final boolean acceptTypeAssertions) {
final Collection<ReadWriteInstruction> result = new LinkedHashSet<ReadWriteInstruction>();
ControlFlowUtil.iteratePrev(instr, instructions,
new Function<Instruction, ControlFlowUtil.Operation>() {
@Override
public ControlFlowUtil.Operation fun(Instruction instruction) {
if (instruction instanceof ReadWriteInstruction) {
final ReadWriteInstruction rwInstruction = (ReadWriteInstruction)instruction;
final PsiElement element = instruction.getElement();
final String name = elementName(element);
final ReadWriteInstruction.ACCESS access = rwInstruction.getAccess();
if ((access.isWriteAccess() || (acceptTypeAssertions && access.isAssertTypeAccess())) &&
Comparing.strEqual(name, varName)) {
result.add(rwInstruction);
return ControlFlowUtil.Operation.CONTINUE;
}
}
return ControlFlowUtil.Operation.NEXT;
}
});
return result;
}
@Nullable
private static String elementName(PsiElement element) {
if (element instanceof PyImportElement) {
return ((PyImportElement) element).getVisibleName();
}
if (element instanceof PyReferenceExpression) {
final QualifiedName qname = ((PyReferenceExpression)element).asQualifiedName();
if (qname != null) {
return qname.toString();
}
}
return element instanceof PyElement ? ((PyElement)element).getName() : null;
}
@NotNull
public static PsiElement[] getPostRefs(ScopeOwner block, PyTargetExpression var, PyExpression anchor) {
final ControlFlow controlFlow = ControlFlowCache.getControlFlow(block);
final Instruction[] instructions = controlFlow.getInstructions();
final int instr = ControlFlowUtil.findInstructionNumberByElement(instructions, anchor);
if (instr < 0) {
return new PyElement[0];
}
final boolean[] visited = new boolean[instructions.length];
final Collection<PyElement> result = Sets.newHashSet();
for (Instruction instruction : instructions[instr].allSucc()) {
getPostRefs(var, instructions, instruction.num(), visited, result);
}
return result.toArray(new PyElement[result.size()]);
}
private static void getPostRefs(PyTargetExpression var,
Instruction[] instructions,
int instr,
boolean[] visited,
Collection<PyElement> result) {
// TODO: Use ControlFlowUtil.process() for forwards CFG traversal
if (visited[instr]) return;
visited[instr] = true;
if (instructions[instr] instanceof ReadWriteInstruction) {
final ReadWriteInstruction instruction = (ReadWriteInstruction)instructions[instr];
final PsiElement element = instruction.getElement();
String name = elementName(element);
if (Comparing.strEqual(name, var.getName())) {
final ReadWriteInstruction.ACCESS access = instruction.getAccess();
if (access.isWriteAccess()) {
return;
}
result.add((PyElement)instruction.getElement());
}
}
for (Instruction instruction : instructions[instr].allSucc()) {
getPostRefs(var, instructions, instruction.num(), visited, result);
}
}
public static class InstructionNotFoundException extends RuntimeException {
}
}