Initial PyReachingDefs implementation

This commit is contained in:
Oleg Shpynov
2010-01-25 16:05:26 +03:00
parent c9dbb1c8be
commit 21796073e8
15 changed files with 439 additions and 16 deletions
@@ -313,11 +313,11 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
final ArrayList<Instruction> rescueInstructions = new ArrayList<Instruction>();
final ArrayList<Instruction> exceptInstructions = new ArrayList<Instruction>();
for (PyExceptPart exceptPart : node.getExceptParts()) {
myBuilder.prevInstruction = lastBlockInstruction;
final Instruction rescueInstruction = myBuilder.startNode(exceptPart);
rescueInstructions.add(rescueInstruction);
final Instruction exceptInstruction = myBuilder.startNode(exceptPart);
exceptInstructions.add(exceptInstruction);
exceptPart.accept(this);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
@@ -340,7 +340,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
// handle raise instructions inside compound statement
if (pendingElement instanceof PyRaiseStatement && PsiTreeUtil.isAncestor(tryPart, pendingElement, false)) {
for (Instruction rescueInstruction : rescueInstructions) {
for (Instruction rescueInstruction : exceptInstructions) {
myBuilder.addEdge(instruction, rescueInstruction);
}
return;
@@ -367,6 +367,23 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
@Override
public void visitPyListCompExpression(final PyListCompExpression node) {
super.visitPyListCompExpression(node);
myBuilder.startNode(node);
for (ComprhIfComponent component : node.getIfComponents()) {
final PyExpression condition = component.getTest();
condition.accept(this);
final Instruction head = myBuilder.prevInstruction;
final Instruction prevInstruction = myBuilder.startConditionalNode(condition, condition, true);
// restore head
myBuilder.prevInstruction = head;
myBuilder.addPendingEdge(node, myBuilder.startConditionalNode(condition, condition, false)); // false condition
myBuilder.prevInstruction = prevInstruction;
}
for (ComprhForComponent forComponent : node.getForComponents()) {
forComponent.getIteratedList().accept(this);
forComponent.getIteratorVariable().accept(this);
}
node.getResultExpression().accept(this);
}
}
@@ -0,0 +1,74 @@
package com.jetbrains.python.codeInsight.dataflow;
import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.codeInsight.dataflow.DFAMap;
import com.intellij.codeInsight.dataflow.DfaInstance;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.codeInsight.controlflow.WriteInstruction;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
/**
* @author oleg
*/
public class PyReachingDefsDfaInstance implements DfaInstance<ScopeVariable> {
// Use this its own map, because check in PyReachingDefsDfaSemilattice is important
public static final DFAMap<ScopeVariable> INITIAL_MAP = new DFAMap<ScopeVariable>();
public DFAMap<ScopeVariable> fun(final DFAMap<ScopeVariable> map, Instruction instruction) {
final PsiElement element = instruction.getElement();
//if (element == null || element.getUserData(ReferenceCompletionUtil.REFERENCE_BEING_COMPLETED)!=null){
// return map;
//}
// Scope reduction
final DFAMap<ScopeVariable> reducedMap = new DFAMap<ScopeVariable>();
for (Map.Entry<String, ScopeVariable> entry : map.entrySet()) {
final ScopeVariable value = entry.getValue();
if (element != null && PsiTreeUtil.isAncestor(value.getScope(), element, false)){
reducedMap.put(entry.getKey(), value);
}
}
return processReducedMap(reducedMap, instruction, element);
}
private static DFAMap<ScopeVariable> processReducedMap(final DFAMap<ScopeVariable> map,
final Instruction instruction,
final PsiElement element) {
if (instruction instanceof WriteInstruction) {
final WriteInstruction wInstruction = (WriteInstruction)instruction;
final String name = wInstruction.getName();
final ScopeVariable variable = map.get(name);
// If parameter
if (UsageAnalyzer.isParameter(element)) {
final PsiElement scope = ScopeUtil.getScopeElement(element);
final ScopeVariable scopeVariable = new ScopeVariableImpl(name, true, scope, element);
map.put(name, scopeVariable);
} else {
final ScopeVariableImpl scopeVariable;
final boolean isParameter = variable != null && variable.isParameter();
if (variable == null) {
final PsiElement scope = ScopeUtil.getScopeElement(element);
scopeVariable = new ScopeVariableImpl(name, isParameter, scope, element);
} else {
scopeVariable = new ScopeVariableImpl(name, isParameter, variable.getScope(),
variable.getDeclarations());
}
map.put(name, scopeVariable);
}
}
return map;
}
@NotNull
public DFAMap<ScopeVariable> initial() {
return INITIAL_MAP;
}
public boolean isForward() {
return true;
}
}
@@ -0,0 +1,79 @@
package com.jetbrains.python.codeInsight.dataflow;
import com.intellij.codeInsight.dataflow.DFAMap;
import com.intellij.codeInsight.dataflow.Semilattice;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Set;
/**
* @author oleg
*/
public class PyReachingDefsSemilattice implements Semilattice<ScopeVariable> {
public boolean eq(DFAMap<ScopeVariable> e1, DFAMap<ScopeVariable> e2) {
if (e1 == PyReachingDefsDfaInstance.INITIAL_MAP && e2 != PyReachingDefsDfaInstance.INITIAL_MAP ||
e2 == PyReachingDefsDfaInstance.INITIAL_MAP && e1 != PyReachingDefsDfaInstance.INITIAL_MAP) {
return false;
}
return e1.equals(e2);
}
public DFAMap<ScopeVariable> join(ArrayList<DFAMap<ScopeVariable>> ins) {
if (ins.isEmpty()) {
return DFAMap.empty();
}
if (ins.size() == 1) {
return ins.get(0);
}
final Set<String> resultNames = getResultNames(ins);
if (resultNames == null || resultNames.isEmpty()) {
return new DFAMap<ScopeVariable>();
}
final DFAMap<ScopeVariable> result = new DFAMap<ScopeVariable>();
for (String name : resultNames) {
boolean isParameter = true;
Set<PsiElement> declarations = new HashSet<PsiElement>();
PsiElement scope = null;
// iterating over all maps
for (DFAMap<ScopeVariable> map : ins) {
final ScopeVariable variable = map.get(name);
if (variable == null) {
continue;
}
final PsiElement valueScope = variable.getScope();
if (scope == null || scope == valueScope) {
scope = valueScope;
declarations.addAll(variable.getDeclarations());
} else
if (PsiTreeUtil.isAncestor(valueScope, scope, false)){
scope = valueScope;
declarations = new HashSet<PsiElement>(variable.getDeclarations());
}
}
final ScopeVariable scopeVariable = new ScopeVariableImpl(name, isParameter, scope, declarations);
result.put(name, scopeVariable);
}
return result;
}
@Nullable
private static Set<String> getResultNames(final ArrayList<DFAMap<ScopeVariable>> ins) {
// Compute intersection of all the names
Set<String> names2Include = null;
for (DFAMap<ScopeVariable> map : ins) {
if (map == PyReachingDefsDfaInstance.INITIAL_MAP) {
continue;
}
names2Include = map.intersectKeys(names2Include);
}
return names2Include;
}
}
@@ -0,0 +1,42 @@
package com.jetbrains.python.codeInsight.dataflow;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowOwner;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyExceptPartNavigator;
import com.jetbrains.python.psi.impl.PyForStatementNavigator;
import com.jetbrains.python.psi.impl.PyListCompExpressionNavigator;
/**
* @author oleg
*/
public class ScopeUtil {
public static PsiElement getScopeElement(final PsiElement element) {
if (element instanceof PyNamedParameter){
final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class, false);
if (function != null){
return function;
}
}
final PyExceptPart exceptPart = PyExceptPartNavigator.getPyExceptPartByTarget(element);
if (exceptPart != null){
return exceptPart;
}
final PyForStatement forStatement = PyForStatementNavigator.getPyForStatementByIterable(element);
if (forStatement != null){
return forStatement;
}
final PyListCompExpression listCompExpression = PyListCompExpressionNavigator.getPyListCompExpressionByVariable(element);
if (listCompExpression != null){
return listCompExpression;
}
final ControlFlowOwner owner = PsiTreeUtil.getParentOfType(element, ControlFlowOwner.class, false);
assert owner != null : "element should have not null controlflow owner";
return owner;
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2000-2008 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.codeInsight.dataflow;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* @author oleg
*/
public interface ScopeVariable {
@NotNull
String getName();
@NotNull
Collection<PsiElement> getDeclarations();
PsiElement getScope();
boolean isParameter();
}
@@ -0,0 +1,47 @@
package com.jetbrains.python.codeInsight.dataflow;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyElement;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
/**
* @author oleg
*/
public class ScopeVariableImpl implements ScopeVariable {
private String myName;
private final Collection<PsiElement> myDeclarations;
private PsiElement myScope;
private boolean isParameter;
public ScopeVariableImpl(final String name, final boolean parameter, final PsiElement scope, final Collection<PsiElement> declarations) {
myName = name;
myDeclarations = declarations;
myScope = scope;
isParameter = parameter;
}
public ScopeVariableImpl(final String name, final boolean parameter, final PsiElement scope, PsiElement declaration) {
this(name, parameter, scope, Collections.singletonList(declaration));
}
@NotNull
public String getName() {
return myName;
}
@NotNull
public Collection<PsiElement> getDeclarations() {
return myDeclarations;
}
public PsiElement getScope() {
return myScope;
}
public boolean isParameter() {
return isParameter;
}
}
@@ -0,0 +1,34 @@
package com.jetbrains.python.codeInsight.dataflow;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyParameter;
import com.jetbrains.python.psi.impl.PyExceptPartNavigator;
import com.jetbrains.python.psi.impl.PyForStatementNavigator;
import com.jetbrains.python.psi.impl.PyListCompExpressionNavigator;
/**
* @author oleg
*/
public class UsageAnalyzer {
public static boolean isParameter(final PsiElement element) {
// Except Block
if (PyExceptPartNavigator.getPyExceptPartByTarget(element) != null){
return true;
}
// For iterable statement
if (PyForStatementNavigator.getPyForStatementByIterable(element) != null){
return true;
}
// List comprehension expression
if (PyListCompExpressionNavigator.getPyListCompExpressionByVariable(element) != null){
return true;
}
if (element instanceof PyParameter) {
return true;
}
return false;
}
}
@@ -27,5 +27,4 @@ public interface PyListCompExpression extends PyExpression, NameDefiner {
PyExpression getResultExpression();
List<ComprhForComponent> getForComponents();
List<ComprhIfComponent> getIfComponents();
}
@@ -0,0 +1,25 @@
package com.jetbrains.python.psi.impl;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.psi.PyExceptPart;
import com.jetbrains.python.psi.PyExpression;
import org.jetbrains.annotations.Nullable;
/**
* @author oleg
*/
public class PyExceptPartNavigator {
@Nullable
public static PyExceptPart getPyExceptPartByTarget(final PsiElement element){
final PyExceptPart pyExceptPart = PsiTreeUtil.getParentOfType(element, PyExceptPart.class, false);
if (pyExceptPart == null){
return null;
}
final PyExpression expr = pyExceptPart.getTarget();
if (expr != null && PsiTreeUtil.isAncestor(expr, element, false)){
return pyExceptPart;
}
return null;
}
}
@@ -0,0 +1,25 @@
package com.jetbrains.python.psi.impl;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyForStatement;
import org.jetbrains.annotations.Nullable;
/**
* @author oleg
*/
public class PyForStatementNavigator {
@Nullable
public static PyForStatement getPyForStatementByIterable(final PsiElement element){
final PyForStatement forStatement = PsiTreeUtil.getParentOfType(element, PyForStatement.class, false);
if (forStatement == null){
return null;
}
final PyExpression target = forStatement.getForPart().getTarget();
if (target != null && PsiTreeUtil.isAncestor(target, element, false)){
return forStatement;
}
return null;
}
}
@@ -0,0 +1,28 @@
package com.jetbrains.python.psi.impl;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.psi.ComprhForComponent;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyListCompExpression;
import org.jetbrains.annotations.Nullable;
/**
* @author oleg
*/
public class PyListCompExpressionNavigator {
@Nullable
public static PyListCompExpression getPyListCompExpressionByVariable(final PsiElement element){
final PyListCompExpression listCompExpression = PsiTreeUtil.getParentOfType(element, PyListCompExpression.class, false);
if (listCompExpression == null){
return null;
}
for (ComprhForComponent component : listCompExpression.getForComponents()) {
final PyExpression variable = component.getIteratorVariable();
if (variable != null && PsiTreeUtil.isAncestor(variable, element, false)){
return listCompExpression;
}
}
return null;
}
}
@@ -4,12 +4,16 @@
3(4) WRITE ACCESS: collapse
4(5) element: PyExpressionStatement
5(6) element: PyAssignmentStatement
6(7) WRITE ACCESS: method
7(8) WRITE ACCESS: methodList
8(9) element: PyAssignmentStatement
9(10) WRITE ACCESS: s
10(11) WRITE ACCESS: s
11(12) WRITE ACCESS: processFunc
12(13) element: PyPrintStatement
13(14) WRITE ACCESS: method
14() element: null
6(7,8) element: PyListCompExpression
7(9) element: PyCallExpression: callable. Condition: callable(getattr(object, method)):true
8(10) element: PyCallExpression: callable. Condition: callable(getattr(object, method)):false
9(10) WRITE ACCESS: method
10(11) WRITE ACCESS: methodList
11(12) element: PyAssignmentStatement
12(13) WRITE ACCESS: s
13(14) WRITE ACCESS: s
14(15) WRITE ACCESS: processFunc
15(16) element: PyPrintStatement
16(17) element: PyListCompExpression
17(18) WRITE ACCESS: method
18() element: null
@@ -1 +1 @@
[k for k, v in params.items()]
[k for k, v in params.items() if foo]
@@ -0,0 +1,8 @@
0(1) element: null
1(2) element: PyExpressionStatement
2(3,4) element: PyListCompExpression
3(5) element: PyReferenceExpression: foo. Condition: foo:true
4(7) element: PyReferenceExpression: foo. Condition: foo:false
5(6) WRITE ACCESS: k
6(7) WRITE ACCESS: v
7() element: null
@@ -66,6 +66,10 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
doTest();
}
public void testListComp() throws Exception {
doTest();
}
public void testFunction() throws Exception {
final String testName = getTestName(false).toLowerCase();
configureByFile(testName + ".py");