Merge branch 'python-fixes'

This commit is contained in:
Andrey Vlasovskikh
2013-04-23 21:26:29 +04:00
7 changed files with 103 additions and 18 deletions
@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
import com.intellij.codeInsight.controlflow.ControlFlowBuilder;
import com.intellij.codeInsight.controlflow.Instruction;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import java.util.List;
@@ -18,7 +19,9 @@ public class InstructionBuilder {
List<Instruction> result = Lists.newArrayList();
for (PyTypeAssertionEvaluator.Assertion def: assertions) {
final PyReferenceExpression e = def.getElement();
result.add(ReadWriteInstruction.assertType(builder, e, e.getName(), def.getTypeEvalFunction()));
final PyQualifiedName qname = e.asQualifiedName();
final String name = qname != null ? qname.toString() : e.getName();
result.add(ReadWriteInstruction.assertType(builder, e, name, def.getTypeEvalFunction()));
}
return result;
}
@@ -16,6 +16,7 @@ import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.console.PydevConsoleRunner;
import com.jetbrains.python.console.completion.PydevConsoleReference;
import com.jetbrains.python.console.pydev.ConsoleCommunication;
@@ -204,7 +205,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
}
}
ResolveResult[] targets = getReference(PyResolveContext.noImplicits().withTypeEvalContext(context)).multiResolve(false);
if (targets.length == 0) return null;
if (targets.length == 0) {
return getQualifiedReferenceTypeByControlFlow(context);
}
for (ResolveResult resolveResult : targets) {
PsiElement target = resolveResult.getElement();
if (target == this || target == null) {
@@ -226,6 +229,24 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
}
}
@Nullable
public PyType getQualifiedReferenceTypeByControlFlow(@NotNull TypeEvalContext context) {
PyExpression qualifier = getQualifier();
if (context.allowDataFlow(this) && qualifier != null) {
PyExpression next = qualifier;
while (next != null) {
qualifier = next;
next = qualifier instanceof PyQualifiedExpression ? ((PyQualifiedExpression)qualifier).getQualifier() : null;
}
final ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(this);
final PyQualifiedName qname = asQualifiedName();
if (qname != null && scopeOwner != null) {
return getTypeByControlFlow(qname.toString(), context, qualifier, scopeOwner);
}
}
return null;
}
@Nullable
public Ref<PyType> getTypeOfProperty(@NotNull TypeEvalContext context) {
final PyExpression qualifier = getQualifier();
@@ -309,23 +330,13 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
if ((target instanceof PyTargetExpression || target instanceof PyNamedParameter) && anchor != null && context.allowDataFlow(anchor)) {
final ScopeOwner scopeOwner = PsiTreeUtil.getStubOrPsiParentOfType(anchor, ScopeOwner.class);
if (scopeOwner != null && scopeOwner == PsiTreeUtil.getStubOrPsiParentOfType(target, ScopeOwner.class)) {
PyAugAssignmentStatement augAssignment = PsiTreeUtil.getParentOfType(anchor, PyAugAssignmentStatement.class);
try {
final List<ReadWriteInstruction> defs = PyDefUseUtil.getLatestDefs(scopeOwner,
((PyElement)target).getName(),
augAssignment != null ? augAssignment : anchor,
true);
if (!defs.isEmpty()) {
PyType type = defs.get(0).getType(context, anchor);
for (int i = 1; i < defs.size(); i++) {
type = PyUnionType.union(type, defs.get(i).getType(context, anchor));
}
final String name = ((PyElement)target).getName();
if (name != null) {
final PyType type = getTypeByControlFlow(name, context, anchor, scopeOwner);
if (type != null) {
return type;
}
}
catch (PyDefUseUtil.InstructionNotFoundException e) {
// ignore
}
}
}
if (target instanceof PyFunction) {
@@ -356,6 +367,27 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
return null;
}
private static PyType getTypeByControlFlow(@NotNull String name,
@NotNull TypeEvalContext context,
@NotNull PyExpression anchor,
@NotNull ScopeOwner scopeOwner) {
PyAugAssignmentStatement augAssignment = PsiTreeUtil.getParentOfType(anchor, PyAugAssignmentStatement.class);
try {
final PyElement element = augAssignment != null ? augAssignment : anchor;
final List<ReadWriteInstruction> defs = PyDefUseUtil.getLatestDefs(scopeOwner, name, element, true);
if (!defs.isEmpty()) {
PyType type = defs.get(0).getType(context, anchor);
for (int i = 1; i < defs.size(); i++) {
type = PyUnionType.union(type, defs.get(i).getType(context, anchor));
}
return type;
}
}
catch (PyDefUseUtil.InstructionNotFoundException ignored) {
}
return null;
}
@Nullable
public static PyType getReferenceTypeFromProviders(@NotNull final PsiElement target,
TypeEvalContext context,
@@ -160,6 +160,10 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
if (typeOfProperty != null) {
return typeOfProperty.get();
}
final PyType cfgType = refex.getQualifiedReferenceTypeByControlFlow(context);
if (cfgType != null) {
return cfgType;
}
return typeFromTarget;
}
}
@@ -42,7 +42,7 @@ public class PyImportedModuleType implements PyType {
final PsiElement resolved = myImportedModule.resolve();
if (resolved instanceof PyFile) {
final PyFile file = (PyFile)resolved;
return new PyModuleType(file, myImportedModule).resolveMember(name, location, direction, resolveContext, true);
return new PyModuleType(file, myImportedModule).resolveMember(name, location, direction, resolveContext, inherited);
}
else if (resolved instanceof PsiDirectory) {
final List<PsiElement> elements = Collections.singletonList(ResolveImportUtil.resolveChild(resolved, name, null, true, true));
@@ -32,7 +32,7 @@ public class PyUnionType implements PyType {
boolean all_nulls = true;
for (PyType member : myMembers) {
if (member != null) {
List<? extends RatedResolveResult> result = member.resolveMember(name, null, direction, resolveContext, true);
List<? extends RatedResolveResult> result = member.resolveMember(name, null, direction, resolveContext, inherited);
if (result != null) {
all_nulls = false;
ret.addAll(result);
@@ -12,6 +12,7 @@ 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.jetbrains.python.psi.impl.PyQualifiedName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -75,6 +76,12 @@ public class PyDefUseUtil {
if (element instanceof PyImportElement) {
return ((PyImportElement) element).getVisibleName();
}
if (element instanceof PyReferenceExpression) {
final PyQualifiedName qname = ((PyReferenceExpression)element).asQualifiedName();
if (qname != null) {
return qname.toString();
}
}
return element instanceof PyElement ? ((PyElement)element).getName() : null;
}
@@ -497,6 +497,45 @@ public class PyTypeTest extends PyTestCase {
" expr = x\n");
}
// PY-5614
public void testUnknownReferenceTypeAttribute() {
doTest("str",
"def f(x):\n" +
" if isinstance(x.foo, str):\n" +
" expr = x.foo\n");
}
// PY-5614
public void testUnknownTypeAttribute() {
doTest("str",
"class C(object):\n" +
" def __init__(self, foo):\n" +
" self.foo = foo\n" +
" def f(self):\n" +
" if isinstance(self.foo, str):\n" +
" expr = self.foo\n");
}
// PY-5614
public void testKnownTypeAttribute() {
doTest("str",
"class C(object):\n" +
" def __init__(self):\n" +
" self.foo = 42\n" +
" def f(self):\n" +
" if isinstance(self.foo, str):\n" +
" expr = self.foo\n");
}
// PY-5614
public void testNestedUnknownReferenceTypeAttribute() {
doTest("str",
"def f(x):\n" +
" if isinstance(x.foo.bar, str):\n" +
" expr = x.foo.bar\n");
}
private PyExpression parseExpr(String text) {
myFixture.configureByText(PythonFileType.INSTANCE, text);
return myFixture.findElementByText("expr", PyExpression.class);